source: mainline/uspace/srv/hw/char/i8042/i8042.c@ 36e9cd1

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 36e9cd1 was 385a3d6, checked in by Jiri Svoboda <jiri@…>, 16 years ago

Fix mouse IRQ bug.

  • Property mode set to 100644
File size: 7.3 KB
Line 
1/*
2 * Copyright (c) 2001-2004 Jakub Jermar
3 * Copyright (c) 2006 Josef Cejka
4 * Copyright (c) 2009 Jiri Svoboda
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * - The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/** @addtogroup kbd_port
32 * @ingroup kbd
33 * @{
34 */
35/** @file
36 * @brief i8042 PS/2 port driver.
37 */
38
39#include <ddi.h>
40#include <libarch/ddi.h>
41#include <devmap.h>
42#include <ipc/ipc.h>
43#include <async.h>
44#include <unistd.h>
45#include <sysinfo.h>
46#include <stdio.h>
47#include <errno.h>
48
49#include "i8042.h"
50
51#define NAME "i8042"
52#define NAMESPACE "char"
53
54/* Interesting bits for status register */
55#define i8042_OUTPUT_FULL 0x01
56#define i8042_INPUT_FULL 0x02
57#define i8042_AUX_DATA 0x20
58
59/* Command constants */
60#define i8042_CMD_WRITE_CMDB 0x60 /**< write command byte */
61#define i8042_CMD_WRITE_AUX 0xd4 /**< write aux device */
62
63/* Command byte fields */
64#define i8042_KBD_IE 0x01
65#define i8042_AUX_IE 0x02
66#define i8042_KBD_DISABLE 0x10
67#define i8042_AUX_DISABLE 0x20
68#define i8042_KBD_TRANSLATE 0x40
69
70/* Mouse constants */
71#define MOUSE_OUT_INIT 0xf4
72#define MOUSE_ACK 0xfa
73
74enum {
75 DEVID_PRI = 0, /**< primary device */
76 DEVID_AUX = 1, /**< AUX device */
77 MAX_DEVS = 2
78};
79
80static irq_cmd_t i8042_cmds[] = {
81 {
82 .cmd = CMD_PIO_READ_8,
83 .addr = NULL, /* will be patched in run-time */
84 .dstarg = 1
85 },
86 {
87 .cmd = CMD_BTEST,
88 .value = i8042_OUTPUT_FULL,
89 .srcarg = 1,
90 .dstarg = 3
91 },
92 {
93 .cmd = CMD_PREDICATE,
94 .value = 2,
95 .srcarg = 3
96 },
97 {
98 .cmd = CMD_PIO_READ_8,
99 .addr = NULL, /* will be patched in run-time */
100 .dstarg = 2
101 },
102 {
103 .cmd = CMD_ACCEPT
104 }
105};
106
107static irq_code_t i8042_kbd = {
108 sizeof(i8042_cmds) / sizeof(irq_cmd_t),
109 i8042_cmds
110};
111
112static uintptr_t i8042_physical;
113static uintptr_t i8042_kernel;
114static i8042_t * i8042;
115
116static i8042_port_t i8042_port[MAX_DEVS];
117
118static void wait_ready(void)
119{
120 while (pio_read_8(&i8042->status) & i8042_INPUT_FULL);
121}
122
123static void i8042_irq_handler(ipc_callid_t iid, ipc_call_t *call);
124static void i8042_connection(ipc_callid_t iid, ipc_call_t *icall);
125static int i8042_init(void);
126static void i8042_port_write(int devid, uint8_t data);
127
128
129int main(int argc, char *argv[])
130{
131 char name[16];
132 int i, rc;
133 char dchar[MAX_DEVS] = { 'a', 'b' };
134
135 printf(NAME ": i8042 PS/2 port driver\n");
136
137 rc = devmap_driver_register(NAME, i8042_connection);
138 if (rc < 0) {
139 printf(NAME ": Unable to register driver.\n");
140 return rc;
141 }
142
143 if (i8042_init() != EOK)
144 return -1;
145
146 for (i = 0; i < MAX_DEVS; i++) {
147 i8042_port[i].client_phone = -1;
148
149 snprintf(name, 16, "%s/ps2%c", NAMESPACE, dchar[i]);
150 rc = devmap_device_register(name, &i8042_port[i].dev_handle);
151 if (rc != EOK) {
152 devmap_hangup_phone(DEVMAP_DRIVER);
153 printf(NAME ": Unable to register device %s.\n", name);
154 return rc;
155 }
156 printf(NAME ": Registered device %s\n", name);
157 }
158
159 printf(NAME ": Accepting connections\n");
160 task_retval(0);
161 async_manager();
162
163 /* Not reached */
164 return 0;
165}
166
167static int i8042_init(void)
168{
169 void *vaddr;
170
171 i8042_physical = sysinfo_value("i8042.address.physical");
172 i8042_kernel = sysinfo_value("i8042.address.kernel");
173 if (pio_enable((void *) i8042_physical, sizeof(i8042_t), &vaddr) != 0)
174 return -1;
175 i8042 = vaddr;
176
177 async_set_interrupt_received(i8042_irq_handler);
178
179 /* Disable kbd, enable mouse */
180 pio_write_8(&i8042->status, i8042_CMD_WRITE_CMDB);
181 wait_ready();
182 pio_write_8(&i8042->status, i8042_CMD_WRITE_CMDB);
183 wait_ready();
184 pio_write_8(&i8042->data, i8042_KBD_DISABLE);
185 wait_ready();
186
187 /* Flush all current IO */
188 while (pio_read_8(&i8042->status) & i8042_OUTPUT_FULL)
189 (void) pio_read_8(&i8042->data);
190
191 i8042_port_write(DEVID_AUX, MOUSE_OUT_INIT);
192
193 i8042_kbd.cmds[0].addr = (void *) &((i8042_t *) i8042_kernel)->status;
194 i8042_kbd.cmds[3].addr = (void *) &((i8042_t *) i8042_kernel)->data;
195 ipc_register_irq(sysinfo_value("i8042.inr_a"), device_assign_devno(), 0, &i8042_kbd);
196 ipc_register_irq(sysinfo_value("i8042.inr_b"), device_assign_devno(), 0, &i8042_kbd);
197
198 pio_write_8(&i8042->status, i8042_CMD_WRITE_CMDB);
199 wait_ready();
200 pio_write_8(&i8042->data, i8042_KBD_IE | i8042_KBD_TRANSLATE |
201 i8042_AUX_IE);
202 wait_ready();
203
204 return 0;
205}
206
207/** Character device connection handler */
208static void i8042_connection(ipc_callid_t iid, ipc_call_t *icall)
209{
210 ipc_callid_t callid;
211 ipc_call_t call;
212 ipcarg_t method;
213 dev_handle_t dh;
214 int retval;
215 int dev_id, i;
216
217 printf(NAME ": connection handler\n");
218
219 /* Get the device handle. */
220 dh = IPC_GET_ARG1(*icall);
221
222 /* Determine which disk device is the client connecting to. */
223 dev_id = -1;
224 for (i = 0; i < MAX_DEVS; i++) {
225 if (i8042_port[i].dev_handle == dh)
226 dev_id = i;
227 }
228
229 if (dev_id < 0) {
230 ipc_answer_0(iid, EINVAL);
231 return;
232 }
233
234 /* Answer the IPC_M_CONNECT_ME_TO call. */
235 ipc_answer_0(iid, EOK);
236
237 printf(NAME ": accepted connection\n");
238
239 while (1) {
240 callid = async_get_call(&call);
241 method = IPC_GET_METHOD(call);
242 switch (method) {
243 case IPC_M_PHONE_HUNGUP:
244 /* The other side has hung up. */
245 ipc_answer_0(callid, EOK);
246 return;
247 case IPC_M_CONNECT_TO_ME:
248 printf(NAME ": creating callback connection\n");
249 if (i8042_port[dev_id].client_phone != -1) {
250 retval = ELIMIT;
251 break;
252 }
253 i8042_port[dev_id].client_phone = IPC_GET_ARG5(call);
254 retval = 0;
255 break;
256 case IPC_FIRST_USER_METHOD:
257 printf(NAME ": write %d to devid %d\n",
258 IPC_GET_ARG1(call), dev_id);
259 i8042_port_write(dev_id, IPC_GET_ARG1(call));
260 retval = 0;
261 break;
262 default:
263 retval = EINVAL;
264 break;
265 }
266 ipc_answer_0(callid, retval);
267 }
268}
269
270void i8042_port_write(int devid, uint8_t data)
271{
272 if (devid == DEVID_AUX) {
273 pio_write_8(&i8042->status, i8042_CMD_WRITE_AUX);
274 wait_ready();
275 }
276 pio_write_8(&i8042->data, data);
277 wait_ready();
278}
279
280static void i8042_irq_handler(ipc_callid_t iid, ipc_call_t *call)
281{
282 int status, data;
283 int devid;
284
285 status = IPC_GET_ARG1(*call);
286 data = IPC_GET_ARG2(*call);
287
288 if ((status & i8042_AUX_DATA)) {
289 devid = DEVID_AUX;
290 } else {
291 devid = DEVID_PRI;
292 }
293
294 if (i8042_port[devid].client_phone != -1) {
295 async_msg_1(i8042_port[devid].client_phone,
296 IPC_FIRST_USER_METHOD, data);
297 }
298}
299
300/**
301 * @}
302 */
Note: See TracBrowser for help on using the repository browser.