Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.14-rc3 459 lines 9.3 kB view raw
1/* 2 * linux/drivers/char/ec3104_keyb.c 3 * 4 * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> 5 * 6 * based on linux/drivers/char/pc_keyb.c, which had the following comments: 7 * 8 * Separation of the PC low-level part by Geert Uytterhoeven, May 1997 9 * See keyboard.c for the whole history. 10 * 11 * Major cleanup by Martin Mares, May 1997 12 * 13 * Combined the keyboard and PS/2 mouse handling into one file, 14 * because they share the same hardware. 15 * Johan Myreen <jem@iki.fi> 1998-10-08. 16 * 17 * Code fixes to handle mouse ACKs properly. 18 * C. Scott Ananian <cananian@alumni.princeton.edu> 1999-01-29. 19 */ 20/* EC3104 note: 21 * This code was written without any documentation about the EC3104 chip. While 22 * I hope I got most of the basic functionality right, the register names I use 23 * are most likely completely different from those in the chip documentation. 24 * 25 * If you have any further information about the EC3104, please tell me 26 * (prumpf@tux.org). 27 */ 28 29#include <linux/config.h> 30 31#include <linux/spinlock.h> 32#include <linux/sched.h> 33#include <linux/interrupt.h> 34#include <linux/tty.h> 35#include <linux/mm.h> 36#include <linux/signal.h> 37#include <linux/init.h> 38#include <linux/kbd_ll.h> 39#include <linux/delay.h> 40#include <linux/random.h> 41#include <linux/poll.h> 42#include <linux/miscdevice.h> 43#include <linux/slab.h> 44#include <linux/kbd_kern.h> 45#include <linux/smp_lock.h> 46#include <linux/bitops.h> 47 48#include <asm/keyboard.h> 49#include <asm/uaccess.h> 50#include <asm/irq.h> 51#include <asm/system.h> 52#include <asm/ec3104.h> 53 54#include <asm/io.h> 55 56/* Some configuration switches are present in the include file... */ 57 58#include <linux/pc_keyb.h> 59 60#define MSR_CTS 0x10 61#define MCR_RTS 0x02 62#define LSR_DR 0x01 63#define LSR_BOTH_EMPTY 0x60 64 65static struct e5_struct { 66 u8 packet[8]; 67 int pos; 68 int length; 69 70 u8 cached_mcr; 71 u8 last_msr; 72} ec3104_keyb; 73 74/* Simple translation table for the SysRq keys */ 75 76 77#ifdef CONFIG_MAGIC_SYSRQ 78unsigned char ec3104_kbd_sysrq_xlate[128] = 79 "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ 80 "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ 81 "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ 82 "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ 83 "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ 84 "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ 85 "\r\000/"; /* 0x60 - 0x6f */ 86#endif 87 88static void kbd_write_command_w(int data); 89static void kbd_write_output_w(int data); 90#ifdef CONFIG_PSMOUSE 91static void aux_write_ack(int val); 92static void __aux_write_ack(int val); 93#endif 94 95static DEFINE_SPINLOCK(kbd_controller_lock); 96static unsigned char handle_kbd_event(void); 97 98/* used only by send_data - set by keyboard_interrupt */ 99static volatile unsigned char reply_expected; 100static volatile unsigned char acknowledge; 101static volatile unsigned char resend; 102 103 104int ec3104_kbd_setkeycode(unsigned int scancode, unsigned int keycode) 105{ 106 return 0; 107} 108 109int ec3104_kbd_getkeycode(unsigned int scancode) 110{ 111 return 0; 112} 113 114 115/* yes, it probably would be faster to use an array. I don't care. */ 116 117static inline unsigned char ec3104_scan2key(unsigned char scancode) 118{ 119 switch (scancode) { 120 case 1: /* '`' */ 121 return 41; 122 123 case 2 ... 27: 124 return scancode; 125 126 case 28: /* '\\' */ 127 return 43; 128 129 case 29 ... 39: 130 return scancode + 1; 131 132 case 40: /* '\r' */ 133 return 28; 134 135 case 41 ... 50: 136 return scancode + 3; 137 138 case 51: /* ' ' */ 139 return 57; 140 141 case 52: /* escape */ 142 return 1; 143 144 case 54: /* insert/delete (labelled delete) */ 145 /* this should arguably be 110, but I'd like to have ctrl-alt-del 146 * working with a standard keymap */ 147 return 111; 148 149 case 55: /* left */ 150 return 105; 151 case 56: /* home */ 152 return 102; 153 case 57: /* end */ 154 return 107; 155 case 58: /* up */ 156 return 103; 157 case 59: /* down */ 158 return 108; 159 case 60: /* pgup */ 160 return 104; 161 case 61: /* pgdown */ 162 return 109; 163 case 62: /* right */ 164 return 106; 165 166 case 79 ... 88: /* f1 - f10 */ 167 return scancode - 20; 168 169 case 89 ... 90: /* f11 - f12 */ 170 return scancode - 2; 171 172 case 91: /* left shift */ 173 return 42; 174 175 case 92: /* right shift */ 176 return 54; 177 178 case 93: /* left alt */ 179 return 56; 180 case 94: /* right alt */ 181 return 100; 182 case 95: /* left ctrl */ 183 return 29; 184 case 96: /* right ctrl */ 185 return 97; 186 187 case 97: /* caps lock */ 188 return 58; 189 case 102: /* left windows */ 190 return 125; 191 case 103: /* right windows */ 192 return 126; 193 194 case 106: /* Fn */ 195 /* this is wrong. */ 196 return 84; 197 198 default: 199 return 0; 200 } 201} 202 203int ec3104_kbd_translate(unsigned char scancode, unsigned char *keycode, 204 char raw_mode) 205{ 206 scancode &= 0x7f; 207 208 *keycode = ec3104_scan2key(scancode); 209 210 return 1; 211} 212 213char ec3104_kbd_unexpected_up(unsigned char keycode) 214{ 215 return 0200; 216} 217 218static inline void handle_keyboard_event(unsigned char scancode) 219{ 220#ifdef CONFIG_VT 221 handle_scancode(scancode, !(scancode & 0x80)); 222#endif 223 tasklet_schedule(&keyboard_tasklet); 224} 225 226void ec3104_kbd_leds(unsigned char leds) 227{ 228} 229 230static u8 e5_checksum(u8 *packet, int count) 231{ 232 int i; 233 u8 sum = 0; 234 235 for (i=0; i<count; i++) 236 sum ^= packet[i]; 237 238 if (sum & 0x80) 239 sum ^= 0xc0; 240 241 return sum; 242} 243 244static void e5_wait_for_cts(struct e5_struct *k) 245{ 246 u8 msr; 247 248 do { 249 msr = ctrl_inb(EC3104_SER4_MSR); 250 } while (!(msr & MSR_CTS)); 251} 252 253 254static void e5_send_byte(u8 byte, struct e5_struct *k) 255{ 256 u8 status; 257 258 do { 259 status = ctrl_inb(EC3104_SER4_LSR); 260 } while ((status & LSR_BOTH_EMPTY) != LSR_BOTH_EMPTY); 261 262 printk("<%02x>", byte); 263 264 ctrl_outb(byte, EC3104_SER4_DATA); 265 266 do { 267 status = ctrl_inb(EC3104_SER4_LSR); 268 } while ((status & LSR_BOTH_EMPTY) != LSR_BOTH_EMPTY); 269 270} 271 272static int e5_send_packet(u8 *packet, int count, struct e5_struct *k) 273{ 274 int i; 275 276 disable_irq(EC3104_IRQ_SER4); 277 278 if (k->cached_mcr & MCR_RTS) { 279 printk("e5_send_packet: too slow\n"); 280 enable_irq(EC3104_IRQ_SER4); 281 return -EAGAIN; 282 } 283 284 k->cached_mcr |= MCR_RTS; 285 ctrl_outb(k->cached_mcr, EC3104_SER4_MCR); 286 287 e5_wait_for_cts(k); 288 289 printk("p: "); 290 291 for(i=0; i<count; i++) 292 e5_send_byte(packet[i], k); 293 294 e5_send_byte(e5_checksum(packet, count), k); 295 296 printk("\n"); 297 298 udelay(1500); 299 300 k->cached_mcr &= ~MCR_RTS; 301 ctrl_outb(k->cached_mcr, EC3104_SER4_MCR); 302 303 set_current_state(TASK_UNINTERRUPTIBLE); 304 305 306 307 enable_irq(EC3104_IRQ_SER4); 308 309 310 311 return 0; 312} 313 314/* 315 * E5 packets we know about: 316 * E5->host 0x80 0x05 <checksum> - resend packet 317 * host->E5 0x83 0x43 <contrast> - set LCD contrast 318 * host->E5 0x85 0x41 0x02 <brightness> 0x02 - set LCD backlight 319 * E5->host 0x87 <ps2 packet> 0x00 <checksum> - external PS2 320 * E5->host 0x88 <scancode> <checksum> - key press 321 */ 322 323static void e5_receive(struct e5_struct *k) 324{ 325 k->packet[k->pos++] = ctrl_inb(EC3104_SER4_DATA); 326 327 if (k->pos == 1) { 328 switch(k->packet[0]) { 329 case 0x80: 330 k->length = 3; 331 break; 332 333 case 0x87: /* PS2 ext */ 334 k->length = 6; 335 break; 336 337 case 0x88: /* keyboard */ 338 k->length = 3; 339 break; 340 341 default: 342 k->length = 1; 343 printk(KERN_WARNING "unknown E5 packet %02x\n", 344 k->packet[0]); 345 } 346 } 347 348 if (k->pos == k->length) { 349 int i; 350 351 if (e5_checksum(k->packet, k->length) != 0) 352 printk(KERN_WARNING "E5: wrong checksum\n"); 353 354#if 0 355 printk("E5 packet ["); 356 for(i=0; i<k->length; i++) { 357 printk("%02x ", k->packet[i]); 358 } 359 360 printk("(%02x)]\n", e5_checksum(k->packet, k->length-1)); 361#endif 362 363 switch(k->packet[0]) { 364 case 0x80: 365 case 0x88: 366 handle_keyboard_event(k->packet[1]); 367 break; 368 } 369 370 k->pos = k->length = 0; 371 } 372} 373 374static void ec3104_keyb_interrupt(int irq, void *data, struct pt_regs *regs) 375{ 376 struct e5_struct *k = &ec3104_keyb; 377 u8 msr, lsr; 378 379 msr = ctrl_inb(EC3104_SER4_MSR); 380 381 if ((msr & MSR_CTS) && !(k->last_msr & MSR_CTS)) { 382 if (k->cached_mcr & MCR_RTS) 383 printk("confused: RTS already high\n"); 384 /* CTS went high. Send RTS. */ 385 k->cached_mcr |= MCR_RTS; 386 387 ctrl_outb(k->cached_mcr, EC3104_SER4_MCR); 388 } else if ((!(msr & MSR_CTS)) && (k->last_msr & MSR_CTS)) { 389 /* CTS went low. */ 390 if (!(k->cached_mcr & MCR_RTS)) 391 printk("confused: RTS already low\n"); 392 393 k->cached_mcr &= ~MCR_RTS; 394 395 ctrl_outb(k->cached_mcr, EC3104_SER4_MCR); 396 } 397 398 k->last_msr = msr; 399 400 lsr = ctrl_inb(EC3104_SER4_LSR); 401 402 if (lsr & LSR_DR) 403 e5_receive(k); 404} 405 406static void ec3104_keyb_clear_state(void) 407{ 408 struct e5_struct *k = &ec3104_keyb; 409 u8 msr, lsr; 410 411 /* we want CTS to be low */ 412 k->last_msr = 0; 413 414 for (;;) { 415 msleep(100); 416 417 msr = ctrl_inb(EC3104_SER4_MSR); 418 419 lsr = ctrl_inb(EC3104_SER4_LSR); 420 421 if (lsr & LSR_DR) { 422 e5_receive(k); 423 continue; 424 } 425 426 if ((msr & MSR_CTS) && !(k->last_msr & MSR_CTS)) { 427 if (k->cached_mcr & MCR_RTS) 428 printk("confused: RTS already high\n"); 429 /* CTS went high. Send RTS. */ 430 k->cached_mcr |= MCR_RTS; 431 432 ctrl_outb(k->cached_mcr, EC3104_SER4_MCR); 433 } else if ((!(msr & MSR_CTS)) && (k->last_msr & MSR_CTS)) { 434 /* CTS went low. */ 435 if (!(k->cached_mcr & MCR_RTS)) 436 printk("confused: RTS already low\n"); 437 438 k->cached_mcr &= ~MCR_RTS; 439 440 ctrl_outb(k->cached_mcr, EC3104_SER4_MCR); 441 } else 442 break; 443 444 k->last_msr = msr; 445 446 continue; 447 } 448} 449 450void __init ec3104_kbd_init_hw(void) 451{ 452 ec3104_keyb.last_msr = ctrl_inb(EC3104_SER4_MSR); 453 ec3104_keyb.cached_mcr = ctrl_inb(EC3104_SER4_MCR); 454 455 ec3104_keyb_clear_state(); 456 457 /* Ok, finally allocate the IRQ, and off we go.. */ 458 request_irq(EC3104_IRQ_SER4, ec3104_keyb_interrupt, 0, "keyboard", NULL); 459}