i8042.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2001-2004 Jakub Jermar
00003  * Copyright (c) 2006 Josef Cejka
00004  * Copyright (c) 2009 Jiri Svoboda
00005  * All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted provided that the following conditions
00009  * are met:
00010  *
00011  * - Redistributions of source code must retain the above copyright
00012  *   notice, this list of conditions and the following disclaimer.
00013  * - Redistributions in binary form must reproduce the above copyright
00014  *   notice, this list of conditions and the following disclaimer in the
00015  *   documentation and/or other materials provided with the distribution.
00016  * - The name of the author may not be used to endorse or promote products
00017  *   derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00020  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00021  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00022  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00023  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00024  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00025  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00026  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00028  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  */
00030 
00039 #include <ddi.h>
00040 #include <libarch/ddi.h>
00041 #include <devmap.h>
00042 #include <async.h>
00043 #include <unistd.h>
00044 #include <sysinfo.h>
00045 #include <stdio.h>
00046 #include <errno.h>
00047 #include <inttypes.h>
00048 
00049 #include "i8042.h"
00050 
00051 #define NAME "i8042"
00052 #define NAMESPACE "char"
00053 
00054 /* Interesting bits for status register */
00055 #define i8042_OUTPUT_FULL       0x01
00056 #define i8042_INPUT_FULL        0x02
00057 #define i8042_AUX_DATA          0x20
00058 
00059 /* Command constants */
00060 #define i8042_CMD_WRITE_CMDB    0x60    
00061 #define i8042_CMD_WRITE_AUX     0xd4    
00063 /* Command byte fields */
00064 #define i8042_KBD_IE            0x01
00065 #define i8042_AUX_IE            0x02
00066 #define i8042_KBD_DISABLE       0x10
00067 #define i8042_AUX_DISABLE       0x20
00068 #define i8042_KBD_TRANSLATE     0x40
00069 
00070 
00071 enum {
00072         DEVID_PRI = 0, 
00073         DEVID_AUX = 1, 
00074         MAX_DEVS  = 2
00075 };
00076 
00077 static irq_cmd_t i8042_cmds[] = {
00078         {
00079                 .cmd = CMD_PIO_READ_8,
00080                 .addr = NULL,   /* will be patched in run-time */
00081                 .dstarg = 1
00082         },
00083         {
00084                 .cmd = CMD_BTEST,
00085                 .value = i8042_OUTPUT_FULL,
00086                 .srcarg = 1,
00087                 .dstarg = 3
00088         },
00089         {
00090                 .cmd = CMD_PREDICATE,
00091                 .value = 2,
00092                 .srcarg = 3
00093         },
00094         {
00095                 .cmd = CMD_PIO_READ_8,
00096                 .addr = NULL,   /* will be patched in run-time */
00097                 .dstarg = 2
00098         },
00099         {
00100                 .cmd = CMD_ACCEPT
00101         }
00102 };
00103 
00104 static irq_code_t i8042_kbd = {
00105         sizeof(i8042_cmds) / sizeof(irq_cmd_t),
00106         i8042_cmds
00107 };
00108 
00109 static uintptr_t i8042_physical;
00110 static uintptr_t i8042_kernel;
00111 static i8042_t * i8042;
00112 
00113 static i8042_port_t i8042_port[MAX_DEVS];
00114 
00115 static void wait_ready(void)
00116 {
00117         while (pio_read_8(&i8042->status) & i8042_INPUT_FULL);
00118 }
00119 
00120 static void i8042_irq_handler(ipc_callid_t iid, ipc_call_t *call);
00121 static void i8042_connection(ipc_callid_t iid, ipc_call_t *icall);
00122 static int i8042_init(void);
00123 static void i8042_port_write(int devid, uint8_t data);
00124 
00125 
00126 int main(int argc, char *argv[])
00127 {
00128         char name[16];
00129         int i, rc;
00130         char dchar[MAX_DEVS] = { 'a', 'b' };
00131 
00132         printf(NAME ": i8042 PS/2 port driver\n");
00133 
00134         rc = devmap_driver_register(NAME, i8042_connection);
00135         if (rc < 0) {
00136                 printf(NAME ": Unable to register driver.\n");
00137                 return rc;
00138         }
00139 
00140         if (i8042_init() != EOK)
00141                 return -1;
00142 
00143         for (i = 0; i < MAX_DEVS; i++) {
00144                 i8042_port[i].client_phone = -1;
00145 
00146                 snprintf(name, 16, "%s/ps2%c", NAMESPACE, dchar[i]);
00147                 rc = devmap_device_register(name, &i8042_port[i].devmap_handle);
00148                 if (rc != EOK) {
00149                         printf(NAME ": Unable to register device %s.\n", name);
00150                         return rc;
00151                 }
00152                 printf(NAME ": Registered device %s\n", name);
00153         }
00154 
00155         printf(NAME ": Accepting connections\n");
00156         task_retval(0);
00157         async_manager();
00158 
00159         /* Not reached */
00160         return 0;
00161 }
00162 
00163 static int i8042_init(void)
00164 {
00165         if (sysinfo_get_value("i8042.address.physical", &i8042_physical) != EOK)
00166                 return -1;
00167         
00168         if (sysinfo_get_value("i8042.address.kernel", &i8042_kernel) != EOK)
00169                 return -1;
00170         
00171         void *vaddr;
00172         if (pio_enable((void *) i8042_physical, sizeof(i8042_t), &vaddr) != 0)
00173                 return -1;
00174         
00175         i8042 = vaddr;
00176         
00177         sysarg_t inr_a;
00178         sysarg_t inr_b;
00179         
00180         if (sysinfo_get_value("i8042.inr_a", &inr_a) != EOK)
00181                 return -1;
00182         
00183         if (sysinfo_get_value("i8042.inr_b", &inr_b) != EOK)
00184                 return -1;
00185         
00186         async_set_interrupt_received(i8042_irq_handler);
00187         
00188         /* Disable kbd and aux */
00189         wait_ready();
00190         pio_write_8(&i8042->status, i8042_CMD_WRITE_CMDB);
00191         wait_ready();
00192         pio_write_8(&i8042->data, i8042_KBD_DISABLE | i8042_AUX_DISABLE);
00193 
00194         /* Flush all current IO */
00195         while (pio_read_8(&i8042->status) & i8042_OUTPUT_FULL)
00196                 (void) pio_read_8(&i8042->data);
00197 
00198         i8042_kbd.cmds[0].addr = (void *) &((i8042_t *) i8042_kernel)->status;
00199         i8042_kbd.cmds[3].addr = (void *) &((i8042_t *) i8042_kernel)->data;
00200         register_irq(inr_a, device_assign_devno(), 0, &i8042_kbd);
00201         register_irq(inr_b, device_assign_devno(), 0, &i8042_kbd);
00202         printf("%s: registered for interrupts %" PRIun " and %" PRIun "\n",
00203             NAME, inr_a, inr_b);
00204 
00205         wait_ready();
00206         pio_write_8(&i8042->status, i8042_CMD_WRITE_CMDB);
00207         wait_ready();
00208         pio_write_8(&i8042->data, i8042_KBD_IE | i8042_KBD_TRANSLATE |
00209             i8042_AUX_IE);
00210 
00211         return 0;
00212 }
00213 
00215 static void i8042_connection(ipc_callid_t iid, ipc_call_t *icall)
00216 {
00217         ipc_callid_t callid;
00218         ipc_call_t call;
00219         sysarg_t method;
00220         devmap_handle_t dh;
00221         int retval;
00222         int dev_id, i;
00223 
00224         printf(NAME ": connection handler\n");
00225 
00226         /* Get the device handle. */
00227         dh = IPC_GET_ARG1(*icall);
00228 
00229         /* Determine which disk device is the client connecting to. */
00230         dev_id = -1;
00231         for (i = 0; i < MAX_DEVS; i++) {
00232                 if (i8042_port[i].devmap_handle == dh)
00233                         dev_id = i;
00234         }
00235 
00236         if (dev_id < 0) {
00237                 async_answer_0(iid, EINVAL);
00238                 return;
00239         }
00240 
00241         /* Answer the IPC_M_CONNECT_ME_TO call. */
00242         async_answer_0(iid, EOK);
00243 
00244         printf(NAME ": accepted connection\n");
00245 
00246         while (1) {
00247                 callid = async_get_call(&call);
00248                 method = IPC_GET_IMETHOD(call);
00249                 switch (method) {
00250                 case IPC_M_PHONE_HUNGUP:
00251                         /* The other side has hung up. */
00252                         async_answer_0(callid, EOK);
00253                         return;
00254                 case IPC_M_CONNECT_TO_ME:
00255                         printf(NAME ": creating callback connection\n");
00256                         if (i8042_port[dev_id].client_phone != -1) {
00257                                 retval = ELIMIT;
00258                                 break;
00259                         }
00260                         i8042_port[dev_id].client_phone = IPC_GET_ARG5(call);
00261                         retval = 0;
00262                         break;
00263                 case IPC_FIRST_USER_METHOD:
00264                         printf(NAME ": write %" PRIun " to devid %d\n",
00265                             IPC_GET_ARG1(call), dev_id);
00266                         i8042_port_write(dev_id, IPC_GET_ARG1(call));
00267                         retval = 0;
00268                         break;
00269                 default:
00270                         retval = EINVAL;
00271                         break;
00272                 }
00273                 async_answer_0(callid, retval);
00274         }
00275 }
00276 
00277 void i8042_port_write(int devid, uint8_t data)
00278 {
00279         if (devid == DEVID_AUX) {
00280                 wait_ready();
00281                 pio_write_8(&i8042->status, i8042_CMD_WRITE_AUX);
00282         }
00283         wait_ready();
00284         pio_write_8(&i8042->data, data);
00285 }
00286 
00287 static void i8042_irq_handler(ipc_callid_t iid, ipc_call_t *call)
00288 {
00289         int status, data;
00290         int devid;
00291 
00292         status = IPC_GET_ARG1(*call);
00293         data = IPC_GET_ARG2(*call);
00294 
00295         if ((status & i8042_AUX_DATA)) {
00296                 devid = DEVID_AUX;
00297         } else {
00298                 devid = DEVID_PRI;
00299         }
00300 
00301         if (i8042_port[devid].client_phone != -1) {
00302                 async_msg_1(i8042_port[devid].client_phone,
00303                     IPC_FIRST_USER_METHOD, data);
00304         }
00305 }
00306 

Generated on Thu Jun 2 07:45:50 2011 for HelenOS/USB by  doxygen 1.4.7