📄 pcikbd.c
字号:
/* $Id: pcikbd.c,v 1.61 2001/08/18 09:40:46 davem Exp $ * pcikbd.c: Ultra/AX PC keyboard support. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) * JavaStation support by Pete A. Zaitcev. * * This code is mainly put together from various places in * drivers/char, please refer to these sources for credits * to the original authors. */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/ioport.h>#include <linux/poll.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/random.h>#include <linux/miscdevice.h>#include <linux/kbd_ll.h>#include <linux/kbd_kern.h>#include <linux/vt_kern.h>#include <linux/delay.h>#include <linux/spinlock.h>#include <linux/smp_lock.h>#include <linux/init.h>#include <asm/ebus.h>#if defined(CONFIG_USB) && defined(CONFIG_SPARC64)#include <asm/isa.h>#endif#include <asm/oplib.h>#include <asm/irq.h>#include <asm/io.h>#include <asm/uaccess.h>/* * Different platforms provide different permutations of names. * AXi - kb_ps2, kdmouse. * MrCoffee - keyboard, mouse. * Espresso - keyboard, kdmouse. */#define PCI_KB_NAME1 "kb_ps2"#define PCI_KB_NAME2 "keyboard"#define PCI_MS_NAME1 "kdmouse"#define PCI_MS_NAME2 "mouse"#include "pcikbd.h"#include "sunserial.h"#ifndef __sparc_v9__static int pcikbd_mrcoffee = 0;#else#define pcikbd_mrcoffee 0extern void (*prom_keyboard)(void);#endifstatic unsigned long pcikbd_iobase = 0;static unsigned int pcikbd_irq = 0;/* used only by send_data - set by keyboard_interrupt */static volatile unsigned char reply_expected = 0;static volatile unsigned char acknowledge = 0;static volatile unsigned char resend = 0;static spinlock_t pcikbd_lock = SPIN_LOCK_UNLOCKED;static void pcikbd_write(int address, int data);static int pcikbd_wait_for_input(void);unsigned char pckbd_read_mask = KBD_STAT_OBF;extern int pcikbd_init(void);extern void pci_compute_shiftstate(void);extern int pci_setkeycode(unsigned int, unsigned int);extern int pci_getkeycode(unsigned int);extern void pci_setledstate(struct kbd_struct *, unsigned int);extern unsigned char pci_getledstate(void);#define pcikbd_inb(x) inb(x)#define pcikbd_outb(v,x) outb(v,x)/* Wait for keyboard controller input buffer to drain. * Must be invoked under the pcikbd_lock. */static void kb_wait(void){ unsigned long timeout = 250; do { if(!(pcikbd_inb(pcikbd_iobase + KBD_STATUS_REG) & KBD_STAT_IBF)) return; mdelay(1); } while (--timeout);}/* * Translation of escaped scancodes to keycodes. * This is now user-settable. * The keycodes 1-88,96-111,119 are fairly standard, and * should probably not be changed - changing might confuse X. * X also interprets scancode 0x5d (KEY_Begin). * * For 1-88 keycode equals scancode. */#define E0_KPENTER 96#define E0_RCTRL 97#define E0_KPSLASH 98#define E0_PRSCR 99#define E0_RALT 100#define E0_BREAK 101 /* (control-pause) */#define E0_HOME 102#define E0_UP 103#define E0_PGUP 104#define E0_LEFT 105#define E0_RIGHT 106#define E0_END 107#define E0_DOWN 108#define E0_PGDN 109#define E0_INS 110#define E0_DEL 111#define E1_PAUSE 119/* * The keycodes below are randomly located in 89-95,112-118,120-127. * They could be thrown away (and all occurrences below replaced by 0), * but that would force many users to use the `setkeycodes' utility, where * they needed not before. It does not matter that there are duplicates, as * long as no duplication occurs for any single keyboard. */#define SC_LIM 89#define FOCUS_PF1 85 /* actual code! */#define FOCUS_PF2 89#define FOCUS_PF3 90#define FOCUS_PF4 91#define FOCUS_PF5 92#define FOCUS_PF6 93#define FOCUS_PF7 94#define FOCUS_PF8 95#define FOCUS_PF9 120#define FOCUS_PF10 121#define FOCUS_PF11 122#define FOCUS_PF12 123#define JAP_86 124/* tfj@olivia.ping.dk: * The four keys are located over the numeric keypad, and are * labelled A1-A4. It's an rc930 keyboard, from * Regnecentralen/RC International, Now ICL. * Scancodes: 59, 5a, 5b, 5c. */#define RGN1 124#define RGN2 125#define RGN3 126#define RGN4 127static unsigned char high_keys[128 - SC_LIM] = { RGN1, RGN2, RGN3, RGN4, 0, 0, 0, /* 0x59-0x5f */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ 0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12, /* 0x68-0x6f */ 0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3, /* 0x70-0x77 */ FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7, /* 0x78-0x7b */ FOCUS_PF8, JAP_86, FOCUS_PF10, 0 /* 0x7c-0x7f */};/* BTC */#define E0_MACRO 112/* LK450 */#define E0_F13 113#define E0_F14 114#define E0_HELP 115#define E0_DO 116#define E0_F17 117#define E0_KPMINPLUS 118/* * My OmniKey generates e0 4c for the "OMNI" key and the * right alt key does nada. [kkoller@nyx10.cs.du.edu] */#define E0_OK 124/* * New microsoft keyboard is rumoured to have * e0 5b (left window button), e0 5c (right window button), * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU] * [or: Windows_L, Windows_R, TaskMan] */#define E0_MSLW 125#define E0_MSRW 126#define E0_MSTM 127static unsigned char e0_keys[128] = { 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */ 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */ 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */ E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */ E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */ E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */ E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */ 0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, /* 0x58-0x5f */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ 0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */ 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */};/* Simple translation table for the SysRq keys */#ifdef CONFIG_MAGIC_SYSRQunsigned char pcikbd_sysrq_xlate[128] = "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ "\r\000/"; /* 0x60 - 0x6f */#endif#define DEFAULT_KEYB_REP_DELAY 250#define DEFAULT_KEYB_REP_RATE 30 /* cps */static struct kbd_repeat kbdrate = { DEFAULT_KEYB_REP_DELAY, DEFAULT_KEYB_REP_RATE};static unsigned char parse_kbd_rate(struct kbd_repeat *r);static int write_kbd_rate(unsigned char r);int pcikbd_setkeycode(unsigned int scancode, unsigned int keycode){ if(scancode < SC_LIM || scancode > 255 || keycode > 127) return -EINVAL; if(scancode < 128) high_keys[scancode - SC_LIM] = keycode; else e0_keys[scancode - 128] = keycode; return 0;}int pcikbd_getkeycode(unsigned int scancode){ return (scancode < SC_LIM || scancode > 255) ? -EINVAL : (scancode < 128) ? high_keys[scancode - SC_LIM] : e0_keys[scancode - 128];}static int do_acknowledge(unsigned char scancode){ if(reply_expected) { if(scancode == KBD_REPLY_ACK) { acknowledge = 1; reply_expected = 0; return 0; } else if(scancode == KBD_REPLY_RESEND) { resend = 1; reply_expected = 0; return 0; } } return 1;}#ifdef __sparc_v9__static void pcikbd_enter_prom(void){ pcikbd_write(KBD_DATA_REG, KBD_CMD_DISABLE); if(pcikbd_wait_for_input() != KBD_REPLY_ACK) printk("Prom Enter: Disable keyboard: no ACK\n"); /* Disable PC scancode translation */ pcikbd_write(KBD_CNTL_REG, KBD_CCMD_WRITE_MODE); pcikbd_write(KBD_DATA_REG, KBD_MODE_SYS); pcikbd_write(KBD_DATA_REG, KBD_CMD_ENABLE); if (pcikbd_wait_for_input() != KBD_REPLY_ACK) printk("Prom Enter: Enable Keyboard: no ACK\n");}#endifstatic void ctrl_break(void){ extern int stop_a_enabled; unsigned long timeout; int status, data; int mode; if (!stop_a_enabled) return; pcikbd_write(KBD_DATA_REG, KBD_CMD_DISABLE); if(pcikbd_wait_for_input() != KBD_REPLY_ACK) printk("Prom Enter: Disable keyboard: no ACK\n"); /* Save current mode register settings */ pcikbd_write(KBD_CNTL_REG, KBD_CCMD_READ_MODE); if ((mode = pcikbd_wait_for_input()) == -1) printk("Prom Enter: Read Mode: no ACK\n"); /* Disable PC scancode translation */ pcikbd_write(KBD_CNTL_REG, KBD_CCMD_WRITE_MODE); pcikbd_write(KBD_DATA_REG, mode & ~(KBD_MODE_KCC)); pcikbd_write(KBD_DATA_REG, KBD_CMD_ENABLE); if (pcikbd_wait_for_input() != KBD_REPLY_ACK) printk("Prom Enter: Enable Keyboard: no ACK\n"); /* Drop into OBP. * Note that we must flush the user windows * first before giving up control. */ flush_user_windows(); prom_cmdline(); /* Read prom's key up event (use short timeout) */ do { timeout = 10; do { mdelay(1); status = pcikbd_inb(pcikbd_iobase + KBD_STATUS_REG); if (!(status & KBD_STAT_OBF)) continue; data = pcikbd_inb(pcikbd_iobase + KBD_DATA_REG); if (status & (KBD_STAT_GTO | KBD_STAT_PERR)) continue; break; } while (--timeout > 0); } while (timeout > 0); /* Reenable PC scancode translation */ pcikbd_write(KBD_DATA_REG, KBD_CMD_DISABLE); if(pcikbd_wait_for_input() != KBD_REPLY_ACK) printk("Prom Leave: Disable keyboard: no ACK\n"); pcikbd_write(KBD_CNTL_REG, KBD_CCMD_WRITE_MODE); pcikbd_write(KBD_DATA_REG, mode); pcikbd_write(KBD_DATA_REG, KBD_CMD_ENABLE); if (pcikbd_wait_for_input() != KBD_REPLY_ACK) printk("Prom Leave: Enable Keyboard: no ACK\n"); /* Reset keyboard rate */ write_kbd_rate(parse_kbd_rate(&kbdrate));}int pcikbd_translate(unsigned char scancode, unsigned char *keycode, char raw_mode){ static int prev_scancode = 0; int down = scancode & 0x80 ? 0 : 1; if (scancode == 0xe0 || scancode == 0xe1) { prev_scancode = scancode; return 0; } if (scancode == 0x00 || scancode == 0xff) { prev_scancode = 0; return 0; } scancode &= 0x7f; if(prev_scancode) { if(prev_scancode != 0xe0) { if(prev_scancode == 0xe1 && scancode == 0x1d) { prev_scancode = 0x100; return 0; } else if(prev_scancode == 0x100 && scancode == 0x45) { *keycode = E1_PAUSE; prev_scancode = 0; } else { prev_scancode = 0; return 0; } } else { prev_scancode = 0; if(scancode == 0x2a || scancode == 0x36) return 0; if(e0_keys[scancode]) *keycode = e0_keys[scancode]; else return 0; } } else if(scancode >= SC_LIM) { *keycode = high_keys[scancode - SC_LIM]; if(!*keycode) return 0; } else *keycode = scancode; if (*keycode == E0_BREAK) { if (down) return 0; /* Handle ctrl-break event */ ctrl_break(); /* Send ctrl up event to the keyboard driver */ *keycode = 0x1d; } return 1;}char pcikbd_unexpected_up(unsigned char keycode){ if(keycode >= SC_LIM || keycode == 85) return 0; else return 0200;}static voidpcikbd_interrupt(int irq, void *dev_id, struct pt_regs *regs){ unsigned long flags; unsigned char status; spin_lock_irqsave(&pcikbd_lock, flags); kbd_pt_regs = regs; status = pcikbd_inb(pcikbd_iobase + KBD_STATUS_REG); do { unsigned char scancode; if(status & pckbd_read_mask & KBD_STAT_MOUSE_OBF) break; scancode = pcikbd_inb(pcikbd_iobase + KBD_DATA_REG); if((status & KBD_STAT_OBF) && do_acknowledge(scancode)) handle_scancode(scancode, !(scancode & 0x80)); status = pcikbd_inb(pcikbd_iobase + KBD_STATUS_REG); } while(status & KBD_STAT_OBF); tasklet_schedule(&keyboard_tasklet); spin_unlock_irqrestore(&pcikbd_lock, flags);}static int send_data(unsigned char data){ int retries = 3; unsigned long flags; do { unsigned long timeout = 1000; spin_lock_irqsave(&pcikbd_lock, flags); kb_wait(); acknowledge = 0; resend = 0; reply_expected = 1; pcikbd_outb(data, pcikbd_iobase + KBD_DATA_REG); spin_unlock_irqrestore(&pcikbd_lock, flags); do { if (acknowledge) return 1; if (resend) break; mdelay(1); } while (--timeout); if (timeout == 0) break; } while (retries-- > 0); return 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -