分享

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

 昵称1542620 2010-05-29
[转贴] 编写Linux下的USB键盘驱(一)
1.   指定USB键盘驱动所需的头文件: 

#include <linux/kernel.h>/*内核头文件,含有内核一些常用函数的原型定义*/

- b  ^$ i! [0 ^! F+ j9 G

#include <linux/slab.h>/*定义内存分配的一些函数*/


#include <linux/module.h>/*模块编译必须的头文件*/


#include <linux/input.h>/*输入设备相关函数的头文件*/

5 i4 ]& H; U3 a2 u' ?* A% {2 M

#include <linux/init.h>/*linux初始化模块函数定义*/


#include <linux/usb.h> /*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:

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多