summaryrefslogtreecommitdiff
path: root/common/keyboard_test.c
blob: eb6fc11bddcf31324ef4e84ca22986cca29d492b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
/*
 * Copyright 2013 The ChromiumOS Authors
 * 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++;
	}

	ccprints("keyscan_seq done, upto=%d", 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 enum ec_status 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));