分类: LINUX
2010-05-14 13:48:40
#include
#include
#include
#include
#include
#include
/*使用第一套键盘扫描码表:A-1E;B-30;C-2E…*/
static unsigned char usb_kbd_keycode[256] = {
5 K1 ]8 I% v8 o0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
9 l$ @+ O4 f' u9 D8 V105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,
122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
! U& h2 }, O U1 d0 q: n- Z% h+ `29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
5 _$ X& }( G% {' j k1 [! l, P150,158,159,128,136,177,178,176,142,152,173,140
) C2 q0 z4 x9 M, Z. h& {+ Estatic struct usb_device_id usb_kbd_id_table [] = {
{ USB_INTERFACE_INFO(3, 1, 1) },/*3,1,1分别表示接口类,接口子类,接口协议;3,1,1为键盘接口类;鼠标为3,1,2*/
$ n* x- f$ j% a0 U{ } /* Terminating entry */
8 S# f9 C/ \! ^5 g};
struct usb_kbd {
struct input_dev *dev; /*定义一个输入设备*/
8 i: g! U& F6 z2 F6 cstruct usb_device *usbdev;/*定义一个usb设备*/
unsigned char old[8]; /*按键离开时所用之数据缓冲区*/
struct urb *irq/*usb键盘之中断请求块*/, *led/*usb键盘之指示灯请求块*/;
unsigned char newleds;/*目标指定灯状态*/
/ p' {# l$ d" k+ U! n, m8 P: j; @8 uchar name[128];/*存放厂商名字及产品名字*/
char phys[64];/*设备之节点*/
unsigned char *new;/*按键按下时所用之数据缓冲区*/
struct usb_ctrlrequest *cr;/*控制请求结构*/
0 p1 Q' B8 H3 v8 D9 {; c# V4 Z$ tunsigned char *leds;/*当前指示灯状态*/
dma_addr_t cr_dma; /*控制请求DMA缓冲地址*/
dma_addr_t new_dma; /*中断urb会使用该DMA缓冲区*/
dma_addr_t leds_dma; /*指示灯DAM缓冲地址*/
8 X8 @, G2 N9 I' i6 z/ w! v/*USB键盘驱动结构体*/
, C: l6 r. C& V3 r6 ustatic struct usb_driver usb_kbd_driver = {
.name = "usbkbd",/*驱动名字*/
.probe = usb_kbd_probe,/*驱动探测函数,加载时用到*/
1 A% |! S8 [$ N0 {, L! v4 Z! F.disconnect = usb_kbd_disconnect,/*驱动断开函数,在卸载时用到*/
! L4 E C/ O% `. t$ ?! o3 o% u. Y.id_table = usb_kbd_id_table,/*驱动设备ID表,用来指定设备或接口*/
' q* g' l! H2 {+ t3 \};
, Z, F" Y' v$ K b/*驱动程序生命周期的开始点,向 USB core 注册这个键盘驱动程序。*/
static int __init usb_kbd_init(void)
{
# A" x8 L; p0 B. v" y" Uint result = usb_register(&usb_kbd_driver);/*注册USB键盘驱动*/
if (result == 0) /*注册失败*/
info(DRIVER_VERSION ":" DRIVER_DESC);
7 X$ [2 l5 R3 A0 h: f! I" X( E6 ureturn result;
/* 驱动程序生命周期的结束点,向 USB core 注销这个键盘驱动程序。 */
static void __exit usb_kbd_exit(void)
% p1 Z! u. ~9 X2 `+ t6 T{
printk("SUNWILL-USBKBD:usb_kbd_exit begin...\n");
usb_deregister(&usb_kbd_driver);/*注销USB键盘驱动*/
* @; I% {* c( [9 @/*中断请求处理函数,有中断请求到达时调用该函数*/
1 T* B5 M6 i- @static void usb_kbd_irq(struct urb *urb, struct pt_regs *regs)
5 z9 Z- H& I# T3 c H" g{
struct usb_kbd *kbd = urb->context;
9 {/ ^- ^. i2 R4 Z$ b% ?int i;
: ]$ Y" W) H: I3 Y: M, e+ X. {0 |7 qswitch (urb->status) {
case 0: /* success */
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
" ^- t/ Q/ q! R: q- x [case -ESHUTDOWN:
return;
6 J& V# T" |" K! O$ ~/* -EPIPE: should clear the halt */
default: /* error */
goto resubmit;
' x. f0 |2 \/ Y" |" K r}
//input_regs(kbd->dev, regs);
( l9 K& G# K2 A; U; s) i, q. k/*不知道其用意, 注释掉该部分仍可正常工作*/
2 n7 m9 O6 B' k0 t' rfor (i = 0; i < 8; i++)/*8次的值依次是:29-42-56-125-97-54-100-126*/
# S Y) m! d+ `4 {! X# N2 m% [1 ~0 Z$ l( A{
input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);
, X' ~6 h( y$ l7 h}
/*若同时只按下1个按键则在第[2]个字节,若同时有两个按键则第二个在第[3]字节,类推最多可有6个按键同时按下*/
+ S9 ?5 }+ W" X7 A5 }for (i = 2; i < 8; i++) {
; l0 c, V& M6 n7 \/*获取键盘离开的中断*/
if (kbd->old > 3 && memscan(kbd->new + 2, kbd->old, 6) == kbd->new + 8) {/*同时没有该KEY的按下状态*/
2 F3 e3 g8 X1 x% H7 iif (usb_kbd_keycode[kbd->old])
) a, d$ X: V4 F! ] k. t9 j4 P( V{
input_report_key(kbd->dev, usb_kbd_keycode[kbd->old], 0);
/ }9 Y) _4 b, _* C1 s* S/ g C, e4 d}
else
info("Unknown key (scancode %#x) released.", kbd->old);
. s9 R$ x5 N' q}
( L) k' C. C. C7 C4 P/ R/*获取键盘按下的中断*/
/ c" I1 m1 S6 X0 }! a1 {% {if (kbd->new > 3 && memscan(kbd->old + 2, kbd->new, 6) == kbd->old + 8) {/*同时没有该KEY的离开状态*/
if (usb_kbd_keycode[kbd->new])
; `/ S/ c5 h% h, T{
' T y8 p/ p3 H) g' N1 p, Binput_report_key(kbd->dev, usb_kbd_keycode[kbd->new], 1);
- }. S, u" U$ v3 ~}
2 \/ B( ~4 _6 |; l. X4 _else
6 s2 a$ A5 ?, J' b7 y# tinfo("Unknown key (scancode %#x) pressed.", kbd->new);
}
}
/*同步设备,告知事件的接收者驱动已经发出了一个完整的报告*/
& R% M$ i8 c& W) winput_sync(kbd->dev);
memcpy(kbd->old, kbd->new, 8);/*防止未松开时被当成新的按键处理*/
6 V$ l/ N0 r9 i1 Sresubmit:
i = usb_submit_urb (urb, GFP_ATOMIC);/*发送USB请求块*/
if (i)
# F6 T# }' A+ C' v' ^err ("can't resubmit intr, %s-%s/input0, status %d",
. Q" r- c% t- z0 ~2 S$ ukbd->usbdev->bus->bus_name,
& M4 r1 H# Z/ a, u$ lkbd->usbdev->devpath, i);
/*事件处理函数*/
3 [9 w, B6 ]8 D5 t; R* |static int usb_kbd_event(struct input_dev *dev, unsigned int type,
4 @3 K) J; |. kunsigned int code, int value)
# r8 N6 a. h0 K; M: n) J/ K' e{
struct usb_kbd *kbd = dev->private;
if (type != EV_LED) /*不支持LED事件 */
return -1;
/*获取指示灯的目标状态*/
kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |
(!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) |
(!!test_bit(LED_NUML, dev->led));
if (kbd->led->status == -EINPROGRESS)
return 0;
/*指示灯状态已经是目标状态则不需要再做任何操作*/
0 d; ~- m: t$ F, l5 Y5 P1 ]if (*(kbd->leds) == kbd->newleds)
, J" U C+ o9 ^1 \return 0;
/ r; I2 R/ | G2 Y*(kbd->leds) = kbd->newleds;
kbd->led->dev = kbd->usbdev;
2 V( K; p- {9 _* C/ R/*发送usb请求块*/
if (usb_submit_urb(kbd->led, GFP_ATOMIC))
6 X. `: n, p$ Herr("usb_submit_urb(leds) failed");
, F# N( c& y2 V; Areturn 0;
: Z2 K0 Q' ^" ]' S: {/*接在event之后操作,该功能其实usb_kbd_event中已经有了,该函数的作用可能是防止event的操作失败,一般注释掉该函数中的所有行都可以正常工作*/
static void usb_kbd_led(struct urb *urb, struct pt_regs *regs)
- @8 [0 V" Z8 }7 b& S2 `! c) S{
struct usb_kbd *kbd = urb->context;
: P9 P6 Y3 `. f" Mif (urb->status)
warn("led urb status %d received", urb->status);
' Q: l8 [6 E! n" \: L: M; U3 w, qif (*(kbd->leds) == kbd->newleds)/*指示灯状态已经是目标状态则不需要再做任何操作*/
return;
*(kbd->leds) = kbd->newleds;
( M$ p; s1 _$ f: b0 S* Tkbd->led->dev = kbd->usbdev;
if (usb_submit_urb(kbd->led, GFP_ATOMIC))
# Y0 z+ p1 T1 r' merr("usb_submit_urb(leds) failed");
/*打开键盘设备时,开始提交在 probe 函数中构建的 urb,进入 urb 周期。 */
0 o* G; B9 U- F- D7 {- rstatic int usb_kbd_open(struct input_dev *dev)
( X' B5 S) F; k- {{
0 F7 ^% g8 C: [# w6 Estruct usb_kbd *kbd = dev->private;
kbd->irq->dev = kbd->usbdev;
if (usb_submit_urb(kbd->irq, GFP_KERNEL))
return -EIO;
0 [& d( }( [% N/ Kreturn 0;
/*关闭键盘设备时,结束 urb 生命周期。 */
+ W/ T0 E+ j- J, e! Jstatic void usb_kbd_close(struct input_dev *dev)
{
( F' Z/ q( h; `/ z0 f0 Q1 _; estruct usb_kbd *kbd = dev->private;
0 @( m1 I9 ]+ p* T7 |usb_kill_urb(kbd->irq); /*取消kbd->irq这个usb请求块*/
/*分配URB内存空间即创建URB*/
. D5 ~8 t6 g: jstatic int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)
{
if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL)))
return -1;
if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL)))
return -1;
: U5 y; v$ z9 G6 L$ `$ nif (!(kbd->new = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &kbd->new_dma)))
& u) L! ~" E0 t, y) k* Greturn -1;
# I" m* h0 n/ Xif (!(kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), GFP_ATOMIC, &kbd->cr_dma)))
9 R1 {# D4 N5 H, Y5 N' Q+ P# V8 H6 ~return -1;
if (!(kbd->leds = usb_buffer_alloc(dev, 1, GFP_ATOMIC, &kbd->leds_dma)))
return -1;
return 0;
/*释放URB内存空间即销毁URB*/
8 b; X3 r9 ~/ }3 jstatic void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd)
{
/ _: t2 ~1 d6 A9 Sif (kbd->irq)
usb_free_urb(kbd->irq);
if (kbd->led)
usb_free_urb(kbd->led);
if (kbd->new)
1 H/ f4 b: b0 y3 k% e9 Lusb_buffer_free(dev, 8, kbd->new, kbd->new_dma);
/ @; f$ W4 z8 A4 v0 [" V+ sif (kbd->cr)
usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma);
6 {& E3 v* q. V2 v* y# uif (kbd->leds)
usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma);
& l2 r% j7 Z6 `" p/ H}
/*USB键盘驱动探测函数,初始化设备并指定一些处理函数的地址*/
static int usb_kbd_probe(struct usb_interface *iface,
const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(iface);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
. ~! k+ q3 h4 G' J. p/ I! s sstruct usb_kbd *kbd;
' W- S2 g" l7 r% Y9 I4 j* lstruct input_dev *input_dev;
1 O$ O# ^; S; ~. Eint i, pipe, maxp;
1 u0 j2 [) f+ @& L/*当前选择的interface*/
interface = iface->cur_altsetting;
, E0 C) A: W- i2 h: Z- K. {8 o/*键盘只有一个中断IN端点*/
. H7 ]3 D U; c7 ^3 f) Eif (interface->desc.bNumEndpoints != 1)
return -ENODEV;
; F% V6 `. @- [- i* S/ h# W+ y/ f/*获取端点描述符*/
; f H$ Q5 q. d) j* r$ cendpoint = &interface->endpoint[0].desc;
v0 Y- S* C+ ?+ B7 \4 H% x* {if (!(endpoint->bEndpointAddress & USB_DIR_IN))
return -ENODEV;
. `: a$ m/ s# {2 R; v+ Vif ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT)
return -ENODEV;
3 |5 t" B8 N/ E/*将endpoint设置为中断IN端点*/
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
/*获取包的最大值*/
! t4 D) w2 A" f3 e' Wmaxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
2 A& t8 l1 m6 y( N: t1 @6 f/ Ikbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);
input_dev = input_allocate_device();
if (!kbd || !input_dev)
n A( g6 h d3 ^3 @, o2 z. [# Y- fgoto fail1;
4 f3 P8 p9 u4 i" i( _if (usb_kbd_alloc_mem(dev, kbd))
goto fail2;
2 I5 k4 X. V' ~; S, X7 @/* 填充 usb 设备结构体和输入设备结构体 */
kbd->usbdev = dev;
kbd->dev = input_dev;
/*以"厂商名字 产品名字"的格式将其写入kbd->name*/
if (dev->manufacturer)
+ y/ a6 z( T# y- L7 H8 Bstrlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));
8 P4 ^( k3 Q/ |( }9 n! cif (dev->product) {
' S; _( i4 m; fif (dev->manufacturer)
strlcat(kbd->name, " ", sizeof(kbd->name));
strlcat(kbd->name, dev->product, sizeof(kbd->name));
}
/*检测不到厂商名字*/
+ j# S3 x0 d: i- Wif (!strlen(kbd->name))
v5 e+ m6 v7 h5 U$ b2 r, Q" Isnprintf(kbd->name, sizeof(kbd->name),
"USB HIDBP Keyboard %04x:%04x",
\+ q% m+ R3 E& d9 X2 [le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
7 J! A- D& w' k0 b3 R' b& O1 P/*设备链接地址*/
; U: ^, j1 j! |) T4 g, P1 R/ N+ d! Eusb_make_path(dev, kbd->phys, sizeof(kbd->phys));
strlcpy(kbd->phys, "/input0", sizeof(kbd->phys));
1 ]8 D# W1 Q0 u0 k# iinput_dev->name = kbd->name;
, r) \ N- r* D2 b0 V; [input_dev->phys = kbd->phys;
2 O* k) H* w8 o6 g. |2 ^0 q/*
! k% }9 U+ w$ x7 ~6 @* input_dev 中的 input_id 结构体,用来存储厂商、设备类型和设备的编号,这个函数是将设备描述符
* 中的编号赋给内嵌的输入子系统结构体
! {6 c- V3 y) p" S5 N*/
: {% W( H# H' r1 E$ \* L/ \' A2 zusb_to_input_id(dev, &input_dev->id);
3 [, a# {7 w4 v6 j5 h, E/* cdev 是设备所属类别(class device) */
. q, y" t. p+ ?0 Yinput_dev->cdev.dev = &iface->dev;
1 h! n0 q% ^6 x% m0 ^/* input_dev 的 private 数据项用于表示当前输入设备的种类,这里将键盘结构体对象赋给它 */
input_dev->private = kbd;
* Q* X# |# s4 n( W2 |input_dev->evbit[0] = BIT(EV_KEY)/*键码事件*/ | BIT(EV_LED)/*LED事件*/ | BIT(EV_REP)/*自动重覆数值*/;
input_dev->ledbit[0] = BIT(LED_NUML)/*数字灯*/ | BIT(LED_CAPSL)/*大小写灯*/ | BIT(LED_SCROLLL)/*滚动灯*/ | BIT(LED_COMPOSE) | BIT(LED_KANA);
# ]+ D' g- O; S' A' W$ u7 pfor (i = 0; i < 255; i++)
# d" N0 G5 B; n% H" P7 E. O' M5 oset_bit(usb_kbd_keycode, input_dev->keybit);
clear_bit(0, input_dev->keybit);
- X; L( q7 Q. c! Winput_dev->event = usb_kbd_event;/*注册事件处理函数入口*/
input_dev->open = usb_kbd_open;/*注册设备打开函数入口*/
) `4 c- M3 t+ q3 r' x' B: Dinput_dev->close = usb_kbd_close;/*注册设备关闭函数入口*/
2 }. l" B( I) Q9 m+ I, n: G/*初始化中断URB*/
) F- R% x( u( ]2 iusb_fill_int_urb(kbd->irq/*初始化kbd->irq这个urb*/, dev/*这个urb要发送到dev这个设备*/, pipe/*这个urb要发送到pipe这个端点*/,
kbd->new/*指向缓冲的指针*/, (maxp > 8 ? 8 : maxp)/*缓冲长度*/,
?! t2 g* U) T7 j6 Dusb_kbd_irq/*这个urb完成时调用的处理函数*/, kbd/*指向数据块的指针,被添加到这个urb结构可被完成处理函数获取*/, endpoint->bInterval/*urb应当被调度的间隔*/);
3 q/ G# Q+ I7 k2 rkbd->irq->transfer_dma = kbd->new_dma; /*指定urb需要传输的DMA缓冲区*/
4 ~0 D) y' K* ^, nkbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;/*本urb有一个DMA缓冲区需要传输*/
1 d+ ]/ |: U1 x$ b. G7 Hkbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;/*操作的是类接口对象*/
( |4 A: X3 @9 o* o# D- v1 Y0 skbd->cr->bRequest = 0x09; /*中断请求编号*/
kbd->cr->wValue = cpu_to_le16(0x200);
kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);/*接口号*/
6 V( k0 o2 Q4 }$ Mkbd->cr->wLength = cpu_to_le16(1);/*数据传输阶段传输多少个bytes*/
/*初始化控制URB*/
usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),
(void *) kbd->cr, kbd->leds, 1,
usb_kbd_led, kbd);
kbd->led->setup_dma = kbd->cr_dma;
$ d9 \( r3 \& o6 jkbd->led->transfer_dma = kbd->leds_dma;
8 [/ v: p" l! i5 N3 P8 {/ Hkbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP/*如果使用DMA传输则urb中setup_dma指针所指向的缓冲区是DMA缓冲区而不是setup_packet所指向的缓冲区*/);
( ?9 \4 P: i. `/*注册输入设备*/
input_register_device(kbd->dev);
usb_set_intfdata(iface, kbd);/*设置接口私有数据*/
return 0;
' t: s8 V4 O4 Z1 X% ufail2: usb_kbd_free_mem(dev, kbd);
fail1: input_free_device(input_dev);
) ~% P0 \, T, c8 j; y3 Hkfree(kbd);
return -ENOMEM;
/*断开连接(如键盘设备拔出)的处理函数*/
2 V- F6 ?+ e1 B- ^, Ostatic void usb_kbd_disconnect(struct usb_interface *intf)
. {$ Q* n: Q2 A6 d{
~1 e. ?7 p4 k9 b& _# Istruct usb_kbd *kbd = usb_get_intfdata (intf);/*获取接口的私有数据给kbd*/
+ X: b A: E% @: w6 S# z2 Tusb_set_intfdata(intf, NULL);/*设置接口的私有数据为NULL*/
if (kbd) {
usb_kill_urb(kbd->irq);/*取消中断请求*/
input_unregister_device(kbd->dev);/*注销设备*/
. o; ^/ X9 G: r* C- X! eusb_kbd_free_mem(interface_to_usbdev(intf), kbd);/*释放内存空间*/
% o# D- J8 m* tkfree(kbd);
}
##############################
) B- T/ q/ m( E* | e#usbkdb Makefile for linux
/ V2 W, _$ q/ Z# b& e$ F6 X##############################
; i% d5 j0 M7 k W9 [; Qobj-m:=usbkbd.o
7 l; F" C1 p# H: O) N* o" _1 VKERNELDIR ?= /lib/modules/$(shell uname -r)/build
6 w4 h* }) I, }4 Y2 P3 o. F3 APWD:=$(shell pwd)
default: