source: mainline/uspace/drv/char/i8042/i8042.c@ 32ba5a6

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 32ba5a6 was 9b56a8dd, checked in by Jan Vesely <jano.vesely@…>, 14 years ago

i8042, ps2mouse, xtkbd:Drop newlines at the end of ddf_msg messages.

  • Property mode set to 100644
File size: 9.7 KB
Line 
1/*
2 * Copyright (c) 2001-2004 Jakub Jermar
3 * Copyright (c) 2006 Josef Cejka
4 * Copyright (c) 2009 Jiri Svoboda
5 * Copyright (c) 2011 Jan Vesely
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * - Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * - The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/** @addtogroup kbd_port
33 * @ingroup kbd
34 * @{
35 */
36/** @file
37 * @brief i8042 PS/2 port driver.
38 */
39
40#include <ddi.h>
41#include <devman.h>
42#include <device/hw_res.h>
43#include <libarch/ddi.h>
44#include <loc.h>
45#include <async.h>
46#include <unistd.h>
47#include <stdio.h>
48#include <errno.h>
49#include <str_error.h>
50#include <inttypes.h>
51#include <ddf/log.h>
52#include <ddf/interrupt.h>
53
54#include <ops/char_dev.h>
55
56#include "i8042.h"
57
58#define NAME "i8042"
59
60static int i8042_write_kbd(ddf_fun_t *, char *, size_t);
61static int i8042_read_kbd(ddf_fun_t *, char *, size_t);
62static int i8042_write_aux(ddf_fun_t *, char *, size_t);
63static int i8042_read_aux(ddf_fun_t *, char *, size_t);
64
65static char_dev_ops_t kbd_iface = {
66 .read = i8042_read_kbd,
67 .write = i8042_write_kbd,
68};
69
70static char_dev_ops_t aux_iface = {
71 .read = i8042_read_aux,
72 .write = i8042_write_aux,
73};
74
75static ddf_dev_ops_t kbd_ops = {
76 .interfaces[CHAR_DEV_IFACE] = &kbd_iface
77};
78
79static ddf_dev_ops_t aux_ops = {
80 .interfaces[CHAR_DEV_IFACE] = &aux_iface
81};
82
83/* Interesting bits for status register */
84#define i8042_OUTPUT_FULL 0x01
85#define i8042_INPUT_FULL 0x02
86#define i8042_AUX_DATA 0x20
87
88/* Command constants */
89#define i8042_CMD_WRITE_CMDB 0x60 /**< write command byte */
90#define i8042_CMD_WRITE_AUX 0xd4 /**< write aux device */
91
92/* Command byte fields */
93#define i8042_KBD_IE 0x01
94#define i8042_AUX_IE 0x02
95#define i8042_KBD_DISABLE 0x10
96#define i8042_AUX_DISABLE 0x20
97#define i8042_KBD_TRANSLATE 0x40 /* Use this to switch to XT scancodes */
98
99static const irq_cmd_t i8042_cmds[] = {
100 {
101 .cmd = CMD_PIO_READ_8,
102 .addr = NULL, /* will be patched in run-time */
103 .dstarg = 1
104 },
105 {
106 .cmd = CMD_BTEST,
107 .value = i8042_OUTPUT_FULL,
108 .srcarg = 1,
109 .dstarg = 3
110 },
111 {
112 .cmd = CMD_PREDICATE,
113 .value = 2,
114 .srcarg = 3
115 },
116 {
117 .cmd = CMD_PIO_READ_8,
118 .addr = NULL, /* will be patched in run-time */
119 .dstarg = 2
120 },
121 {
122 .cmd = CMD_ACCEPT
123 }
124};
125/*----------------------------------------------------------------------------*/
126static void wait_ready(i8042_t *dev)
127{
128 assert(dev);
129 while (pio_read_8(&dev->regs->status) & i8042_INPUT_FULL);
130}
131/*----------------------------------------------------------------------------*/
132static void wait_ready_write(i8042_t *dev)
133{
134 assert(dev);
135 while (pio_read_8(&dev->regs->status) & i8042_OUTPUT_FULL);
136}
137/*----------------------------------------------------------------------------*/
138static void i8042_irq_handler(
139 ddf_dev_t *dev, ipc_callid_t iid, ipc_call_t *call)
140{
141 if (!dev || !dev->driver_data)
142 return;
143 i8042_t *controller = dev->driver_data;
144
145 const uint8_t status = IPC_GET_ARG1(*call);
146 const uint8_t data = IPC_GET_ARG2(*call);
147 buffer_t *buffer = (status & i8042_AUX_DATA) ?
148 &controller->aux_buffer : &controller->kbd_buffer;
149 buffer_write(buffer, data);
150}
151/*----------------------------------------------------------------------------*/
152int i8042_init(i8042_t *dev, void *regs, size_t reg_size, int irq_kbd,
153 int irq_mouse, ddf_dev_t *ddf_dev)
154{
155 assert(ddf_dev);
156 assert(dev);
157
158 if (reg_size < sizeof(i8042_regs_t))
159 return EINVAL;
160
161 if (pio_enable(regs, sizeof(i8042_regs_t), (void**)&dev->regs) != 0)
162 return -1;
163
164 dev->kbd_fun = ddf_fun_create(ddf_dev, fun_inner, "ps2a");
165 if (!dev->kbd_fun)
166 return ENOMEM;
167 int ret = ddf_fun_add_match_id(dev->kbd_fun, "char/xtkbd", 90);
168 if (ret != EOK) {
169 ddf_fun_destroy(dev->kbd_fun);
170 return ret;
171 }
172
173 dev->mouse_fun = ddf_fun_create(ddf_dev, fun_inner, "ps2b");
174 if (!dev->mouse_fun) {
175 ddf_fun_destroy(dev->kbd_fun);
176 return ENOMEM;
177 }
178
179 ret = ddf_fun_add_match_id(dev->mouse_fun, "char/ps2mouse", 90);
180 if (ret != EOK) {
181 ddf_fun_destroy(dev->kbd_fun);
182 ddf_fun_destroy(dev->mouse_fun);
183 return ret;
184 }
185
186 dev->kbd_fun->ops = &kbd_ops;
187 dev->mouse_fun->ops = &aux_ops;
188 dev->kbd_fun->driver_data = dev;
189 dev->mouse_fun->driver_data = dev;
190
191 buffer_init(&dev->kbd_buffer, dev->kbd_data, BUFFER_SIZE);
192 buffer_init(&dev->aux_buffer, dev->aux_data, BUFFER_SIZE);
193 fibril_mutex_initialize(&dev->write_guard);
194
195#define CHECK_RET_DESTROY(ret, msg...) \
196if (ret != EOK) { \
197 ddf_msg(LVL_ERROR, msg); \
198 if (dev->kbd_fun) { \
199 dev->kbd_fun->driver_data = NULL; \
200 ddf_fun_destroy(dev->kbd_fun); \
201 } \
202 if (dev->mouse_fun) { \
203 dev->mouse_fun->driver_data = NULL; \
204 ddf_fun_destroy(dev->mouse_fun); \
205 } \
206} else (void)0
207
208 ret = ddf_fun_bind(dev->kbd_fun);
209 CHECK_RET_DESTROY(ret,
210 "Failed to bind keyboard function: %s.", str_error(ret));
211
212 ret = ddf_fun_bind(dev->mouse_fun);
213 CHECK_RET_DESTROY(ret,
214 "Failed to bind mouse function: %s.", str_error(ret));
215
216 /* Disable kbd and aux */
217 wait_ready(dev);
218 pio_write_8(&dev->regs->status, i8042_CMD_WRITE_CMDB);
219 wait_ready(dev);
220 pio_write_8(&dev->regs->data, i8042_KBD_DISABLE | i8042_AUX_DISABLE);
221
222 /* Flush all current IO */
223 while (pio_read_8(&dev->regs->status) & i8042_OUTPUT_FULL)
224 (void) pio_read_8(&dev->regs->data);
225
226#define CHECK_RET_UNBIND_DESTROY(ret, msg...) \
227if (ret != EOK) { \
228 ddf_msg(LVL_ERROR, msg); \
229 if (dev->kbd_fun) { \
230 ddf_fun_unbind(dev->kbd_fun); \
231 dev->kbd_fun->driver_data = NULL; \
232 ddf_fun_destroy(dev->kbd_fun); \
233 } \
234 if (dev->mouse_fun) { \
235 ddf_fun_unbind(dev->mouse_fun); \
236 dev->mouse_fun->driver_data = NULL; \
237 ddf_fun_destroy(dev->mouse_fun); \
238 } \
239} else (void)0
240
241 const size_t cmd_count = sizeof(i8042_cmds) / sizeof(irq_cmd_t);
242 irq_cmd_t cmds[cmd_count];
243 memcpy(cmds, i8042_cmds, sizeof(i8042_cmds));
244 cmds[0].addr = (void *) &dev->regs->status;
245 cmds[3].addr = (void *) &dev->regs->data;
246
247 irq_code_t irq_code = { .cmdcount = cmd_count, .cmds = cmds };
248 ret = register_interrupt_handler(ddf_dev, irq_kbd, i8042_irq_handler,
249 &irq_code);
250 CHECK_RET_UNBIND_DESTROY(ret,
251 "Failed set handler for kbd: %s.", str_error(ret));
252
253 ret = register_interrupt_handler(ddf_dev, irq_mouse, i8042_irq_handler,
254 &irq_code);
255 CHECK_RET_UNBIND_DESTROY(ret,
256 "Failed set handler for mouse: %s.", str_error(ret));
257
258 /* Enable interrupts */
259 async_sess_t *parent_sess =
260 devman_parent_device_connect(EXCHANGE_SERIALIZE, ddf_dev->handle,
261 IPC_FLAG_BLOCKING);
262 ret = parent_sess ? EOK : ENOMEM;
263 CHECK_RET_UNBIND_DESTROY(ret, "Failed to create parent connection.");
264
265 const bool enabled = hw_res_enable_interrupt(parent_sess);
266 async_hangup(parent_sess);
267 ret = enabled ? EOK : EIO;
268 CHECK_RET_UNBIND_DESTROY(ret, "Failed to enable interrupts: %s.");
269
270 /* Enable port interrupts. */
271 wait_ready(dev);
272 pio_write_8(&dev->regs->status, i8042_CMD_WRITE_CMDB);
273 wait_ready(dev);
274 pio_write_8(&dev->regs->data, i8042_KBD_IE | i8042_KBD_TRANSLATE |
275 i8042_AUX_IE);
276
277 return EOK;
278}
279/*----------------------------------------------------------------------------*/
280static int i8042_write_kbd(ddf_fun_t *fun, char *buffer, size_t size)
281{
282 assert(fun);
283 assert(fun->driver_data);
284 i8042_t *controller = fun->driver_data;
285 fibril_mutex_lock(&controller->write_guard);
286 for (size_t i = 0; i < size; ++i) {
287 wait_ready_write(controller);
288 pio_write_8(&controller->regs->data, buffer[i]);
289 }
290 fibril_mutex_unlock(&controller->write_guard);
291 return size;
292}
293/*----------------------------------------------------------------------------*/
294static int i8042_read_kbd(ddf_fun_t *fun, char *buffer, size_t size)
295{
296 assert(fun);
297 assert(fun->driver_data);
298 bzero(buffer, size);
299
300 i8042_t *controller = fun->driver_data;
301
302 for (size_t i = 0; i < size; ++i) {
303 *buffer++ = buffer_read(&controller->kbd_buffer);
304 }
305 return size;
306}
307/*----------------------------------------------------------------------------*/
308static int i8042_write_aux(ddf_fun_t *fun, char *buffer, size_t size)
309{
310 assert(fun);
311 assert(fun->driver_data);
312 i8042_t *controller = fun->driver_data;
313 fibril_mutex_lock(&controller->write_guard);
314 for (size_t i = 0; i < size; ++i) {
315 wait_ready_write(controller);
316 pio_write_8(&controller->regs->status, i8042_CMD_WRITE_AUX);
317 pio_write_8(&controller->regs->data, buffer[i]);
318 }
319 fibril_mutex_unlock(&controller->write_guard);
320 return size;
321}
322/*----------------------------------------------------------------------------*/
323static int i8042_read_aux(ddf_fun_t *fun, char *buffer, size_t size)
324{
325 assert(fun);
326 assert(fun->driver_data);
327 bzero(buffer, size);
328
329 i8042_t *controller = fun->driver_data;
330 for (size_t i = 0; i < size; ++i) {
331 *buffer++ = buffer_read(&controller->aux_buffer);
332 }
333 return size;
334}
335/**
336 * @}
337 */
Note: See TracBrowser for help on using the repository browser.