1/** 2 * \file 3 * \brief Legacy keyboard and mouse (i8042) driver. 4 */ 5 6/* 7 * Copyright (c) 2007, 2008, 2009, 2011, ETH Zurich. 8 * All rights reserved. 9 * 10 * This file is distributed under the terms in the attached LICENSE file. 11 * If you do not find this file, copies can be found by writing to: 12 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group. 13 */ 14 15#include <stdio.h> 16#include <barrelfish/barrelfish.h> 17#include <pci/pci.h> 18 19#include <dev/lpc_kbd_dev.h> 20#include "lpc_kbd.h" 21 22#define KEYBOARD_IRQ 1 23#define MOUSE_IRQ 12 24#define IOPORT_BASE 0x60 25#define IOPORT_MAX 0x64 26 27/// The keyboard 28static struct lpc_kbd_t kbd; 29static bool init_complete; 30 31static void handle_input(lpc_kbd_status_t st) 32{ 33 assert(lpc_kbd_status_obf_extract(st)); 34 35 uint8_t val = lpc_kbd_input_rd(&kbd); 36 37 if (lpc_kbd_status_aobf_extract(st)) { 38 // we have mouse data 39 mouse_data(val); 40 } else { 41 // we have keyboard data 42 static bool extended; 43 if (val == 0xe0) { 44 assert(!extended); 45 extended = true; 46 } else { 47 key_event(val, extended); 48 extended = false; 49 } 50 } 51} 52 53static void interrupt_handler(void *arg) 54{ 55 // ignore interrupts while in initialisation 56 if (!init_complete) { 57 return; 58 } 59 60 // read status register 61 lpc_kbd_status_t st = lpc_kbd_status_rd(&kbd); 62 if (lpc_kbd_status_obf_extract(st)) { 63 handle_input(st); 64 } else { 65 // debug_printf("we took an interrupt, but there's nothing to read?\n"); 66 } 67} 68 69static void send_command(lpc_kbd_cmd_t cmd) 70{ 71 // ensure input buffer and output buffer are empty 72 lpc_kbd_status_t st; 73 int nloop = 0; 74 75 do { 76 st = lpc_kbd_status_rd(&kbd); 77 if (lpc_kbd_status_obf_extract(st)) { 78 handle_input(st); 79 } 80 if (++nloop == 1000) { 81 debug_printf("stuck in send_command: obf=%u aobf=%u ibf=%u\n", 82 lpc_kbd_status_obf_extract(st), 83 lpc_kbd_status_aobf_extract(st), 84 lpc_kbd_status_ibf_extract(st)); 85 } 86 } while (lpc_kbd_status_obf_extract(st) || lpc_kbd_status_ibf_extract(st)); 87 88 // send the command 89 lpc_kbd_command_wr(&kbd, cmd); 90} 91 92static void send_data(uint8_t val) 93{ 94 // ensure input buffer and output buffer are empty 95 lpc_kbd_status_t st; 96 do { 97 st = lpc_kbd_status_rd(&kbd); 98 } while (lpc_kbd_status_obf_extract(st) || lpc_kbd_status_ibf_extract(st)); 99 100 lpc_kbd_output_wr(&kbd, val); 101} 102 103static void init(void) 104{ 105 lpc_kbd_status_t st; 106 107 if (init_complete) { 108 return; 109 } 110 111 // init mackerel state 112 lpc_kbd_initialize(&kbd, IOPORT_BASE); 113 114 // our ultimate goal here is merely to enable mouse and keyboard interrupts 115 // to achieve this, however, we do a little dance with the 8042 controller 116 117 // disable keyboard and mouse, since we share the channel, and don't 118 // want device data misinterpreted as the command byte 119 send_command(lpc_kbd_kbd_disable); 120 send_command(lpc_kbd_aux_disable); 121 122 // issue command to read command byte 123 send_command(lpc_kbd_rd_ccmd); 124 125 // wait for the buffer to fill 126 do { 127 st = lpc_kbd_status_rd(&kbd); 128 } while(!lpc_kbd_status_obf_extract(st)); 129 // XXX: we are supposed to wait for 7us before reading 130 lpc_kbd_ccmd_t ccmd = lpc_kbd_input_rd(&kbd); 131 132 // check that we really succeeded in disabling things beforehand 133 assert(lpc_kbd_ccmd_kbd_dis_extract(ccmd)); 134 assert(lpc_kbd_ccmd_aux_dis_extract(ccmd)); 135 136 // enable both devices and both interrupts, and enable translation 137 ccmd = lpc_kbd_ccmd_kbd_dis_insert(ccmd, 0); 138 ccmd = lpc_kbd_ccmd_kbd_int_insert(ccmd, 1); 139 ccmd = lpc_kbd_ccmd_aux_dis_insert(ccmd, 0); 140 ccmd = lpc_kbd_ccmd_aux_int_insert(ccmd, 1); 141 ccmd = lpc_kbd_ccmd_kbd_xl_insert(ccmd, 1); 142 143 // write back the new command byte 144 send_command(lpc_kbd_wr_ccmd); 145 send_data(ccmd); 146 147 init_complete = true; 148 149 mouse_init(); 150} 151 152void send_mouse_cmd(uint8_t cmd) 153{ 154 assert(init_complete); 155 156 // issue command to write to aux port 157 send_command(lpc_kbd_write_aux); 158 159 // send the actual command 160 send_data(cmd); 161} 162 163int drv_init(void) 164{ 165 int r = pci_client_connect(); 166 assert(r == 0); // XXX 167 168 r = pci_register_legacy_driver_irq(init, IOPORT_BASE, IOPORT_MAX, 169 KEYBOARD_IRQ, interrupt_handler, NULL); 170 if (r != 0) { 171 return r; 172 } 173 174 r = pci_register_legacy_driver_irq(init, IOPORT_BASE, IOPORT_MAX, 175 MOUSE_IRQ, interrupt_handler, NULL); 176 if (r != 0) { 177 return r; 178 } 179 180 return 0; 181} 182