source: mainline/uspace/srv/hw/char/i8042/i8042.c@ 7ea7db31

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 7ea7db31 was 7ea7db31, checked in by Jakub Jermar <jakub@…>, 15 years ago

Cease using devmap_get_phone() and devmap_hangup_phone() in drivers.

  • Property mode set to 100644
File size: 7.4 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#include <inttypes.h>
49
50#include "i8042.h"
51
52#define NAME "i8042"
53#define NAMESPACE "char"
54
55/* Interesting bits for status register */
56#define i8042_OUTPUT_FULL 0x01
57#define i8042_INPUT_FULL 0x02
58#define i8042_AUX_DATA 0x20
59
60/* Command constants */
61#define i8042_CMD_WRITE_CMDB 0x60 /**< write command byte */
62#define i8042_CMD_WRITE_AUX 0xd4 /**< write aux device */
63
64/* Command byte fields */
65#define i8042_KBD_IE 0x01
66#define i8042_AUX_IE 0x02
67#define i8042_KBD_DISABLE 0x10
68#define i8042_AUX_DISABLE 0x20
69#define i8042_KBD_TRANSLATE 0x40
70
71
72enum {
73 DEVID_PRI = 0, /**< primary device */
74 DEVID_AUX = 1, /**< AUX device */
75 MAX_DEVS = 2
76};
77
78static irq_cmd_t i8042_cmds[] = {
79 {
80 .cmd = CMD_PIO_READ_8,
81 .addr = NULL, /* will be patched in run-time */
82 .dstarg = 1
83 },
84 {
85 .cmd = CMD_BTEST,
86 .value = i8042_OUTPUT_FULL,
87 .srcarg = 1,
88 .dstarg = 3
89 },
90 {
91 .cmd = CMD_PREDICATE,
92 .value = 2,
93 .srcarg = 3
94 },
95 {
96 .cmd = CMD_PIO_READ_8,
97 .addr = NULL, /* will be patched in run-time */
98 .dstarg = 2
99 },
100 {
101 .cmd = CMD_ACCEPT
102 }
103};
104
105static irq_code_t i8042_kbd = {
106 sizeof(i8042_cmds) / sizeof(irq_cmd_t),
107 i8042_cmds
108};
109
110static uintptr_t i8042_physical;
111static uintptr_t i8042_kernel;
112static i8042_t * i8042;
113
114static i8042_port_t i8042_port[MAX_DEVS];
115
116static void wait_ready(void)
117{
118 while (pio_read_8(&i8042->status) & i8042_INPUT_FULL);
119}
120
121static void i8042_irq_handler(ipc_callid_t iid, ipc_call_t *call);
122static void i8042_connection(ipc_callid_t iid, ipc_call_t *icall);
123static int i8042_init(void);
124static void i8042_port_write(int devid, uint8_t data);
125
126
127int main(int argc, char *argv[])
128{
129 char name[16];
130 int i, rc;
131 char dchar[MAX_DEVS] = { 'a', 'b' };
132
133 printf(NAME ": i8042 PS/2 port driver\n");
134
135 rc = devmap_driver_register(NAME, i8042_connection);
136 if (rc < 0) {
137 printf(NAME ": Unable to register driver.\n");
138 return rc;
139 }
140
141 if (i8042_init() != EOK)
142 return -1;
143
144 for (i = 0; i < MAX_DEVS; i++) {
145 i8042_port[i].client_phone = -1;
146
147 snprintf(name, 16, "%s/ps2%c", NAMESPACE, dchar[i]);
148 rc = devmap_device_register(name, &i8042_port[i].devmap_handle);
149 if (rc != EOK) {
150 printf(NAME ": Unable to register device %s.\n", name);
151 return rc;
152 }
153 printf(NAME ": Registered device %s\n", name);
154 }
155
156 printf(NAME ": Accepting connections\n");
157 task_retval(0);
158 async_manager();
159
160 /* Not reached */
161 return 0;
162}
163
164static int i8042_init(void)
165{
166 if (sysinfo_get_value("i8042.address.physical", &i8042_physical) != EOK)
167 return -1;
168
169 if (sysinfo_get_value("i8042.address.kernel", &i8042_kernel) != EOK)
170 return -1;
171
172 void *vaddr;
173 if (pio_enable((void *) i8042_physical, sizeof(i8042_t), &vaddr) != 0)
174 return -1;
175
176 i8042 = vaddr;
177
178 sysarg_t inr_a;
179 sysarg_t inr_b;
180
181 if (sysinfo_get_value("i8042.inr_a", &inr_a) != EOK)
182 return -1;
183
184 if (sysinfo_get_value("i8042.inr_b", &inr_b) != EOK)
185 return -1;
186
187 async_set_interrupt_received(i8042_irq_handler);
188
189 /* Disable kbd and aux */
190 wait_ready();
191 pio_write_8(&i8042->status, i8042_CMD_WRITE_CMDB);
192 wait_ready();
193 pio_write_8(&i8042->data, i8042_KBD_DISABLE | i8042_AUX_DISABLE);
194
195 /* Flush all current IO */
196 while (pio_read_8(&i8042->status) & i8042_OUTPUT_FULL)
197 (void) pio_read_8(&i8042->data);
198
199 i8042_kbd.cmds[0].addr = (void *) &((i8042_t *) i8042_kernel)->status;
200 i8042_kbd.cmds[3].addr = (void *) &((i8042_t *) i8042_kernel)->data;
201 ipc_register_irq(inr_a, device_assign_devno(), 0, &i8042_kbd);
202 ipc_register_irq(inr_b, device_assign_devno(), 0, &i8042_kbd);
203 printf("%s: registered for interrupts %" PRIun " and %" PRIun "\n",
204 NAME, inr_a, inr_b);
205
206 wait_ready();
207 pio_write_8(&i8042->status, i8042_CMD_WRITE_CMDB);
208 wait_ready();
209 pio_write_8(&i8042->data, i8042_KBD_IE | i8042_KBD_TRANSLATE |
210 i8042_AUX_IE);
211
212 return 0;
213}
214
215/** Character device connection handler */
216static void i8042_connection(ipc_callid_t iid, ipc_call_t *icall)
217{
218 ipc_callid_t callid;
219 ipc_call_t call;
220 sysarg_t method;
221 devmap_handle_t dh;
222 int retval;
223 int dev_id, i;
224
225 printf(NAME ": connection handler\n");
226
227 /* Get the device handle. */
228 dh = IPC_GET_ARG1(*icall);
229
230 /* Determine which disk device is the client connecting to. */
231 dev_id = -1;
232 for (i = 0; i < MAX_DEVS; i++) {
233 if (i8042_port[i].devmap_handle == dh)
234 dev_id = i;
235 }
236
237 if (dev_id < 0) {
238 ipc_answer_0(iid, EINVAL);
239 return;
240 }
241
242 /* Answer the IPC_M_CONNECT_ME_TO call. */
243 ipc_answer_0(iid, EOK);
244
245 printf(NAME ": accepted connection\n");
246
247 while (1) {
248 callid = async_get_call(&call);
249 method = IPC_GET_IMETHOD(call);
250 switch (method) {
251 case IPC_M_PHONE_HUNGUP:
252 /* The other side has hung up. */
253 ipc_answer_0(callid, EOK);
254 return;
255 case IPC_M_CONNECT_TO_ME:
256 printf(NAME ": creating callback connection\n");
257 if (i8042_port[dev_id].client_phone != -1) {
258 retval = ELIMIT;
259 break;
260 }
261 i8042_port[dev_id].client_phone = IPC_GET_ARG5(call);
262 retval = 0;
263 break;
264 case IPC_FIRST_USER_METHOD:
265 printf(NAME ": write %" PRIun " to devid %d\n",
266 IPC_GET_ARG1(call), dev_id);
267 i8042_port_write(dev_id, IPC_GET_ARG1(call));
268 retval = 0;
269 break;
270 default:
271 retval = EINVAL;
272 break;
273 }
274 ipc_answer_0(callid, retval);
275 }
276}
277
278void i8042_port_write(int devid, uint8_t data)
279{
280 if (devid == DEVID_AUX) {
281 wait_ready();
282 pio_write_8(&i8042->status, i8042_CMD_WRITE_AUX);
283 }
284 wait_ready();
285 pio_write_8(&i8042->data, data);
286}
287
288static void i8042_irq_handler(ipc_callid_t iid, ipc_call_t *call)
289{
290 int status, data;
291 int devid;
292
293 status = IPC_GET_ARG1(*call);
294 data = IPC_GET_ARG2(*call);
295
296 if ((status & i8042_AUX_DATA)) {
297 devid = DEVID_AUX;
298 } else {
299 devid = DEVID_PRI;
300 }
301
302 if (i8042_port[devid].client_phone != -1) {
303 async_msg_1(i8042_port[devid].client_phone,
304 IPC_FIRST_USER_METHOD, data);
305 }
306}
307
308/**
309 * @}
310 */
Note: See TracBrowser for help on using the repository browser.