summaryrefslogtreecommitdiff
path: root/common/i8042.c
blob: 5db0c98388dd729611331054eaa9f162ce2a755c (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
/* Copyright (c) 2011 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.
 *
 * Chrome OS EC i8042 interface code.
 */

#include "board.h"
#include "common.h"
#include "i8042.h"
#include "keyboard.h"
#include "task.h"
#include "timer.h"
#include "uart.h"
#include "util.h"


#define I8042_DEBUG 1

#define MAX_QUEUED_KEY_PRESS 16

/* Circular buffer to host.
 * head: next to dequeqe
 * tail: next to enqueue
 * head == tail: empty.
 * tail + 1 == head: full
 */
static int head_to_buffer = 0;
static int tail_to_buffer = 0;
#define HOST_BUFFER_SIZE (16)
static uint8_t to_host_buffer[HOST_BUFFER_SIZE];

static int i8042_irq_enabled = 0;


/* Reset all i8042 buffer */
void i8042_init()
{
	head_to_buffer = tail_to_buffer = 0;
}


/* Called by the chip-specific code when host sedns a byte to port 0x60. */
void i8042_receives_data(int data)
{
	int ret_len;
	uint8_t output[MAX_SCAN_CODE_LEN];
	enum ec_error_list ret;

	ret_len = handle_keyboard_data(data, output);
	ret = i8042_send_to_host(ret_len, output);
	ASSERT(ret == EC_SUCCESS);
}


/* Called by the chip-specific code when host sedns a byte to port 0x64. */
void i8042_receives_command(int cmd)
{
	int ret_len;
	uint8_t output[MAX_SCAN_CODE_LEN];
	enum ec_error_list ret;

	ret_len = handle_keyboard_command(cmd, output);
	ret = i8042_send_to_host(ret_len, output);
	ASSERT(ret == EC_SUCCESS);
}


/* Called by EC common code to send bytes to host via port 0x60. */
static void enq_to_host(int len, uint8_t *to_host)
{
	int from, to;

	/* Check if the buffer has enough space, then copy them to buffer. */
	if ((tail_to_buffer + len) <= (head_to_buffer + HOST_BUFFER_SIZE - 1)) {
		for (from = 0, to = tail_to_buffer; from < len;) {
			to_host_buffer[to++] = to_host[from++];
			to %= HOST_BUFFER_SIZE;
		}
		tail_to_buffer = (tail_to_buffer + len) % HOST_BUFFER_SIZE;
	}
}


/* Called by common/keyboard.c when the host wants to receive keyboard IRQ
 * (or not).
 */
void i8042_enable_keyboard_irq(void) {
	i8042_irq_enabled = 1;
}

void i8042_disable_keyboard_irq(void) {
	i8042_irq_enabled = 0;
}


void i8042_command_task(void)
{
	while (1) {
		/* Either a new byte to host or host picking up can un-block. */
		task_wait_event(-1);

		while (1) {
			uint8_t chr;
			int empty = 0;

			/* Check if we have data in buffer to host. */
			if (head_to_buffer == tail_to_buffer) {
				empty = 1;  /* nothing to host */
			}
			if (empty) break;

			/* if the host still didn't read that away,
			   try next time. */
			if (keyboard_has_char()) {
#if I8042_DEBUG >= 5
				uart_printf("[%d] i8042_command_task() "
					    "cannot send to host due to host "
					    "havn't taken away.\n",
					    get_time().le.lo);
#endif
				break;
			}

			/* Get a char from buffer. */
			chr = to_host_buffer[head_to_buffer];
			head_to_buffer =
				(head_to_buffer + 1) % HOST_BUFFER_SIZE;

			/* Write to host. */
			keyboard_put_char(chr, i8042_irq_enabled);
#if I8042_DEBUG >= 4
			uart_printf("[%d] i8042_command_task() "
				    "sends to host: 0x%02x\n",
				    get_time().le.lo, chr);
#endif
		}
	}
}


enum ec_error_list i8042_send_to_host(int len, uint8_t *to_host)
{
	enq_to_host(len, to_host);

	/* Wake up the task to handle the command */
	task_wake(TASK_ID_I8042CMD);

	return EC_SUCCESS;
}