blob: 580d4745cf4c3e89674c5c2cf069b4d6b64e2dd8 [file] [log] [blame]
/*
* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <common.h>
#include <console.h>
#include <ec_commands.h>
#include <host_command.h>
#include <keyboard_test.h>
#include <task.h>
#include <util.h>
enum {
KEYSCAN_MAX_LENGTH = 20,
KEYSCAN_SEQ_START_DELAY_US = 10000,
};
static uint8_t keyscan_seq_count;
static int8_t keyscan_seq_upto = -1;
static struct keyscan_item keyscan_items[KEYSCAN_MAX_LENGTH];
struct keyscan_item *keyscan_seq_cur;
static int keyscan_seq_is_active(void)
{
return keyscan_seq_upto != -1;
}
/**
* Get the current item in the keyscan sequence
*
* This looks at the current time, and returns the correct key scan for that
* time.
*
* @return pointer to keyscan item, or NULL if none
*/
static const struct keyscan_item *keyscan_seq_get(void)
{
struct keyscan_item *ksi;
if (!keyscan_seq_is_active())
return NULL;
ksi = &keyscan_items[keyscan_seq_upto];
while (keyscan_seq_upto < keyscan_seq_count) {
/*
* If we haven't reached the time for the next one, return
* this one.
*/
if (!timestamp_expired(ksi->abs_time, NULL)) {
/* Yippee, we get to present this one! */
if (keyscan_seq_cur)
keyscan_seq_cur->done = 1;
return keyscan_seq_cur;
}
keyscan_seq_cur = ksi;
keyscan_seq_upto++;
ksi++;
}
ccprintf("%T: keyscan_seq done, upto=%d\n", keyscan_seq_upto);
keyscan_seq_upto = -1;
keyscan_seq_cur = NULL;
return NULL;
}
uint8_t keyscan_seq_get_scan(int column, uint8_t scan)
{
const struct keyscan_item *item;
/* Use simulated keyscan sequence instead if active */
item = keyscan_seq_get();
if (item) {
/* OR all columns together */
if (column == -1) {
int c;
scan = 0;
for (c = 0; c < KEYBOARD_COLS; c++)
scan |= item->scan[c];
} else {
scan = item->scan[column];
}
}
return scan;
}
int keyscan_seq_next_event_delay(void)
{
const struct keyscan_item *ksi;
int delay;
/*
* Make sure we are pointing to the right event. This function will
* return the event that should currently be presented. In fact we
* want to look at the next event to be presented, so we manually
* look that up after calling this function.
*/
ksi = keyscan_seq_get();
if (!keyscan_seq_is_active())
return -1;
/* Calculate the delay until the event */
ksi = &keyscan_items[keyscan_seq_upto];
delay = MAX(ksi->abs_time.val - get_time().val, 0);
return delay;
}
static void keyscan_seq_start(void)
{
timestamp_t start;
int i;
start = get_time();
start.val += KEYSCAN_SEQ_START_DELAY_US;
for (i = 0; i < keyscan_seq_count; i++) {
struct keyscan_item *ksi = &keyscan_items[i];
ksi->abs_time = start;
ksi->abs_time.val += ksi->time_us;
}
keyscan_seq_upto = 0;
keyscan_seq_cur = NULL;
task_wake(TASK_ID_KEYSCAN);
}
static int keyscan_seq_collect(struct ec_params_keyscan_seq_ctrl *req,
struct ec_result_keyscan_seq_ctrl *resp)
{
struct keyscan_item *ksi;
int start, end;
int i;
/* Range check the input values */
start = req->collect.start_item;
end = start + req->collect.num_items;
if (start >= keyscan_seq_count)
end = start;
else
end = MIN(end, keyscan_seq_count);
start = MIN(start, end);
/* Response plus one byte per item */
end = MIN(end - start, EC_HOST_PARAM_SIZE - sizeof(*resp));
resp->collect.num_items = end - start;
for (i = start, ksi = keyscan_items; i < end; i++, ksi++)
resp->collect.item[i].flags = ksi->done ?
EC_KEYSCAN_SEQ_FLAG_DONE : 0;
return sizeof(*resp) + resp->collect.num_items;
}
static int keyscan_seq_ctrl(struct host_cmd_handler_args *args)
{
struct ec_params_keyscan_seq_ctrl req, *msg;
struct keyscan_item *ksi;
/* For now we must do our own alignment */
memcpy(&req, args->params, sizeof(req));
ccprintf("keyscan %d\n", req.cmd);
switch (req.cmd) {
case EC_KEYSCAN_SEQ_CLEAR:
keyscan_seq_count = 0;
break;
case EC_KEYSCAN_SEQ_ADD:
if (keyscan_seq_count == KEYSCAN_MAX_LENGTH)
return EC_RES_OVERFLOW;
ksi = &keyscan_items[keyscan_seq_count];
ksi->time_us = req.add.time_us;
ksi->done = 0;
ksi->abs_time.val = 0;
msg = (struct ec_params_keyscan_seq_ctrl *)args->params;
memcpy(ksi->scan, msg->add.scan, sizeof(ksi->scan));
keyscan_seq_count++;
break;
case EC_KEYSCAN_SEQ_START:
keyscan_seq_start();
break;
case EC_KEYSCAN_SEQ_COLLECT:
args->response_size = keyscan_seq_collect(&req,
(struct ec_result_keyscan_seq_ctrl *)args->response);
break;
default:
return EC_RES_INVALID_COMMAND;
}
return EC_RES_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_CMD_KEYSCAN_SEQ_CTRL,
keyscan_seq_ctrl,
EC_VER_MASK(0));