source: mainline/uspace/drv/char/i8042/i8042.c@ dd28c1a

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

i8042: Less function use global parameter.

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