[转贴] 编写Linux下的USB键盘驱(一) -

来源:百度文库 编辑:神马文学网 时间:2024/07/04 22:46:41
[转贴] 编写Linux下的USB键盘驱(一) 1.   指定USB键盘驱动所需的头文件:
#include /*内核头文件,含有内核一些常用函数的原型定义*/
- b  ^$ i! [0 ^! F+ j9 G
#include /*定义内存分配的一些函数*/
#include /*模块编译必须的头文件*/
#include /*输入设备相关函数的头文件*/
5 i4 ]& H; U3 a2 u' ?* A% {2 M
#include /*linux初始化模块函数定义*/
#include  /*USB设备相关函数定义*/
# A- B3 ^7 l  ~: b) E6 T
2.   定义键盘码表数组:
/*使用第一套键盘扫描码表:A-1E;B-30;C-2E…*/
static unsigned char usb_kbd_keycode[256] = {
5 K1 ]8 I% v8 o
0, 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 V
105,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, P
150,158,159,128,136,177,178,176,142,152,173,140
) C2 q0 z4 x9 M, Z. h& {+ E
}; 5 E+ q5 ?$ d8 G& s
3.   编写设备ID表:   N$ p" o: c( a+ n6 N; c- j6 L
static 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
};
MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);/*指定设备ID表*/ 3 n- {* {; E6 _3 O& q# s
4.   定义USB键盘结构体:
struct usb_kbd {
struct input_dev *dev; /*定义一个输入设备*/
8 i: g! U& F6 z2 F6 c
struct 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 u
char name[128];/*存放厂商名字及产品名字*/
char phys[64];/*设备之节点*/
unsigned char *new;/*按键按下时所用之数据缓冲区*/
struct usb_ctrlrequest *cr;/*控制请求结构*/
0 p1 Q' B8 H3 v8 D9 {; c# V4 Z$ t
unsigned 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
};
5.   编写USB键盘驱动结构(任何一个LINUX下的驱动都有个类似的驱动结构):
/*USB键盘驱动结构体*/
, C: l6 r. C& V3 r6 u
static 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
6.   编写模块加载函数(每个驱动都会有一个加载函数,由module_init调用):
/*驱动程序生命周期的开始点,向 USB core 注册这个键盘驱动程序。*/
static int __init usb_kbd_init(void)
{
# A" x8 L; p0 B. v" y" U
int 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 u
return result;
}
7.   编写模块卸载函数(每个驱动都会有一个卸载函数,由module_exit调用): , t" e1 X" f3 b) A
/* 驱动程序生命周期的结束点,向 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 @
} 2 l! F  j5 t& `; \  ?
8.   指定模块初始化函数(被指定的函数在insmod驱动时调用):  b2 |. i4 J+ C3 A2 `% N
: e+ ^" z8 n) [+ P& }
module_init(usb_kbd_init);
9.   指定模块退出函数(被指定的函数在rmmod驱动时调用):
module_exit(usb_kbd_exit);
10.   编写中断请求处理函数: ) i% U0 o, F: x1 g: o. v4 c
4 m4 i5 P% d. U: v
/*中断请求处理函数,有中断请求到达时调用该函数*/
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 q
switch (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
C* P5 E/ M. z) a! W3 a: C; u5 p5 ~! O9 M
/*不知道其用意, 注释掉该部分仍可正常工作*/
2 n7 m9 O6 B' k0 t' r
for (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 i
if (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, B
input_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# t
info("Unknown key (scancode %#x) pressed.", kbd->new);
}
}
/*同步设备,告知事件的接收者驱动已经发出了一个完整的报告*/
& R% M$ i8 c& W) w
input_sync(kbd->dev);
memcpy(kbd->old, kbd->new, 8);/*防止未松开时被当成新的按键处理*/
6 V$ l/ N0 r9 i1 S
resubmit:
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$ u
kbd->usbdev->bus->bus_name,
& M4 r1 H# Z/ a, u$ l
kbd->usbdev->devpath, i);
} ) M" s  T9 C8 z% Q
11.   编写事件处理函数:
/*事件处理函数*/
3 [9 w, B6 ]8 D5 t; R* |
static int usb_kbd_event(struct input_dev *dev, unsigned int type,
4 @3 K) J; |. k
unsigned 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));
# z- U5 G# d$ P+ t: v
if (kbd->led->status == -EINPROGRESS)
return 0;
7 b. w4 f3 o" g
/*指示灯状态已经是目标状态则不需要再做任何操作*/
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$ H
err("usb_submit_urb(leds) failed");
, F# N( c& y2 V; A
return 0;
: Z2 K0 Q' ^" ]' S: {
}
12.   编写LED事件处理函数:
/*接在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" M
if (urb->status)
warn("led urb status %d received", urb->status);
' Q: l8 [6 E! n" \: L: M; U3 w, q
if (*(kbd->leds) == kbd->newleds)/*指示灯状态已经是目标状态则不需要再做任何操作*/
return;
c& ]6 l' @5 y9 y( t, I
*(kbd->leds) = kbd->newleds;
( M$ p; s1 _$ f: b0 S* T
kbd->led->dev = kbd->usbdev;
if (usb_submit_urb(kbd->led, GFP_ATOMIC))
# Y0 z+ p1 T1 r' m
err("usb_submit_urb(leds) failed");
}
13.   编写USB设备打开函数: % L8 _# ~+ Q; h8 V! S& j
/*打开键盘设备时,开始提交在 probe 函数中构建的 urb,进入 urb 周期。 */
0 o* G; B9 U- F- D7 {- r
static int usb_kbd_open(struct input_dev *dev)
( X' B5 S) F; k- {
{
0 F7 ^% g8 C: [# w6 E
struct usb_kbd *kbd = dev->private;
kbd->irq->dev = kbd->usbdev;
if (usb_submit_urb(kbd->irq, GFP_KERNEL))
return -EIO;
0 [& d( }( [% N/ K
return 0;
} , [8 \/ P' x9 _& |* ]0 W
14.   编写USB设备关闭函数
/*关闭键盘设备时,结束 urb 生命周期。 */
+ W/ T0 E+ j- J, e! J
static void usb_kbd_close(struct input_dev *dev)
{
( F' Z/ q( h; `/ z0 f0 Q1 _; e
struct usb_kbd *kbd = dev->private;
0 @( m1 I9 ]+ p* T7 |
usb_kill_urb(kbd->irq); /*取消kbd->irq这个usb请求块*/
} 0 @* c% M% y; R; N; W- p
15.   创建URB * }: _6 i# P+ |/ f) e
/*分配URB内存空间即创建URB*/
. D5 ~8 t6 g: j
static 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$ `$ n
if (!(kbd->new = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &kbd->new_dma)))
& u) L! ~" E0 t, y) k* G
return -1;
# I" m* h0 n/ X
if (!(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;
} & a. r$ G4 ]4 O8 e
16.   销毁URB . i# s- u# u$ G8 \
/*释放URB内存空间即销毁URB*/
8 b; X3 r9 ~/ }3 j
static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd)
{
/ _: t2 ~1 d6 A9 S
if (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 L
usb_buffer_free(dev, 8, kbd->new, kbd->new_dma);
/ @; f$ W4 z8 A4 v0 [" V+ s
if (kbd->cr)
usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma);
6 {& E3 v* q. V2 v* y# u
if (kbd->leds)
usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma);
& l2 r% j7 Z6 `" p/ H
}
17.   USB键盘驱动探测函数: 9 {3 `6 X& N+ I, \6 G3 C5 y
/*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  s
struct usb_kbd *kbd;
' W- S2 g" l7 r% Y9 I4 j* l
struct input_dev *input_dev;
1 O$ O# ^; S; ~. E
int 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) E
if (interface->desc.bNumEndpoints != 1)
return -ENODEV;
; F% V6 `. @- [- i* S/ h# W+ y/ f
/*获取端点描述符*/
; f  H$ Q5 q. d) j* r$ c
endpoint = &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+ V
if ((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' W
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
2 A& t8 l1 m6 y( N: t1 @6 f/ I
kbd = 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- f
goto 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 B
strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));
8 P4 ^( k3 Q/ |( }9 n! c
8 u" f& f+ \3 x/ p8 r+ d
if (dev->product) {
' S; _( i4 m; f
if (dev->manufacturer)
strlcat(kbd->name, " ", sizeof(kbd->name));
strlcat(kbd->name, dev->product, sizeof(kbd->name));
}
/*检测不到厂商名字*/
+ j# S3 x0 d: i- W
if (!strlen(kbd->name))
v5 e+ m6 v7 h5 U$ b2 r, Q" I
snprintf(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! E
usb_make_path(dev, kbd->phys, sizeof(kbd->phys));
strlcpy(kbd->phys, "/input0", sizeof(kbd->phys));
1 ]8 D# W1 Q0 u0 k# i
input_dev->name = kbd->name;
, r) \  N- r* D2 b0 V; [
; I2 w; @( \* @, ]
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 z
usb_to_input_id(dev, &input_dev->id);
3 [, a# {7 w4 v6 j5 h, E
/* cdev 是设备所属类别(class device) */
. q, y" t. p+ ?0 Y
input_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 p
for (i = 0; i < 255; i++)
# d" N0 G5 B; n% H" P7 E. O' M5 o
set_bit(usb_kbd_keycode, input_dev->keybit);
clear_bit(0, input_dev->keybit);
- X; L( q7 Q. c! W
- ]/ ?0 y' C7 d9 B: Z" w
input_dev->event = usb_kbd_event;/*注册事件处理函数入口*/
input_dev->open = usb_kbd_open;/*注册设备打开函数入口*/
) `4 c- M3 t+ q3 r' x' B: D
input_dev->close = usb_kbd_close;/*注册设备关闭函数入口*/
2 }. l" B( I) Q9 m+ I, n: G
/*初始化中断URB*/
) F- R% x( u( ]2 i
usb_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 D
usb_kbd_irq/*这个urb完成时调用的处理函数*/, kbd/*指向数据块的指针,被添加到这个urb结构可被完成处理函数获取*/, endpoint->bInterval/*urb应当被调度的间隔*/);
3 q/ G# Q+ I7 k2 r
kbd->irq->transfer_dma = kbd->new_dma; /*指定urb需要传输的DMA缓冲区*/
4 ~0 D) y' K* ^, n
kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;/*本urb有一个DMA缓冲区需要传输*/
1 d+ ]/ |: U1 x$ b. G7 H
kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;/*操作的是类接口对象*/
( |4 A: X3 @9 o* o# D- v1 Y0 s
kbd->cr->bRequest = 0x09; /*中断请求编号*/
kbd->cr->wValue = cpu_to_le16(0x200);
kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);/*接口号*/
6 V( k0 o2 Q4 }$ M
kbd->cr->wLength = cpu_to_le16(1);/*数据传输阶段传输多少个bytes*/
5 b6 c0 J$ c3 X; t
/*初始化控制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 j
kbd->led->transfer_dma = kbd->leds_dma;
8 [/ v: p" l! i5 N3 P8 {/ H
kbd->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);
) Y" y6 u4 K1 A0 v4 M
usb_set_intfdata(iface, kbd);/*设置接口私有数据*/
return 0;
' t: s8 V4 O4 Z1 X% u
fail2:   usb_kbd_free_mem(dev, kbd);
fail1:   input_free_device(input_dev);
) ~% P0 \, T, c8 j; y3 H
kfree(kbd);
return -ENOMEM;
} 6 N' ^+ s- f% }8 D
18.   编写断开连接的函数: 7 L: s) y1 n+ s: g: K
/*断开连接(如键盘设备拔出)的处理函数*/
2 V- F6 ?+ e1 B- ^, O
static void usb_kbd_disconnect(struct usb_interface *intf)
. {$ Q* n: Q2 A6 d
{
~1 e. ?7 p4 k9 b& _# I
struct usb_kbd *kbd = usb_get_intfdata (intf);/*获取接口的私有数据给kbd*/
+ X: b  A: E% @: w6 S# z2 T
usb_set_intfdata(intf, NULL);/*设置接口的私有数据为NULL*/
if (kbd) {
usb_kill_urb(kbd->irq);/*取消中断请求*/
input_unregister_device(kbd->dev);/*注销设备*/
. o; ^/ X9 G: r* C- X! e
usb_kbd_free_mem(interface_to_usbdev(intf), kbd);/*释放内存空间*/
% o# D- J8 m* t
kfree(kbd);
}
} $ N8 e, G, z3 ~$ c4 L
19.   编写Makefile:
##############################
) B- T/ q/ m( E* |  e
#usbkdb Makefile for linux
/ V2 W, _$ q/ Z# b& e$ F6 X
##############################
; i% d5 j0 M7 k  W9 [; Q
obj-m:=usbkbd.o
7 l; F" C1 p# H: O) N* o" _1 V
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
6 w4 h* }) I, }4 Y2 P3 o. F3 A
PWD:=$(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules 4 o7 g9 m1 B) {3 r6 p: