source: mainline/uspace/srv/hw/char/i8042/i8042.c@ 925a21e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 925a21e was 5da7199, checked in by Martin Decky <martin@…>, 14 years ago

remove the obsolete async API

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