📄 kbd.c
字号:
/*****************************************************************************
kbd.c - PC keyboard demo code/driver for OS
Christopher Giese <geezer[AT]execpc.com>
Release date 9/2/99. Distribute freely. ABSOLUTELY NO WARRANTY.
Compile with DJGPP or Turbo C or possibly Borland C.
Changes/fixes:
- got numeric keypad and NumLock working
- slightly different scancode-to-ASCII conversion logic
- created #defines for raw scancode values
- different ordering of events during keyboard and controller init
Sources:
- PORTS.A of Ralf Brown's interrupt list collection
- repairfaq.org keyboard FAQ at
http://www.repairfaq.org/filipg/LINK/PORTS/F_Keyboard_FAQ.html
- Linux source code
Test hardware:
- New Samsung KB3T001SAXAA 104-key keyboard
- Old Maxi 2186035-00-21 101-key keyboard
To do:
- turn off auto-repeat for lock keys?
- ioctl()s (functions, for now -- use interrupts?) to
- set make/break or make-only for each key
- set typematic mode or not for each key
- set typematic rate and delay
- Unicode/UTF-8
- Alt+number entered on numeric keypad == scancode, like DOS?
*****************************************************************************/
#include <stdio.h> /* printf() */
/* union REGS, int86(), inportb(), outportb(), getvect(), setvect() */
#include <dos.h>
#if 1
#define DEBUG(X) X
#else
#define DEBUG(X)
#endif
/* quick and dirty begin/end critical section... */
#define critb() (disable(), 0)
#define crite(X) enable()
/* geezer's Portable Interrupt Macros (tm) */
#if defined(__TURBOC__)
/* getvect(), setvect() in dos.h */
/* from Turbo C++ getvect() help */
#ifdef __cplusplus
#define __CPPARGS ...
#else
#define __CPPARGS
#endif
typedef void interrupt(*vector)(__CPPARGS);
#define SAVE_VECT(Num,Vec) Vec=getvect(Num)
#define SET_VECT(Num,Fn) setvect(Num, Fn)
#define RESTORE_VECT(Num,Vec) setvect(Num, Vec)
#elif defined(__DJGPP__)
#include <dpmi.h> /* _go32_dpmi_... */
#include <go32.h> /* _my_cs() */
#define interrupt
#define __CPPARGS
typedef _go32_dpmi_seginfo vector;
#define SAVE_VECT(Num,Vec) \
_go32_dpmi_get_protected_mode_interrupt_vector(Num, &Vec)
#define SET_VECT(Num,Fn) \
{ _go32_dpmi_seginfo NewVector; \
\
NewVector.pm_selector=_my_cs(); \
NewVector.pm_offset=(unsigned long)Fn; \
_go32_dpmi_allocate_iret_wrapper(&NewVector); \
_go32_dpmi_set_protected_mode_interrupt_vector \
(Num, &NewVector); }
#define RESTORE_VECT(Num,Vec) \
_go32_dpmi_set_protected_mode_interrupt_vector(Num, &Vec)
#else
#error Not Turbo C nor DJGPP, sorry.
#endif
/* Info from file PORTS.A of Ralf Brown's interrupt list collection
I/O addresses of 8042 keyboard controller on PC motherboard */
#define _KBD_STAT_REG 0x64
#define _KBD_CMD_REG 0x64
#define _KBD_DATA_REG 0x60
/* 8042 controller commands (write to 0x64; Table P0401 in PORTS.A) */
#define _KBD_CCMD_WRCMDB 0x60 /* write "Command Byte" */
#define _KBD_CCMD_TEST1 0xAA /* controller self-test */
#define _KBD_CCMD_TEST2 0xAB /* interface test */
#define _KBD_CCMD_ENABLE 0xAE
/* Linux enables A20 by writing _KBD_CCMD_WROPRT to 0x64 and 0xDF to 0x60 */
#define _KBD_CCMD_WROPRT 0xD1 /* write 8042 output port */
/* keyboard commands (write to 0x60; Table P0386 in PORTS.A) */
#define _KBD_KCMD_LEDS 0xED /* set LEDs */
#define _KBD_KCMD_SCSET 0xF0 /* get/set scancode set */
#define _KBD_KCMD_TYPEM 0xF3 /* set auto-repeat settings */
#define _KBD_KCMD_EN 0xF4 /* enable scanning */
#define _KBD_KCMD_DIS 0xF5 /* disable scanning */
#define _KBD_KCMD_TMB 0xFA /* all keys typematic/make-break */
#define _KBD_KCMD_RESET 0xFF /* full reset of 8048 + self-test */
/* bits for "Command Byte" (confusing name; Table P0404 in PORTS.A).
Bit names are from Linux source.
0x80 reserved */
#define _KBD_CB_KCC 0x40 /* do AT-to-XT scancode translation */
#define _KBD_CB_DMS 0x20 /* force mouse clock low */
/* 0x10 disable keyboard clock (?)
0x08 ignore keyboard lock switch (reserved?) */
#define _KBD_CB_SYS 0x04 /* system flag (?) */
/* 0x02 reserved (enable PS/2 mouse IRQ12?) */
#define _KBD_CB_EKI 0x01 /* enable IRQ1 for _KBD_STAT_OBF */
/* Linux also sets _KBD_CB_KCC here: */
#define _KBD_CB_INIT (_KBD_CB_DMS | _KBD_CB_SYS | _KBD_CB_EKI)
/* status bits read from 0x64 (Table P0398 in PORTS.A) */
#define _KBD_STAT_PERR 0x80 /* parity error in data from kbd */
#define _KBD_STAT_GTO 0x40 /* receive timeout */
/* 0x20 xmit timeout
0x10 keyboard locked
0x08
=1 data written to input register is command (PORT 0064h)
=0 data written to input register is data (PORT 0060h)
0x04
system flag status: 0=power up or reset 1=self-test OK */
#define _KBD_STAT_IBF 0x02 /* input buffer full (data for kbd) */
#define _KBD_STAT_OBF 0x01 /* output bffr full (data for host) */
/* #defines from here down can be brought out to a separate .h file,
for use by programs that talk to the keyboard */
/* "bucky bits" */
#define KBD_BUCKY_ALT 0x8000 /* Alt is pressed */
#define KBD_BUCKY_CTRL 0x4000 /* Ctrl is pressed */
#define KBD_BUCKY_SHIFT 0x2000 /* Shift is pressed */
#define KBD_BUCKY_ANY \
(KBD_BUCKY_ALT | KBD_BUCKY_CTRL | KBD_BUCKY_SHIFT)
#define KBD_BUCKY_CAPS 0x1000 /* CapsLock is on */
#define KBD_BUCKY_NUM 0x0800 /* NumLock is on */
#define KBD_BUCKY_SCRL 0x0400 /* ScrollLock is on */
/* "raw" set 3 scancodes */
#define RK_LEFT_CTRL 0x11
#define RK_LEFT_SHIFT 0x12
#define RK_CAPS_LOCK 0x14
#define RK_LEFT_ALT 0x19
#define RK_RIGHT_ALT 0x39
#define RK_RIGHT_CTRL 0x58
#define RK_RIGHT_SHIFT 0x59
#define RK_SCROLL_LOCK 0x5F
#define RK_NUM_LOCK 0x76
#define RK_END_1 0x69 /* End/1 on numeric keypad */
#define RK_BREAK_CODE 0xF0
/* "ASCII" values for non-ASCII keys. All of these are user-defined.
Function keys: */
#define K_F1 0x100
#define K_F2 (K_F1 + 1)
#define K_F3 (K_F2 + 1)
#define K_F4 (K_F3 + 1)
#define K_F5 (K_F4 + 1)
#define K_F6 (K_F5 + 1)
#define K_F7 (K_F6 + 1)
#define K_F8 (K_F7 + 1)
#define K_F9 (K_F8 + 1)
#define K_F10 (K_F9 + 1)
#define K_F11 (K_F10 + 1)
#define K_F12 (K_F11 + 1) /* 0x10B */
/* cursor keys */
#define K_INS (K_F12 + 1) /* 0x10C */
#define K_DEL (K_INS + 1)
#define K_HOME (K_DEL + 1)
#define K_END (K_HOME + 1)
#define K_PGUP (K_END + 1)
#define K_PGDN (K_PGUP + 1)
#define K_LFT (K_PGDN + 1)
#define K_UP (K_LFT + 1)
#define K_DN (K_UP + 1)
#define K_RT (K_DN + 1) /* 0x115 */
/* print screen/sys rq and pause/break */
#define K_PRNT (K_RT + 1) /* 0x116 */
#define K_PAUSE (K_PRNT + 1) /* 0x117 */
/* these return a value but they could also act as additional bucky keys */
#define K_LWIN (K_PAUSE + 1) /* 0x118 */
#define K_RWIN (K_LWIN + 1)
#define K_MENU (K_RWIN + 1) /* 0x11A */
#if defined(__TURBOC__) || !defined(__cplusplus)
typedef enum
{ false=0, true=1 } bool;
#endif
typedef unsigned char u8;
typedef unsigned short u16;
typedef struct /* circular queue */
{ bool NonEmpty;
u8 *Data;
u16 Size, Inptr, Outptr; } queue;
typedef struct /* virtual console! */
{ queue Keystrokes;
u16 KbdStatus; } console;
static console _Con;
/*****************************************************************************
name: inq
action: tries to add byte Data to queue Queue
returns:-1 queue full
0 success
*****************************************************************************/
int inq(queue *Queue, u8 Data)
{ u16 Temp;
Temp=Queue->Inptr + 1;
if(Temp >= Queue->Size)
Temp=0;
if(Temp == Queue->Outptr)
return(-1); /* full */
Queue->Data[Queue->Inptr]=Data;
Queue->Inptr=Temp;
Queue->NonEmpty=true;
return(0); }
/*****************************************************************************
name: deq
action: tries to get byte from Queue
returns:-1 queue empty
>=0 success (byte read)
*****************************************************************************/
int deq(queue *Queue)
{ u8 RetVal;
if(Queue->NonEmpty == false)
return(-1); /* empty */
RetVal=Queue->Data[Queue->Outptr++];
if(Queue->Outptr >= Queue->Size)
Queue->Outptr=0;
if(Queue->Outptr == Queue->Inptr)
Queue->NonEmpty=false;
return(RetVal); }
/*****************************************************************************
name: kbdRead
action: waits for a byte from the keyboard
returns:-1 if timeout
nonnegative byte if success
*****************************************************************************/
static int _kbdRead(void)
{ unsigned long Timeout;
u8 Stat, Data;
for(Timeout=500000L; Timeout != 0; Timeout--)
{ Stat=inportb(_KBD_STAT_REG);
/* loop until 8042 output buffer full */
if((Stat & _KBD_STAT_OBF) != 0)
{ Data=inportb(_KBD_DATA_REG);
/* loop if parity error or receive timeout */
if((Stat & (_KBD_STAT_PERR | _KBD_STAT_GTO)) == 0)
return(Data); }}
return(-1); }
/*****************************************************************************
name: kbdWrite
action: writes Data to 8048 keyboard MCU (Adr=_KBD_DATA_REG) or
8042 keyboard controller (Adr=_KBD_CMD_REG)
*****************************************************************************/
static void _kbdWrite(unsigned Adr, unsigned Data)
{ unsigned long Timeout;
u8 Stat;
/* Linux code didn't have a timeout here... */
for(Timeout=500000L; Timeout != 0; Timeout--)
{ Stat=inportb(_KBD_STAT_REG);
/* loop until 8042 input buffer empty */
if((Stat & _KBD_STAT_IBF) == 0)
break; }
if(Timeout != 0)
outportb(Adr, Data);
/* xxx - else? */ }
/*****************************************************************************
name: kbdWriteRead
action: writes value Val to keyboard port Adr. If Len is
non-zero, reads Len bytes from keyboard and compares
them to Reply
returns:0 if success
nonzero if error (returns value of failing reply byte)
*****************************************************************************/
int _kbdWriteRead(unsigned Adr, unsigned Val, unsigned Len, u8 *Reply)
{ int RetVal;
_kbdWrite(Adr, Val);
for(; Len != 0; Len--)
{ RetVal=_kbdRead();
if(*Reply != RetVal)
{ printf(" failed: expected 0x%02X, got 0x%02X\n",
*Reply, RetVal);
return(RetVal); }
Reply++; }
return(0); }
/*****************************************************************************
name: kbdInit
action: runs self-test on 8048 keyboard MCU and 8042 controller MCU,
resets keyboard, programs 8048 for ScanCodeSet,
programs 8042 for no XT-to-AT conversion,
makes all keys make/break and fast typematic
returns:0 if success
nonzero if error
*****************************************************************************/
int kbdInit(unsigned ScanCodeSet)
{ unsigned Flags;
int Result;
printf("keyboard:");
/* xxx - probe for keyboard controller before proceding?
I don't know what will happen if the controller is bad or missing...
disable interrupts */
Flags=critb();
/* flush pending input */
DEBUG(printf("\n flushing keyboard output");)
while(_kbdRead() != -1)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -