/* * 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 #include #include #include #include #include #include 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));