编写Linux下的USB键盘驱动(附源码)

摘自:http://tech.techweb.com.cn/thread-383835-1-1.html

 

1.   指定USB键盘驱动所需的头文件:
1 t! q5 f( V4 ~9 V
3 [/ M0 P8 J/ `. d" r6 o) Y

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

0 H( C2 p, Z; q7 D( b% m

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

$ {6 ?1 q6 h! m: q

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

9 U: ]9 l3 O, u4 o: ^+ s2 y& I# B

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


8 b3 Z- |8 X  g& ^6 ?

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


7 H6 w! g: K# H/ d" H1 O7 g

#include <linux/usb.h> /*USB设备相关函数定义*/

" [% s# B4 @) q" i
2.   定义键盘码表数组: 8 ?7 T4 C* H9 P9 R( m$ ]

/*使用第一套键盘扫描码表:A-1E;B-30;C-2E*/


  i6 b2 H: [) w

static unsigned char usb_kbd_keycode[256] = {


1 e4 {8 s1 G+ j# R( X

    0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,

: x' j* x: G4 H- j9 K

    50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,

$ s$ s7 _- X/ k$ Y1 A0 s/ n

    4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,

4 p3 K' e( Z" C2 o  p

    27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,


% c- G; a8 ]9 Y1 K! f3 Z8 U/ q1 G# W

    65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,

' a$ [: i1 u2 v

    105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,

0 e$ z0 A- M  L; }

    72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,

4 \( o- M- E8 w8 D- @

    191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,


, E$ ?. v4 I7 `. b1 B- ?/ j

    115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,

$ O! N# i7 }, ^& L' z/ X# q

    122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

  A# G' I$ p# Y

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


7 `' O! Z' w7 j, l3 V& T  e

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

3 b3 z0 N$ n: c* [

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

1 K( s6 |' m& m  I; I$ A

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

) Y2 y& n1 X" W0 t% r8 v+ D5 y

    29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,


- Z. a' a7 M* j8 c3 T* K

    150,158,159,128,136,177,178,176,142,152,173,140


' ~, p4 P& q7 v# M; M3 ^}; 1 T7 }/ r7 W) n2 G' p2 O/ e4 ]. P
3.   编写设备ID表:
7 `! w" l) h! S  d# V% n

static struct usb_device_id usb_kbd_id_table [] = {


( ]; E9 e- t9 _" D) x

    { USB_INTERFACE_INFO(3, 1, 1) },/*3,1,1分别表示接口类,接口子类,接口协议;3,1,1为键盘接口类;鼠标为3,1,2*/


4 X2 H# ~* y# {$ e9 k

    { }           /* Terminating entry */

4 Y# J2 T: u6 W; w

};

" `% S( t5 v5 f+ R* e/ _! n' A
MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);/*指定设备ID表*/ 9 z: C% X: Q5 e/ Y' f
4.   定义USB键盘结构体: 9 ?1 b, {, u* |& [% X

struct usb_kbd {


, b0 h5 `0 ]4 P8 j6 P) G

    struct input_dev *dev; /*定义一个输入设备*/

$ m. o/ R" g0 F9 L6 w# _

    struct usb_device *usbdev;/*定义一个usb设备*/

: B: U; y  S3 u1 A4 m+ f6 O5 `

    unsigned char old[8]; /*按键离开时所用之数据缓冲区*/


# W; g6 M3 }6 c$ X

    struct urb *irq/*usb键盘之中断请求块*/, *led/*usb键盘之指示灯请求块*/;

, {0 ?7 s( u3 _0 x3 l% F7 L

    unsigned char newleds;/*目标指定灯状态*/

5 a& g) A. }6 B# T0 F; f0 i

    char name[128];/*存放厂商名字及产品名字*/

) R/ e9 L1 j" Y

    char phys[64];/*设备之节点*/


! o* S) z) J% `( g
1 ~3 y/ M- f$ H- F

    unsigned char *new;/*按键按下时所用之数据缓冲区*/


: [  a2 L3 i# D- ]4 j& U9 o

    struct usb_ctrlrequest *cr;/*控制请求结构*/

2 t8 h" N# d! j* B* A4 t) T

    unsigned char *leds;/*当前指示灯状态*/


. J' \1 i) l* A; D+ w

    dma_addr_t cr_dma; /*控制请求DMA缓冲地址*/

  _; N1 k4 A5 \/ I

    dma_addr_t new_dma; /*中断urb会使用该DMA缓冲区*/


. R+ o8 E7 K# G0 g* {$ H

    dma_addr_t leds_dma; /*指示灯DAM缓冲地址*/

. O, z1 l! h5 v5 W
};
& `0 p. |% N2 Z4 m9 ~+ x$ w* C& |. O5.   编写USB键盘驱动结构(任何一个LINUX下的驱动都有个类似的驱动结构)
# o. c- A# w9 C. H1 r9 u% L

/*USB键盘驱动结构体*/

' ]  X; K( L- d0 E- V

static struct usb_driver usb_kbd_driver = {


) \7 j0 T+ `' S( i

    .name =   "usbkbd",/*驱动名字*/


: D0 d  E4 k( G; H0 d; A

    .probe = usb_kbd_probe,/*驱动探测函数,加载时用到*/


9 A2 V" I( ~5 t& m: W" E

    .disconnect = usb_kbd_disconnect,/*驱动断开函数,在卸载时用到*/


( T* ?5 ^3 R( Y2 R& Q& r$ n: {/ Q8 o6 e

    .id_table =   usb_kbd_id_table,/*驱动设备ID表,用来指定设备或接口*/


# w# k  z% u" J, o

};

# L/ |! C7 b1 C- z% c$ `4 E# r' s
6.   编写模块加载函数(每个驱动都会有一个加载函数,由module_init调用)
7 J5 t& s* {0 ~1 m

/*驱动程序生命周期的开始点,向 USB core 注册这个键盘驱动程序。*/


% i" R, t: \& X% J: O0 m5 Q

static int __init usb_kbd_init(void)

- K( W* G" V6 S1 g8 ]

{

" \3 h/ y  x+ }; F0 D0 ]* Z: e

    int result = usb_register(&usb_kbd_driver);/*注册USB键盘驱动*/


6 F% }2 c( g8 h

    if (result == 0) /*注册失败*/

2 U% c) c  e0 O; M) p% z- S% {; j

    info(DRIVER_VERSION ":" DRIVER_DESC);

  ^) \1 i$ v8 S5 ]* |/ v8 L4 I

    return result;


5 N+ }, j! q/ `( l: o  a}
* j) z0 {" I- _( ~7.   编写模块卸载函数(每个驱动都会有一个卸载函数,由module_exit调用)
4 U& Z$ A' ~& u8 i

/* 驱动程序生命周期的结束点,向 USB core 注销这个键盘驱动程序。 */

, a1 V2 i4 K& ?$ D' S' p

static void __exit usb_kbd_exit(void)

% d4 i  R+ c* B( m1 m* M3 n

{

; g: e4 c* M: F

    printk("SUNWILL-USBKBD:usb_kbd_exit begin...\n");

) {% g5 j, z8 l/ P6 ^% V5 d& T

    usb_deregister(&usb_kbd_driver);/*注销USB键盘驱动*/


7 h, I% K* i% b1 j}
2 b' e; L( a! z7 s4 A9 T4 ?8.   指定模块初始化函数(被指定的函数在insmod驱动时调用)( k* @0 A2 f+ ]4 [! g
9 U0 `! `9 |! S
module_init(usb_kbd_init); " C0 S0 s/ C# q

/ f8 C& S. h" v7 d) R9.   指定模块退出函数(被指定的函数在rmmod驱动时调用)
* a! G: O) N- o/ f7 @2 M9 [- E* J' ]5 h9 `: J' O1 ]
module_exit(usb_kbd_exit); 2 q( R( p" ~) p- K

: M# l& G. \  I3 q) U10.   编写中断请求处理函数:
" o( Z9 k5 }2 C! E0 V! I4 `  x) a' R! G

/*中断请求处理函数,有中断请求到达时调用该函数*/

4 ?" c" ~! t3 ?; r/ J

static void usb_kbd_irq(struct urb *urb, struct pt_regs *regs)

6 \7 g; s0 P- H. p" R% a

{


5 c5 k( s% e% l0 Q

    struct usb_kbd *kbd = urb->context;


7 E+ O, j. V. W/ z

    int i;


% n7 y  J; @8 ?1 ~" \: ?* |1 b" L2 q

      switch (urb->status) {


$ d% D0 B3 U9 U% b0 a+ {

    case 0:       /* success */

, F1 e' H5 V, F9 j/ e

        break;


8 p8 c$ N. ~9 _8 P/ j

    case -ECONNRESET: /* unlink */

# @* z  B  P) ]0 Z) `7 \* ?' ]

    case -ENOENT:

. G: u* T- t/ g/ K$ z

    case -ESHUTDOWN:

& M4 M: x4 i: ~9 i3 E3 j6 M4 w8 {

        return;


; k1 |3 y4 Y0 i5 Y7 y

    /* -EPIPE: should clear the halt */

5 r+ t, }# X$ I1 O

    default:   /* error */

$ \9 p# Q  Z% }, Q

    goto resubmit;


5 m- t' h) |6 `7 T) ?

    }

4 U* E! A/ S: r3 _9 |5 R

; U. B3 }- i: E3 x

    //input_regs(kbd->dev, regs);


9 {4 R" A: J5 R. A1 N* G  s* X! W2 S1 h

    /*不知道其用意, 注释掉该部分仍可正常工作*/

; m# ~. R7 d/ l/ G$ ~* i

    for (i = 0; i < 8; i++)/*8次的值依次是:29-42-56-125-97-54-100-126*/

- K1 M8 e% N( |6 X6 y  h; Z

    {

5 q) ~% e- b$ E7 O" [; T

    input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);


5 ~9 H  Y9 |3 I6 G. Y3 K* X. b

    }

, S' p9 R# [3 ]2 j7 R  V

   


1 u) u; Y5 }! y7 i

/*若同时只按下1个按键则在第[2]个字节,若同时有两个按键则第二个在第[3]字节,类推最多可有6个按键同时按下*/


9 I3 j1 S0 W! r3 f0 N

    for (i = 2; i < 8; i++) {


( @) J  ^; R5 ~1 B: k4 H7 @

    /*获取键盘离开的中断*/


. R. v. ~6 l. B) S( y. Y: h- C) w7 r

    if (kbd->old > 3 && memscan(kbd->new + 2, kbd->old, 6) == kbd->new + 8) {/*同时没有该KEY的按下状态*/

; Z; C% X- I2 \+ |  c

        if (usb_kbd_keycode[kbd->old])

* L! H# K- [# m+ j" W$ R" j

        {

& x& t/ N/ ]" R# N% t, y3 v

        input_report_key(kbd->dev, usb_kbd_keycode[kbd->old], 0);


$ N& U) s# w: n8 @  w

        }


0 H* U# j8 i. |3 S, V

        else


* x7 R/ n8 J. {) ^  n- D0 s

          info("Unknown key (scancode %#x) released.", kbd->old);

5 ~6 q# \, @' o* o! X4 n7 _: V

    }


7 p! g9 Q  J8 ]% a
/ V/ V1 R+ ]2 y& n1 ^8 r

    /*获取键盘按下的中断*/


. Z5 D. s. ]& c. W& @& D  A- M

    if (kbd->new > 3 && memscan(kbd->old + 2, kbd->new, 6) == kbd->old + 8) {/*同时没有该KEY的离开状态*/

+ }" ]3 o- O7 W; |  Q

        if (usb_kbd_keycode[kbd->new])


6 E4 N! U. J3 w6 G

        {


: _" d' F2 F* p) c  p

          input_report_key(kbd->dev, usb_kbd_keycode[kbd->new], 1);

( c5 o) ?' J' K/ Y  t! M, @

        }

6 T5 _3 |( \! j9 Z! C

        else


2 o  b) @8 n0 _0 A' f  }( J. V  ^$ B& B* P

          info("Unknown key (scancode %#x) pressed.", kbd->new);


3 V" c, k& d/ D" a; Q! o- C5 K  Z

    }

. y( ~' ]9 P4 z0 j9 ~3 F

    }

/ u0 p' a) E' q9 p5 i/ n0 r6 M

    /*同步设备,告知事件的接收者驱动已经发出了一个完整的报告*/

3 m+ W4 T2 L2 H

    input_sync(kbd->dev);

0 m# t5 M5 m  S, Y/ v- x

    memcpy(kbd->old, kbd->new, 8);/*防止未松开时被当成新的按键处理*/


7 D- @% A5 M+ p! y' k

resubmit:

/ @5 J" _9 N2 P7 j! B) J% e* W) z+ b

    i = usb_submit_urb (urb, GFP_ATOMIC);/*发送USB请求块*/


1 X7 O% u) ~. J$ Q$ |

    if (i)

& R/ u" G/ d5 f% e

    err ("can't resubmit intr, %s-%s/input0, status %d",

, k8 w% x: J# i( N8 F

        kbd->usbdev->bus->bus_name,

: o/ C9 z* g1 r" X; x' I& G

        kbd->usbdev->devpath, i);

) M0 k$ D( ?5 L! p% ]
} 4 U! \- w9 T; r8 c0 p: i( b
11.   编写事件处理函数:
/ d. I# R5 c" M3 X8 w+ H

/*事件处理函数*/

# V' z9 H0 {: n7 F/ y+ ~; B0 k

static int usb_kbd_event(struct input_dev *dev, unsigned int type,

% W+ {; f' A- ~( D

      unsigned int code, int value)


  M# b  n3 e  L9 n& c' ?

{

# S% i; V5 Z3 Z7 H, J1 W

    struct usb_kbd *kbd = dev->private;


5 _! G' N2 J% W  c9 @6 e

if (type != EV_LED) /*不支持LED事件 */


# x6 k* [  ~7 w% f: B

    return -1;


" R0 A, U) l* E0 j1 ]1 }

    /*获取指示灯的目标状态*/

% C8 k6 r6 ]7 x0 U# y

    kbd->newleds = (!!test_bit(LED_KANA,   dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |

" w( o4 ^$ n2 Q2 [- Z

      (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL,   dev->led) << 1) |


1 F# c6 W( R/ M6 D

      (!!test_bit(LED_NUML,   dev->led));


9 o, U! e. n& d' W$ [3 q/ a: g4 ^$ C( ^/ e, |9 A

    if (kbd->led->status == -EINPROGRESS)


% I8 b2 C- G0 r6 P

    return 0;

  X2 o  V  A' |* c% y- p
' v3 u8 `9 B" I! r

    /*指示灯状态已经是目标状态则不需要再做任何操作*/

6 U- ?2 i8 n8 E  K

    if (*(kbd->leds) == kbd->newleds)

9 Z9 |1 A, o5 K6 n

    return 0;

0 C6 M6 y2 L) F0 L( }9 W

3 R" g# _9 W" d3 @1 w5 @$ B2 E" z

    *(kbd->leds) = kbd->newleds;


, s  k+ _, L% T# u

    kbd->led->dev = kbd->usbdev;

) o, {7 Y# i4 C

    /*发送usb请求块*/


7 Z' u, b- C- Y8 A: @. [3 r

    if (usb_submit_urb(kbd->led, GFP_ATOMIC))


. R1 X: c8 o: U) {! s" T

    err("usb_submit_urb(leds) failed");


6 D- V5 d3 k. x2 T: B# n

    return 0;


$ C0 ~9 Y$ c! G$ p! W} $ ^/ M+ z5 t# J
12.   编写LED事件处理函数:
, g8 x3 D% n: s) H* g* A

/*接在event之后操作,该功能其实usb_kbd_event中已经有了,该函数的作用可能是防止event的操作失败,一般注释掉该函数中的所有行都可以正常工作*/

6 {9 x& i* C% q- W! H# Y% V

static void usb_kbd_led(struct urb *urb, struct pt_regs *regs)

& @2 ]/ q& J0 z4 c. R/ {8 ~

{

2 x. f' K( L4 u+ m; Y* O2 z- g: j5 O

    struct usb_kbd *kbd = urb->context;

+ p; L; {) o7 W8 N

2 V; G# s! O8 H2 }' V9 c' H4 U8 C

    if (urb->status)

; w! S& N- S" Q3 `* [8 b

    warn("led urb status %d received", urb->status);

7 M, ?, B$ [! f4 X5 _

3 r+ P: t6 x$ J2 ?3 F$ l6 ?

    if (*(kbd->leds) == kbd->newleds)/*指示灯状态已经是目标状态则不需要再做任何操作*/


; i- H0 q6 @: M# I

    return;

% W0 N" O) e4 C5 ^8 O8 S

  j6 a9 g9 ?, S, g  d) S+ k7 @' R

    *(kbd->leds) = kbd->newleds;


, L& h8 G9 z7 J- {5 |- F

    kbd->led->dev = kbd->usbdev;

) K. `7 {( b4 [! R/ l7 z

    if (usb_submit_urb(kbd->led, GFP_ATOMIC))


5 E. w% e" _/ _3 g# N5 I

      err("usb_submit_urb(leds) failed");


9 g6 b8 q: M! E. B6 B* Y}
# d: n' ~7 k4 I( o& L* ^, o# H13.   编写USB设备打开函数:
, `+ r) u2 C" O3 f5 ]9 u, j

/*打开键盘设备时,开始提交在 probe 函数中构建的 urb,进入 urb 周期。 */


' O9 M0 c; D- q% Z. @

static int usb_kbd_open(struct input_dev *dev)


$ e- Q. |1 M% T/ `' V' E

{


* B( s3 M7 l4 Z. G4 O8 y

    struct usb_kbd *kbd = dev->private;

' f: e$ q8 ~' Q9 }' r- `. S

    kbd->irq->dev = kbd->usbdev;

9 j) J9 X/ l: p

    if (usb_submit_urb(kbd->irq, GFP_KERNEL))

, Q1 l  B+ U1 Y7 i" G1 k

      return -EIO;

$ E) _7 i# b. V2 L

    return 0;

3 W8 f2 F+ Q& y4 j/ n& M- F
}
$ D8 T9 M( D' ?; X! ]$ ^14.   编写USB设备关闭函数
1 R/ i( Z5 F% [# o' c

/*关闭键盘设备时,结束 urb 生命周期。 */

! s1 b4 j- l( g! W. V0 X/ K

static void usb_kbd_close(struct input_dev *dev)

; s3 A% q2 B$ z5 u+ q. o1 s

{

$ p; L- V. z9 b$ Y$ p% R# C' `

    struct usb_kbd *kbd = dev->private;

7 s4 ]3 m4 F' o) v& {1 m+ T

    usb_kill_urb(kbd->irq); /*取消kbd->irq这个usb请求块*/

$ }! ?/ ^# W6 u$ z
} - N0 t: v9 \: d5 w+ @+ B: E
15.   创建URB - i: w$ z8 v1 c# v" Z% Z6 c

/*分配URB内存空间即创建URB*/


0 O% V. Y2 n# c/ {7 M2 C

static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)


$ {4 d! G8 K# {5 `  U

{


2 M( D. d4 C: i

    if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL)))

' O/ w# M  g+ L" x* H0 y

    return -1;

+ O# B6 S( G) b

    if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL)))


0 `( ?7 l( W. p/ _1 m' A& P

    return -1;


3 g6 v* o; Q* c3 g  G) S; n, J6 J" k

    if (!(kbd->new = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &kbd->new_dma)))


+ z% k1 ^$ c5 s" u( `) [4 m

    return -1;

; A. M  i" n: y5 J( L4 F# C

    if (!(kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), GFP_ATOMIC, &kbd->cr_dma)))


3 q( l+ y, h# R* F' F

    return -1;

9 I' L0 P/ U- U! n/ ~! M- n

    if (!(kbd->leds = usb_buffer_alloc(dev, 1, GFP_ATOMIC, &kbd->leds_dma)))

) X) Q' c6 I7 e+ N3 E( \

    return -1;

6 Q7 C( {9 E4 d9 q* f) W

    return 0;


# ]% b8 Q5 K4 m) H4 j4 t+ F}
- F  _4 [$ ?6 D16.   销毁URB 6 b0 l% b* G4 f

/*释放URB内存空间即销毁URB*/


' U  ], E# q  R# _* U8 u8 Q

static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd)

  j! k8 ]3 V/ b

{

) t. ]4 c  _5 S( t) A  F% X

    if (kbd->irq)


1 ]8 @  ~! g2 ?  Q% c6 i

      usb_free_urb(kbd->irq);


' v. y$ _! ?+ B; B5 o2 S

    if (kbd->led)


: ?1 D. m# A* L9 `$ o

      usb_free_urb(kbd->led);


' M. \/ e( P1 D) F8 m5 O# F

    if (kbd->new)


8 A/ @+ n' f# H8 x

      usb_buffer_free(dev, 8, kbd->new, kbd->new_dma);

* O; ?, k9 t' Q: [) ~2 U7 X

    if (kbd->cr)

( s! h' r0 w3 r( k2 X3 M

      usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma);

4 E. n: r+ i. U; s: n, Q6 ~

    if (kbd->leds)


# p. R" Z4 A: b$ Q6 ]

      usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma);

& W: h. [% ]7 I

}

) s# T( Y$ a/ n' U
17.   USB键盘驱动探测函数:
* m3 |$ p! J5 v1 N0 O

/*USB键盘驱动探测函数,初始化设备并指定一些处理函数的地址*/


. V/ v& D* Z- ~6 H& s

static int usb_kbd_probe(struct usb_interface *iface,

2 j" _& ?6 u( s! ]

        const struct usb_device_id *id)


( o9 f' L/ c+ \% M" J

{


" y3 D8 X& E* e$ S

    struct usb_device *dev = interface_to_usbdev(iface);


, _( u8 x9 K4 @" C# s, O$ u' t

    struct usb_host_interface *interface;

$ C- r. [4 D) u# z+ S0 |

    struct usb_endpoint_descriptor *endpoint;


6 ?# h) [7 a9 u# f) c

    struct usb_kbd *kbd;


  B! S; {% i/ N  ^, F' ~. C

    struct input_dev *input_dev;


. ]' \; n& W# S& o

    int i, pipe, maxp;

0 ]) w/ o; D4 Y8 P7 H7 m

    /*当前选择的interface*/


; }0 f" P- h' m+ ~0 }7 N; O

    interface = iface->cur_altsetting;


; e  H; U5 f# H2 P' a6 S# d7 {

    /*键盘只有一个中断IN端点*/


3 w1 t& A5 U  t8 `7 q0 P

    if (interface->desc.bNumEndpoints != 1)

: l, C! [8 \7 w$ J

    return -ENODEV;


  \. g' a( U7 X

    /*获取端点描述符*/


; o2 P& _0 Y" r8 m2 O' ?- j

    endpoint = &interface->endpoint[0].desc;


. J* ^. s9 q0 v1 {  r6 X: f

    if (!(endpoint->bEndpointAddress & USB_DIR_IN))


. f4 u; ~: v$ V' K

      return -ENODEV;

$ d7 u0 d/ o+ v$ D6 ]

    if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT)


- @; c. p7 Z" O: N! }

      return -ENODEV;

7 O  s- X- T6 L6 F: |

    /*将endpoint设置为中断IN端点*/


8 s# Q, V8 g6 q' ]  H  C3 D

    pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);

. G* f0 U4 B5 d2 E6 x/ G; f

    /*获取包的最大值*/

/ q3 i! |1 k& p. x2 X! G: V

    maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));


( ]; {1 F7 g8 ]# S3 j) E# R

    kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);

- x" r% N7 h' t

    input_dev = input_allocate_device();


  y0 n# P" X" W( e  D' e

    if (!kbd || !input_dev)

1 c! y* H' [# F) p- |7 W. Y4 `

      goto fail1;


; |2 `! j# A1 _4 I8 X, D8 C
- d! z& T4 h* r, \0 s1 h4 D! b# _

    if (usb_kbd_alloc_mem(dev, kbd))

' C+ h; ?- @$ _7 U

      goto fail2;

( N1 j; H  ?7 X+ a

    /* 填充 usb 设备结构体和输入设备结构体 */

4 v4 g: y- C. M7 \- j: {

    kbd->usbdev = dev;


8 a$ v( D4 }. B. [) p. L) S* C7 V, b

    kbd->dev = input_dev;

0 ^& c, J* g/ w5 j( `4 }0 T2 I
1 g" Y* }0 R9 {5 x3 o# Z

    /*以"厂商名字 产品名字"的格式将其写入kbd->name*/

$ W: q- A% I; f' {1 M

    if (dev->manufacturer)

+ {9 q; Q" ?; E+ A: H

      strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));

. X" i% U- s* `
/ B8 {6 p1 ~  H% j6 \9 Q/ g3 b8 p1 x

    if (dev->product) {


9 d  u' H) N6 {' L

    if (dev->manufacturer)


; z" \, B/ \* y, V/ h  W

        strlcat(kbd->name, " ", sizeof(kbd->name));

% J2 s+ m- j/ D8 ?: \

    strlcat(kbd->name, dev->product, sizeof(kbd->name));

+ b, M: t4 Y) b

    }


2 d5 P$ e4 z# C) K! d7 ^: J
$ k+ O$ }& |; M, W0 A# H/ `8 M0 Q$ W

    /*检测不到厂商名字*/

# q8 U) X- Q9 M  X

    if (!strlen(kbd->name))

: d, R- C/ Z8 J; K( ]5 ?7 i6 ]

    snprintf(kbd->name, sizeof(kbd->name),

: W! j- F% T  p9 n7 W

        "USB HIDBP Keyboard %04x:%04x",

+ X& j/ ]; K! R/ z$ D/ e0 D

        le16_to_cpu(dev->descriptor.idVendor),

0 ~0 t* D2 W0 Y; o  ?* j9 d

        le16_to_cpu(dev->descriptor.idProduct));


5 l5 p8 C4 ]8 J9 G

    /*设备链接地址*/

3 O0 I0 [+ u4 R! [' ~0 K

    usb_make_path(dev, kbd->phys, sizeof(kbd->phys));

# E' Z9 l  B% ]% H# r

    strlcpy(kbd->phys, "/input0", sizeof(kbd->phys));     

. g2 f: I; h4 c# i

    input_dev->name = kbd->name;


$ N9 q3 o7 V& z: K, x. h
0 ^! l) ~" b2 O; h. r2 N

    input_dev->phys = kbd->phys;

7 f/ f* u* m% L7 a' {

    /*


& B# f" ^. J' z+ P* c& p: J) N

* input_dev 中的 input_id 结构体,用来存储厂商、设备类型和设备的编号,这个函数是将设备描述符


: [: J9 V* R  G' n+ o+ M! r

    * 中的编号赋给内嵌的输入子系统结构体

) Z* c3 ^% L7 M' `& m

    */

- ~: c" l9 Q  s5 ]- U$ g

    usb_to_input_id(dev, &input_dev->id);

' a& a/ L) f; D$ R% @  c0 C

    /* cdev 是设备所属类别(class device) */


/ d1 a) J4 O  j1 Z' M" d

    input_dev->cdev.dev = &iface->dev;


' ~6 T) r9 Z6 o. h. ?

/* input_dev 的 private 数据项用于表示当前输入设备的种类,这里将键盘结构体对象赋给它 */


, l9 x* `2 }9 e+ H0 o, Z5 w

    input_dev->private = kbd;


+ e, c$ _* H. [( G- ?

input_dev->evbit[0] = BIT(EV_KEY)/*键码事件*/ | BIT(EV_LED)/*LED事件*/ | BIT(EV_REP)/*自动重覆数值*/;

  T' }5 {3 }! i. i- N

input_dev->ledbit[0] = BIT(LED_NUML)/*数字灯*/ | BIT(LED_CAPSL)/*大小写灯*/ | BIT(LED_SCROLLL)/*滚动灯*/ | BIT(LED_COMPOSE) | BIT(LED_KANA);


; t6 J/ N3 R( O* r8 W) R/ ?7 U! @4 n* L( p4 `

    for (i = 0; i < 255; i++)


& a( h8 S4 K4 l1 S4 H. m

    set_bit(usb_kbd_keycode, input_dev->keybit);

1 v' ?3 q2 a1 B! H: s

    clear_bit(0, input_dev->keybit);

/ c( j" `. R& T" A& [9 V. C

4 x4 p6 H9 [% e( p

    input_dev->event = usb_kbd_event;/*注册事件处理函数入口*/

8 D2 S7 u4 r/ {! r9 S

    input_dev->open = usb_kbd_open;/*注册设备打开函数入口*/


, p6 y  R( O1 L- q9 A& G: L$ @

    input_dev->close = usb_kbd_close;/*注册设备关闭函数入口*/

' G4 a1 @1 R4 L' c$ t0 ]/ _
" Q) Q# f; q8 A) Q- y* T

    /*初始化中断URB*/


4 r1 G* u% t7 N4 m! W! J

    usb_fill_int_urb(kbd->irq/*初始化kbd->irq这个urb*/, dev/*这个urb要发送到dev这个设备*/, pipe/*这个urb要发送到pipe这个端点*/,

4 G6 U* ~: k4 q( [+ O, |4 l

        kbd->new/*指向缓冲的指针*/, (maxp > 8 ? 8 : maxp)/*缓冲长度*/,


- p% |+ ]& I7 c- P

        usb_kbd_irq/*这个urb完成时调用的处理函数*/, kbd/*指向数据块的指针,被添加到这个urb结构可被完成处理函数获取*/, endpoint->bInterval/*urb应当被调度的间隔*/);

* E2 ^+ x- o* _

    kbd->irq->transfer_dma = kbd->new_dma; /*指定urb需要传输的DMA缓冲区*/


# D% ?  Q2 t* z

kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;/*本urb有一个DMA缓冲区需要传输*/

- ?/ x1 x! h  v% w/ S# g  I

    kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;/*操作的是类接口对象*/

+ U: l4 [- v8 ^# {4 ~8 U- S3 Z

    kbd->cr->bRequest = 0x09; /*中断请求编号*/


4 H8 ~$ q! F1 D; T. ]$ t

    kbd->cr->wValue = cpu_to_le16(0x200);


8 ?+ C5 d' ^; [# N

    kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);/*接口号*/


1 r5 C. p% ^0 q# M6 K9 `% S

    kbd->cr->wLength = cpu_to_le16(1);/*数据传输阶段传输多少个bytes*/


, Q- a" B% x' Q! \
: ~; v( D: a; p+ t

    /*初始化控制URB*/

# o' y- a7 j1 b! i- j, ^

    usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),


9 c% ]0 }$ H) O$ @

          (void *) kbd->cr, kbd->leds, 1,

2 E  U6 I) T+ ?! `& K

          usb_kbd_led, kbd);

6 d) `1 O7 G& f

    kbd->led->setup_dma = kbd->cr_dma;


+ P7 C" ]# g1 ^1 E2 N2 j& H

    kbd->led->transfer_dma = kbd->leds_dma;

3 A0 G) K& X5 B2 m) r- W

    kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP/*如果使用DMA传输则urb中setup_dma指针所指向的缓冲区是DMA缓冲区而不是setup_packet所指向的缓冲区*/);

* P1 P3 o( D; t4 r

4 M3 @9 @' N# J

    /*注册输入设备*/

( |. d5 b) K4 I* `8 Z6 }! [' R

    input_register_device(kbd->dev);

& `6 k% Z1 I0 v4 s

; w8 c$ v3 U6 \+ d2 c" H1 i! H

    usb_set_intfdata(iface, kbd);/*设置接口私有数据*/


1 M' ^# U: z2 t/ n7 l2 c) I2 n+ t6 P

    return 0;

# H, {" s7 s3 ~% c  M7 |$ k
; x5 ?$ u% s$ F" B1 a

fail2:   usb_kbd_free_mem(dev, kbd);

  X& _: l5 g3 s! i6 E

fail1:   input_free_device(input_dev);


& t% A4 g; \) e5 b) y

    kfree(kbd);


# q3 u+ T3 z6 d( S  C2 K

    return -ENOMEM;

/ y) N1 |3 p, q! q
}
: z, R/ Y( _+ L0 C* ?$ v18.   编写断开连接的函数:
3 Q( L3 j( |3 n7 [

/*断开连接(如键盘设备拔出)的处理函数*/

( E, v1 R( q9 [9 Q& {: B

static void usb_kbd_disconnect(struct usb_interface *intf)

0 y. w* |( N: h

{

$ Z8 T, r8 e8 S2 h' x

    struct usb_kbd *kbd = usb_get_intfdata (intf);/*获取接口的私有数据给kbd*/


4 x% A: P/ r; g' F. \. L! h$ s

    usb_set_intfdata(intf, NULL);/*设置接口的私有数据为NULL*/

8 y* p0 `! l: j  X% e

    if (kbd) {


9 @" `3 P9 [$ Z+ D& V

    usb_kill_urb(kbd->irq);/*取消中断请求*/


/ ?- W, w5 ]  L8 Y: l

    input_unregister_device(kbd->dev);/*注销设备*/


: b1 A3 }; v7 Q+ g$ x

    usb_kbd_free_mem(interface_to_usbdev(intf), kbd);/*释放内存空间*/

4 U! Q8 v/ V1 R) [4 m, j3 m

    kfree(kbd);


4 a1 i# }. L3 S

    }


8 H7 p) x; ?- l, P$ x}
2 T  \( d7 S8 E" b) l* t3 b9 v19.   编写Makefile: * k. Y9 y  K2 `

##############################

2 _2 `4 ?" D$ G* i! ~5 g& B% z. s

#usbkdb Makefile for linux


3 s0 V1 ?" Y# l! Y  w5 a/ r: r

##############################

3 ]0 |! W3 m  c9 r, f" d3 {$ p3 O

obj-m:=usbkbd.o


+ W$ J* I, l0 H$ }) C: v* D

KERNELDIR ?= /lib/modules/$(shell uname -r)/build


+ _# Q6 U% |3 O1 \

PWD:=$(shell pwd)

) C( J0 ?' F: b0 G* b  m

default:


' p  G/ @4 U, F! g1 z! l$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
2 ?9 {4 {  W% |2 l! t) ], }0 W# A3 Y- L7 g4 a

: |* r/ O- r2 M  f$ V) I4 h5 [* z9 t' X! ^% r3 ^2 ]
6 G1 W- z% M8 S2 p
usbkbd.rar1.   指定USB键盘驱动所需的头文件:
0 ], K9 j! f; h/ T5 X+ J% h# h# P
/ i# U; Z2 Y0 A+ p' u# Q9 M3 G1 ^

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


! |) C+ Z5 b6 b$ g' E. p% C

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

! ]: _* Z# N) y# K$ i! W

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


8 p3 v1 ~$ c: b0 U  L) C/ v/ u! h4 A

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

) U# j3 m: m% I, W' F6 O( i9 f

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

& |2 D0 |: r- a9 c* N. l: [% O* f9 b

#include <linux/usb.h> /*USB设备相关函数定义*/


$ {# a; y7 O8 F1 ~* r) J3 r" ~# \2.   定义键盘码表数组:
* U' |6 s& B8 N' _/ J2 ^& P

/*使用第一套键盘扫描码表:A-1E;B-30;C-2E*/

! X* g; T/ P3 T9 C* Y  b& w; }

static unsigned char usb_kbd_keycode[256] = {


3 p  @) U" z  {9 z- O3 Y2 b. v; F

    0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,


6 A' C: O1 q) D, D: Q7 I$ B

    50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,

; z) L' z" p6 {" J  W1 d

    4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,


& A7 l8 Q+ \2 m2 ?% H4 z

    27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,

  {- {. E, @  d' \+ E

    65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,


. Z8 ~6 `( v' J) s- `7 x$ p

    105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,


$ o$ X, t9 |9 ]0 ~& n$ j

    72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,

; B" A5 |- `9 m$ E1 l

    191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,

# q! b2 ~$ Q& C$ ]+ X2 \6 x5 z  L

    115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,

1 Q8 y1 X8 n6 k7 x+ R& j

    122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


- [7 i# \$ k* o) h) b0 l

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

$ B. Y9 l* A( \5 C8 C# ]7 s

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

; U( {4 M2 u( ?9 M* v& @' n

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

- x5 O" c* u: q% u0 z; G, e

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

4 D  B- P2 V8 y

    29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,

; L9 b9 _) }* A. ]2 R; N7 U

    150,158,159,128,136,177,178,176,142,152,173,140


8 N& ^: F4 L5 Z* q7 J}; ; `* b. k+ I4 ]
3.   编写设备ID表: - O4 ]% L- d* s- o+ \

static struct usb_device_id usb_kbd_id_table [] = {


+ m/ A% w6 Y% r4 h' V

    { USB_INTERFACE_INFO(3, 1, 1) },/*3,1,1分别表示接口类,接口子类,接口协议;3,1,1为键盘接口类;鼠标为3,1,2*/


/ j2 Q8 C5 w% G- X, C* o

    { }           /* Terminating entry */


1 v- [( h6 D1 ~5 j* `- a! R8 h

};

+ q/ @0 L+ W$ u6 \' E# P
MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);/*指定设备ID表*/
) b; C  R" X' x8 _4.   定义USB键盘结构体: ! j6 F+ T; ?& {7 V  k( @( z  `

struct usb_kbd {

8 G: G7 O; |2 @0 D+ e4 S

    struct input_dev *dev; /*定义一个输入设备*/


, f/ t2 u, l; j2 ~9 |1 i

    struct usb_device *usbdev;/*定义一个usb设备*/


" k) `; e8 y8 V2 r5 T; E

    unsigned char old[8]; /*按键离开时所用之数据缓冲区*/

, \- f5 o4 m: |

    struct urb *irq/*usb键盘之中断请求块*/, *led/*usb键盘之指示灯请求块*/;


7 {, L+ M" z, D

    unsigned char newleds;/*目标指定灯状态*/

; _, w/ u: `9 Z- |6 L( ~; d" F' Z

    char name[128];/*存放厂商名字及产品名字*/

) Y- R# x7 d) ?6 h1 t

    char phys[64];/*设备之节点*/


$ o6 H7 y& Z) C5 N) m0 D' B
+ j6 y+ y% K& a6 C: Q) W

    unsigned char *new;/*按键按下时所用之数据缓冲区*/


! [: c8 Q2 v* M5 J( J

    struct usb_ctrlrequest *cr;/*控制请求结构*/

4 X5 g: v8 @3 ^/ o

    unsigned char *leds;/*当前指示灯状态*/

7 A0 x/ ?9 O9 Y: r/ Q2 z

    dma_addr_t cr_dma; /*控制请求DMA缓冲地址*/


9 k% j) p1 }4 [* f* S

    dma_addr_t new_dma; /*中断urb会使用该DMA缓冲区*/


" c; F0 P  \" t1 @4 T& C* n+ I

    dma_addr_t leds_dma; /*指示灯DAM缓冲地址*/

  `) f( z+ }! f) K4 D0 y
};
8 J% T9 o# R! p$ Q3 v5.   编写USB键盘驱动结构(任何一个LINUX下的驱动都有个类似的驱动结构)
" e. Y/ S( G, F4 |

/*USB键盘驱动结构体*/

$ z5 h2 x/ R7 B1 L1 G2 e

static struct usb_driver usb_kbd_driver = {

% l. P9 b$ p1 D6 K; a% D

    .name =   "usbkbd",/*驱动名字*/

6 S6 ^1 J! j0 h$ i9 m

    .probe = usb_kbd_probe,/*驱动探测函数,加载时用到*/

: `2 E/ V& I' N; Z- `+ l

    .disconnect = usb_kbd_disconnect,/*驱动断开函数,在卸载时用到*/


5 l2 g) C: e+ p

    .id_table =   usb_kbd_id_table,/*驱动设备ID表,用来指定设备或接口*/


$ B5 d% [2 w1 q5 f; Z

};

/ o, p9 m4 ^. T5 s& x
6.   编写模块加载函数(每个驱动都会有一个加载函数,由module_init调用)
3 p) E5 N. }3 w# F) G

/*驱动程序生命周期的开始点,向 USB core 注册这个键盘驱动程序。*/

6 C* }! @+ t* y! V8 M% y' W

static int __init usb_kbd_init(void)


" E6 E* h, u' d+ T. h9 n

{


  c: G: y# f, G. K$ h' d# K, o& U  i' C

    int result = usb_register(&usb_kbd_driver);/*注册USB键盘驱动*/


1 B/ N- D- U2 @$ d8 O

    if (result == 0) /*注册失败*/


* B1 E1 T% k7 r( G5 ]5 ]3 A5 _1 W  |

    info(DRIVER_VERSION ":" DRIVER_DESC);


  j% k8 ^4 h/ _0 ~

    return result;


! @; F. ?: c9 F} 9 a, G2 g  X) Z  U. D+ i
7.   编写模块卸载函数(每个驱动都会有一个卸载函数,由module_exit调用) % G8 s/ j8 z: A8 R+ ^( t

/* 驱动程序生命周期的结束点,向 USB core 注销这个键盘驱动程序。 */


8 E- q% K; r- k8 \3 r1 H$ i; {

static void __exit usb_kbd_exit(void)


; |! W  j% u* C: `% L1 T9 y  y$ @

{


, I  e/ B: R0 D4 G) a

    printk("SUNWILL-USBKBD:usb_kbd_exit begin...\n");

3 I( L* J* h4 M) k/ p+ V# b# V

    usb_deregister(&usb_kbd_driver);/*注销USB键盘驱动*/


- p" d6 n+ N7 D9 ?! Z& k}
7 _' B; ]' y; x: B; x9 Q8.   指定模块初始化函数(被指定的函数在insmod驱动时调用)
5 m0 M8 r; }. ~, a+ h8 \2 l8 ~' x& n' V. S
module_init(usb_kbd_init);
% }4 [2 s7 ]7 a1 q2 ?/ b: m& E1 v
! K: V, _) g  E  [& |9.   指定模块退出函数(被指定的函数在rmmod驱动时调用): o/ n* o2 j9 l* q, o

! M9 K; R4 W8 m0 _, L8 W' M  r' ~% Fmodule_exit(usb_kbd_exit);
, n+ _& {2 `* o$ T0 }0 z1 M% A% k  }) F( m
10.   编写中断请求处理函数:
3 G1 H! l# L& E
1 t& e7 K7 A5 w" v0 o5 Y+ K

/*中断请求处理函数,有中断请求到达时调用该函数*/

4 q5 W1 w0 g) s4 W5 P

static void usb_kbd_irq(struct urb *urb, struct pt_regs *regs)


, A3 ~; U( Q5 s: R' G1 u9 [: I* T

{

& a) K* g6 S2 v, H2 w6 C* M

    struct usb_kbd *kbd = urb->context;

# ?; r3 K' T- \, \

    int i;

- L7 t& h. \9 E$ i1 [) i% V

      switch (urb->status) {


6 J2 k: A8 E0 j, l) T

    case 0:       /* success */


8 P4 g* Z4 `# k

        break;

$ Z- I, b3 r9 g) c  B

    case -ECONNRESET: /* unlink */

: J5 `1 Z; }7 r# Y8 w/ I

    case -ENOENT:


+ |( q! U" R4 O1 q" Z5 w$ Q% \

    case -ESHUTDOWN:

0 G1 |" Y# V; o

        return;


0 s4 Q8 D; l; w/ R1 v4 m. Q

    /* -EPIPE: should clear the halt */


7 u( E1 D) [7 p7 i" d+ H% B+ z8 Z

    default:   /* error */

% q' \$ ?0 ?8 ?

    goto resubmit;


3 A9 E9 t9 `6 h* B& E) R* H

    }

" b# _0 h+ B- @( ]/ D$ N
; k" Z1 Q: R/ ~) i: y

    //input_regs(kbd->dev, regs);


; [# i/ \6 `3 j
* x; m7 S4 ~6 i

    /*不知道其用意, 注释掉该部分仍可正常工作*/


4 H6 y! b, ]6 e# _5 n

    for (i = 0; i < 8; i++)/*8次的值依次是:29-42-56-125-97-54-100-126*/


8 i4 H* F! p6 z) {2 E- S- C

    {


' K& g" M" q3 @5 e

    input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);


- [$ C6 s4 o# O( O0 u, y, u

    }

# D! J) }' Y+ F& P2 B; E

   


1 g5 n4 u- L% ~" i% m) \

/*若同时只按下1个按键则在第[2]个字节,若同时有两个按键则第二个在第[3]字节,类推最多可有6个按键同时按下*/


- b: Y3 U0 D, Z3 G5 r

    for (i = 2; i < 8; i++) {

; e/ _- A+ o8 X

    /*获取键盘离开的中断*/


5 i' P% k5 w- [$ ^7 m/ |

    if (kbd->old > 3 && memscan(kbd->new + 2, kbd->old, 6) == kbd->new + 8) {/*同时没有该KEY的按下状态*/


- `9 }4 R: G& j+ T% e

        if (usb_kbd_keycode[kbd->old])

' }& `1 M- b4 x1 Q2 A7 P

        {

# q- [  ^# Q7 W" F3 G5 g

        input_report_key(kbd->dev, usb_kbd_keycode[kbd->old], 0);


& u$ Z7 x+ s* M5 g( B9 y) ?

        }


6 o% h+ x8 f9 E8 J4 d9 n

        else


1 x* [/ H. }) O

          info("Unknown key (scancode %#x) released.", kbd->old);


7 S0 _8 B  O0 _/ I8 U

    }


* _4 ~2 m5 s$ q* l. m1 i+ c6 d+ O
- Z. p7 h6 c. m+ p1 z+ w

    /*获取键盘按下的中断*/

  m7 p! {) D8 Z# [

    if (kbd->new > 3 && memscan(kbd->old + 2, kbd->new, 6) == kbd->old + 8) {/*同时没有该KEY的离开状态*/


! M2 u+ Q; H7 D  l

        if (usb_kbd_keycode[kbd->new])

: j; G' \: `. _( b+ ]

        {


0 o+ _+ W; F; m- P% R! y8 r4 H8 R

          input_report_key(kbd->dev, usb_kbd_keycode[kbd->new], 1);


5 ?/ p' e+ l- B4 V. M

        }

' b- n% M8 D- {; N

        else

$ B2 X- }- I* L  ?  U. D

          info("Unknown key (scancode %#x) pressed.", kbd->new);

7 M- X+ g" ^. o2 w0 v3 ?

    }


+ H1 D3 J7 m. [4 C' m

    }

& \, z1 S1 W6 Z& Q/ V

    /*同步设备,告知事件的接收者驱动已经发出了一个完整的报告*/


2 q! k9 O$ u5 b' J; K2 O+ P

    input_sync(kbd->dev);


0 C/ I( P% E7 N

    memcpy(kbd->old, kbd->new, 8);/*防止未松开时被当成新的按键处理*/


# M" y% l  N1 P* l

resubmit:


: `7 d+ B# I3 n1 L/ ?  U

    i = usb_submit_urb (urb, GFP_ATOMIC);/*发送USB请求块*/

6 d0 [- D. W. k7 Q

    if (i)


9 v7 I( K9 J5 g9 f5 G9 c  X$ G

    err ("can't resubmit intr, %s-%s/input0, status %d",


: i( ~7 u& i% p4 d# R) Z7 ]4 l

        kbd->usbdev->bus->bus_name,

' c/ T, b- K3 a: ?% H+ r

        kbd->usbdev->devpath, i);

1 m  e$ X( V- v
}
  m5 S0 B* U* K! D11.   编写事件处理函数: 0 @3 G: @% L+ b/ k1 m

/*事件处理函数*/

& V, h9 a! J1 q$ \5 [0 A

static int usb_kbd_event(struct input_dev *dev, unsigned int type,

! x! F: p. ?3 p: k" _

      unsigned int code, int value)


( P/ x, i! H* A, a8 L1 {8 D

{

' ]# f$ e' [# i

    struct usb_kbd *kbd = dev->private;


: X0 v' p6 t' r- E3 \

if (type != EV_LED) /*不支持LED事件 */


6 V! o! j" ]$ y( Q% [4 f7 c

    return -1;

5 c' ]& ^% P# [. ^. [, S, n: s

    /*获取指示灯的目标状态*/

, E1 f2 ^7 r7 R1 {

    kbd->newleds = (!!test_bit(LED_KANA,   dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |

5 w2 U8 j7 B2 J7 ^- o

      (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL,   dev->led) << 1) |


& E7 Z$ L, v  b3 I$ R0 M

      (!!test_bit(LED_NUML,   dev->led));


/ V, {2 w9 y+ {! d' n
4 e1 R5 _2 R/ m

    if (kbd->led->status == -EINPROGRESS)

- H1 h( N$ n2 b5 X

    return 0;


% I3 |0 N* a( u* p6 E
9 {3 l3 z* q$ a' e4 E

    /*指示灯状态已经是目标状态则不需要再做任何操作*/


3 _2 X' e5 \4 V; c3 s5 ?

    if (*(kbd->leds) == kbd->newleds)

% P9 {# N5 L+ ]

    return 0;

' H2 y; g& J" i) x/ V
* W8 z8 |9 k; p) k% |9 K/ E

    *(kbd->leds) = kbd->newleds;


& ]2 t9 n7 e) h* W2 X

    kbd->led->dev = kbd->usbdev;


$ L% p% t- }: b7 S

    /*发送usb请求块*/


: V4 [: {( j& X: d

    if (usb_submit_urb(kbd->led, GFP_ATOMIC))

0 z) }9 j9 j# ~# {

    err("usb_submit_urb(leds) failed");

$ H) D9 z) K( F3 O$ `7 j

    return 0;


* N: K# K: r6 a. P6 i}
# f" L: A# X1 O. V+ j4 j  `/ h1 B12.   编写LED事件处理函数:   B$ h1 @! u& n4 B1 ~

/*接在event之后操作,该功能其实usb_kbd_event中已经有了,该函数的作用可能是防止event的操作失败,一般注释掉该函数中的所有行都可以正常工作*/


5 `# g9 J3 k6 S  [

static void usb_kbd_led(struct urb *urb, struct pt_regs *regs)


9 T6 r9 d0 k% |% ^$ |8 W5 @- K

{


; S' b' n6 F6 {+ h

    struct usb_kbd *kbd = urb->context;


" A$ b8 a: w: c9 x$ V4 O; n
0 G  I; P" J8 }4 Y, d* G

    if (urb->status)

5 n% I9 M% Q, J/ l5 F( |

    warn("led urb status %d received", urb->status);

+ C6 d: V$ n! a0 N
3 G$ ]8 {8 j6 `! y7 _

    if (*(kbd->leds) == kbd->newleds)/*指示灯状态已经是目标状态则不需要再做任何操作*/


! [: U2 ?  G9 E& ], X/ U$ C

    return;


, I$ ~# }8 f7 A! J8 m% x5 m8 P! h! w4 u) x3 T" d* h* r

    *(kbd->leds) = kbd->newleds;

! g! S) c: e& P1 z! m. L

    kbd->led->dev = kbd->usbdev;


1 y' O+ X" e& X) D

    if (usb_submit_urb(kbd->led, GFP_ATOMIC))

, \1 R; k$ n. i/ v" @9 ~3 Q& l

      err("usb_submit_urb(leds) failed");


7 \! F. ]  j+ u! g5 ]( I}
4 k: h6 c8 C, V# N/ h13.   编写USB设备打开函数: ; q* i! @0 s% o0 {5 t+ u! C

/*打开键盘设备时,开始提交在 probe 函数中构建的 urb,进入 urb 周期。 */


* P- V2 F7 Y3 K8 N5 Z: w

static int usb_kbd_open(struct input_dev *dev)

4 T4 X5 e- t' g- N( P8 C

{


8 u2 G! q: g! t3 T2 _

    struct usb_kbd *kbd = dev->private;

# X) V! U6 z9 o7 e

    kbd->irq->dev = kbd->usbdev;

+ h6 F! [5 M# r( Z) B. `

    if (usb_submit_urb(kbd->irq, GFP_KERNEL))


3 p1 ^; U; k! b+ J5 @( I

      return -EIO;

2 L3 ^8 {' o7 h% U6 F3 {* `1 i

    return 0;

) O, Y, y& r5 O, Q( B( }
}
# a* u6 [6 R+ Q! \8 {$ X! ~14.   编写USB设备关闭函数 4 ?/ n4 p; w/ y( H$ F0 x  g

/*关闭键盘设备时,结束 urb 生命周期。 */

$ k3 `4 t/ H! c9 q1 s# u

static void usb_kbd_close(struct input_dev *dev)


  ~4 U! \- K2 {1 h6 F

{

0 G- C) l" p4 B& N1 j

    struct usb_kbd *kbd = dev->private;


! g! }; J# W) j) B  u

    usb_kill_urb(kbd->irq); /*取消kbd->irq这个usb请求块*/


5 B" B4 a. i( o' `% M/ y- _& B8 m}   n1 F* V5 b- n) w7 E
15.   创建URB
* N, n+ R/ e4 I

/*分配URB内存空间即创建URB*/

! \8 ^' X' X0 m& \) K6 Q

static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)


* }* m' h- a- j! C4 ?

{

4 }0 q3 F  k2 l! J% U

    if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL)))


: `7 O+ Y: p2 e9 S  V9 u

    return -1;


- ~! _( z6 |! t( D  v6 P

    if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL)))


3 ^& i) o4 M  Y5 R, b

    return -1;

# {) {% S/ u: t3 @3 @

    if (!(kbd->new = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &kbd->new_dma)))


% e" C, S# F9 P

    return -1;

  J8 D! u/ n- S

    if (!(kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), GFP_ATOMIC, &kbd->cr_dma)))

2 [2 U0 z. g6 m5 ]/ D& T  K

    return -1;


* t- }8 {  k' e6 j0 [8 w

    if (!(kbd->leds = usb_buffer_alloc(dev, 1, GFP_ATOMIC, &kbd->leds_dma)))

5 L$ t- W7 F. q; A5 i  s4 M# ^

    return -1;

# c8 p; l7 I+ X  s' j; a8 F6 A8 H) q

    return 0;

1 E/ X9 E: I* e1 ]
} ! }3 S" F6 \( k6 e- ]1 g
16.   销毁URB
  b" J; v) K% j1 U' W- _

/*释放URB内存空间即销毁URB*/

5 O4 t8 y  [  X( z3 {

static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd)

% y+ m0 f, P) V5 |8 F

{

# I: X) p2 e0 Z+ }% |

    if (kbd->irq)


1 W& N/ G: O. l) I  U/ g/ `" ?' e, h

      usb_free_urb(kbd->irq);


) ~) g2 F) n5 ]- l9 ~2 J1 \- L

    if (kbd->led)

" n' W+ y" `( V% V0 c

      usb_free_urb(kbd->led);

; y. o+ h+ X* p; k* d  c

    if (kbd->new)

7 T. K; a% X5 A9 C" Q

      usb_buffer_free(dev, 8, kbd->new, kbd->new_dma);


+ L# A' a! B! k0 A. w( ?3 _8 T

    if (kbd->cr)


5 l" d# R! q1 l% L

      usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma);

, ?' ^/ X- d& V; B& _

    if (kbd->leds)


( I  P8 w7 Z- T1 s- R

      usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma);

( L( W/ Z1 e3 Q

}


! V- S3 |7 N  q7 g; ^* W17.   USB键盘驱动探测函数: & b# ?: ?0 x- x- |3 Z$ {% g3 U' V

/*USB键盘驱动探测函数,初始化设备并指定一些处理函数的地址*/

- ~4 o( D7 x9 r! _4 _5 t

static int usb_kbd_probe(struct usb_interface *iface,

9 g7 G" o; {8 ~8 X1 I+ t' U

        const struct usb_device_id *id)

0 R) Q! n! D; x; n: g

{

3 a+ E6 A/ x, q# `5 s+ T0 E! _

    struct usb_device *dev = interface_to_usbdev(iface);


  t, E( q. ?7 ?* n( e+ H) M; l

    struct usb_host_interface *interface;


0 l" v' q7 u# X" e8 r

    struct usb_endpoint_descriptor *endpoint;


! M4 L/ {9 w- \! z' x4 ]: H

    struct usb_kbd *kbd;


$ w% Z) T5 b0 m) c. {! F

    struct input_dev *input_dev;

) B! Q9 ~& `  ]! j! R. G

    int i, pipe, maxp;


# Z8 E8 d/ }7 C- h4 Y

    /*当前选择的interface*/

/ M* ]2 y$ `) m7 n* S+ z  D

    interface = iface->cur_altsetting;

& M+ H/ l. D/ U

    /*键盘只有一个中断IN端点*/


1 I0 d2 O7 F, p6 c  ?

    if (interface->desc.bNumEndpoints != 1)

2 _& p0 C: V3 G/ e+ ]' d# {+ s

    return -ENODEV;

) \. y0 `* z9 P& I9 d& u) |7 o

    /*获取端点描述符*/

9 N" R5 i* r$ y& {

    endpoint = &interface->endpoint[0].desc;


. W, [" Y  g, a. @9 X

    if (!(endpoint->bEndpointAddress & USB_DIR_IN))

% n& Z2 ^9 ]1 a, F

      return -ENODEV;


. \3 k. {# A; {  {: h9 `9 z( [

    if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT)


' b3 j. C2 S& x

      return -ENODEV;

1 {2 ]# `" _4 x

    /*将endpoint设置为中断IN端点*/

5 c, _7 s2 i6 f% Z5 i7 _

    pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);

! H$ ?, C, [9 C( Z

    /*获取包的最大值*/


3 S$ H* y; a( j5 f

    maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));


6 p8 |' q" n3 O+ W  Z7 y

    kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);

% S+ m9 L0 j* l7 D. S- b( N

    input_dev = input_allocate_device();

- i- e9 ~, f( j, h

    if (!kbd || !input_dev)

5 k" h. H9 l) I1 U$ X* E

      goto fail1;


5 O0 |3 F. @, m, o9 Z& H0 {
4 w# O( ~7 i6 [9 y

    if (usb_kbd_alloc_mem(dev, kbd))

- t) A- Y2 ^  i- x; U6 v

      goto fail2;

+ K0 Z+ h! M! A" ?% C% r7 Q5 N% m& a

    /* 填充 usb 设备结构体和输入设备结构体 */

  c. t; L  I! R$ \* Q

    kbd->usbdev = dev;


6 {0 k. L" M( M( M; h

    kbd->dev = input_dev;


. A! H4 |# m) H9 h* v1 O* D0 f# c# g# g+ B0 H

    /*以"厂商名字 产品名字"的格式将其写入kbd->name*/

; K3 g- O3 j( v  ^- v

    if (dev->manufacturer)

4 e" e. z* v) d- s

      strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));


8 w. f% E! t  r' O3 D3 A) p0 c' n$ j. ]2 W- {- d

    if (dev->product) {


8 j! ]! ^8 h2 U# h6 y2 R) V! L

    if (dev->manufacturer)

& u, N* C2 h# d# M, x' o% h* c: R

        strlcat(kbd->name, " ", sizeof(kbd->name));


9 B* _% R3 W, }9 U3 v2 W+ G

    strlcat(kbd->name, dev->product, sizeof(kbd->name));


+ z* _! ?$ H2 i2 v4 ~/ ?1 K

    }

4 G& r, @6 y- U; o8 G; ], C9 x* w# e  V

; x  D- {; O- Z# {, P) w7 ~- o

    /*检测不到厂商名字*/

. z1 _1 |2 `4 R& A% {

    if (!strlen(kbd->name))


* S/ x9 ]6 l2 d& w4 e. f& F  `

    snprintf(kbd->name, sizeof(kbd->name),

  o4 X' b- ?0 a4 X. l* U, F# j

        "USB HIDBP Keyboard %04x:%04x",


- G) l( I5 Y! U+ {# P5 ]3 ~

        le16_to_cpu(dev->descriptor.idVendor),

6 m: N" Y8 Y& V+ P. D+ ~: v

        le16_to_cpu(dev->descriptor.idProduct));

/ l% f( U% {/ c

    /*设备链接地址*/


, X+ B/ b) v4 _4 R7 X

    usb_make_path(dev, kbd->phys, sizeof(kbd->phys));

3 D' o6 i& U8 |

    strlcpy(kbd->phys, "/input0", sizeof(kbd->phys));     

9 U0 F2 z+ J0 G! {( ?: }  J

    input_dev->name = kbd->name;


# o. R! B& l" p1 `5 g; M* b* M  ^* e0 l/ h3 c  z6 Q" ?' v- Z. H

    input_dev->phys = kbd->phys;


! j* k% F4 Q" D0 o! |

    /*


4 H. N; ~, |. j; d0 ?  W

* input_dev 中的 input_id 结构体,用来存储厂商、设备类型和设备的编号,这个函数是将设备描述符


) W% J/ M/ n3 j3 r

    * 中的编号赋给内嵌的输入子系统结构体

  C( }3 C; V6 v8 X: V$ P' N0 j# i

    */


" M$ f# ~4 I) W( X: p

    usb_to_input_id(dev, &input_dev->id);


' I" N9 B8 e( u3 D

    /* cdev 是设备所属类别(class device) */

; x  Q. U+ C4 V0 {1 G

    input_dev->cdev.dev = &iface->dev;

9 [/ H2 S! x- o! V* S# ~; U! o

/* input_dev 的 private 数据项用于表示当前输入设备的种类,这里将键盘结构体对象赋给它 */


2 L( W  h1 q7 f- v7 d$ x

    input_dev->private = kbd;

7 S2 g" X. m# g& |

input_dev->evbit[0] = BIT(EV_KEY)/*键码事件*/ | BIT(EV_LED)/*LED事件*/ | BIT(EV_REP)/*自动重覆数值*/;

$ V! c2 l) `8 z2 E* N8 a) v

input_dev->ledbit[0] = BIT(LED_NUML)/*数字灯*/ | BIT(LED_CAPSL)/*大小写灯*/ | BIT(LED_SCROLLL)/*滚动灯*/ | BIT(LED_COMPOSE) | BIT(LED_KANA);


5 E  e2 q! o: C, U9 ~1 L6 k
& m8 x; o. n% y# j: L' j* F2 }

    for (i = 0; i < 255; i++)


0 Z3 {; N  P3 P# c6 @

    set_bit(usb_kbd_keycode, input_dev->keybit);


# h# B( k5 y. F6 k; F0 @

    clear_bit(0, input_dev->keybit);

  q4 e# [+ e% V- a8 q. \
1 w$ N1 [" x" G, Q5 g

    input_dev->event = usb_kbd_event;/*注册事件处理函数入口*/

0 H. t+ C8 r- L1 O8 \

    input_dev->open = usb_kbd_open;/*注册设备打开函数入口*/


2 ~" p; _, X! x* m% k

    input_dev->close = usb_kbd_close;/*注册设备关闭函数入口*/


0 v8 Y( R. ?0 N, V  b. V1 k! `& l$ e3 O

    /*初始化中断URB*/


4 P) c, c3 v) E; n9 z0 e

    usb_fill_int_urb(kbd->irq/*初始化kbd->irq这个urb*/, dev/*这个urb要发送到dev这个设备*/, pipe/*这个urb要发送到pipe这个端点*/,

. U3 Q' n5 b. d$ z5 W$ T4 H

        kbd->new/*指向缓冲的指针*/, (maxp > 8 ? 8 : maxp)/*缓冲长度*/,

& Z2 b& N5 k0 |0 s. Z6 C

        usb_kbd_irq/*这个urb完成时调用的处理函数*/, kbd/*指向数据块的指针,被添加到这个urb结构可被完成处理函数获取*/, endpoint->bInterval/*urb应当被调度的间隔*/);


- E" ^0 m5 T3 A/ E

    kbd->irq->transfer_dma = kbd->new_dma; /*指定urb需要传输的DMA缓冲区*/


9 c" O' q  B" d0 R/ l

kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;/*本urb有一个DMA缓冲区需要传输*/


- w+ y) Y# K, V* f

    kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;/*操作的是类接口对象*/

  A" _6 A! i% i( t

    kbd->cr->bRequest = 0x09; /*中断请求编号*/

7 \  F" l; P4 c3 m4 V

    kbd->cr->wValue = cpu_to_le16(0x200);


2 f9 o' P' Z! e5 l! Z6 E

    kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);/*接口号*/


4 W" u4 `% C  w8 J- K

    kbd->cr->wLength = cpu_to_le16(1);/*数据传输阶段传输多少个bytes*/

# |- O( Z5 E0 h$ J
0 ?9 _1 s% {# w* @

    /*初始化控制URB*/


& t3 M0 W8 }2 \

    usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),

, Q1 `1 a$ D% ~2 Z1 H  C- w

          (void *) kbd->cr, kbd->leds, 1,

) l$ D% z1 L5 q/ C1 Q

          usb_kbd_led, kbd);

" [6 u+ Z& c% H( N

    kbd->led->setup_dma = kbd->cr_dma;

; i* n* E6 _1 U& \

    kbd->led->transfer_dma = kbd->leds_dma;


) o+ D' V' I6 S& Y' E

    kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP/*如果使用DMA传输则urb中setup_dma指针所指向的缓冲区是DMA缓冲区而不是setup_packet所指向的缓冲区*/);

( d8 ]& x+ a( U

3 M% O$ `. I2 A4 V9 s

    /*注册输入设备*/


, Q# N' |, S* M5 m) I

    input_register_device(kbd->dev);

$ R8 K, i9 T4 v

! p$ y: K- j0 R# u% I5 |

    usb_set_intfdata(iface, kbd);/*设置接口私有数据*/


% Q8 F! d- V& c( O

    return 0;

$ z4 y+ Y( I' r5 {$ F8 b  D

/ _3 L& x) V4 q3 `

fail2:   usb_kbd_free_mem(dev, kbd);


4 _# v6 _& ?- z1 o1 y

fail1:   input_free_device(input_dev);


  M" u5 @) a) ?9 |0 F* U

    kfree(kbd);

* ]5 C; S, \; D3 B& I8 }/ n

    return -ENOMEM;

' d1 D: ^1 s7 ^  y8 w/ f
} 8 m" C; S0 v. l
18.   编写断开连接的函数:
& D0 {7 B' l/ n2 `# V9 W

/*断开连接(如键盘设备拔出)的处理函数*/


! K: k- ]0 g. ~7 z7 S

static void usb_kbd_disconnect(struct usb_interface *intf)


3 ^& K3 n1 t6 y+ G/ H7 F

{


& o* ]% P5 P0 h

    struct usb_kbd *kbd = usb_get_intfdata (intf);/*获取接口的私有数据给kbd*/

% F8 V$ ]' Y) b- X; W0 k/ E

    usb_set_intfdata(intf, NULL);/*设置接口的私有数据为NULL*/


' U0 c6 f/ d0 O* f- X

    if (kbd) {

2 e' y6 C/ z% i$ r' G0 Q

    usb_kill_urb(kbd->irq);/*取消中断请求*/


4 ]0 k3 J- [8 u  c

    input_unregister_device(kbd->dev);/*注销设备*/


. f5 X% y: F3 o  C- O9 v

    usb_kbd_free_mem(interface_to_usbdev(intf), kbd);/*释放内存空间*/

7 Z; t& b! p4 N7 l6 D4 {

    kfree(kbd);

# V& r0 {0 @/ e" L6 T

    }


: Z" U' H6 l* j* u# C* L} " G7 P, M# J9 B7 ?
19.   编写Makefile: & D1 l* T6 B& h$ y5 }' @3 _

##############################


) B9 D3 |6 M/ E4 B7 ^

#usbkdb Makefile for linux

1 H7 m2 }; Q  k0 z$ }# _

##############################

+ Y) F: ^# m9 B- i) F

obj-m:=usbkbd.o

# ], N( f; U  z2 C4 M& [& l% O) h

KERNELDIR ?= /lib/modules/$(shell uname -r)/build


( Y$ M5 J8 Z2 b7 C& B$ y

PWD:=$(shell pwd)

& ^2 j5 B0 P- D5 @

default:


' O5 U1 z  j  `' ]; R# ^$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

1.    指定USB键盘驱动所需的头文件:
1 t! q5 f( V4 ~9 V
3 [/ M0 P8 J/ `. d" r6 o) Y

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

0 H( C2 p, Z; q7 D( b% m

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

$ {6 ?1 q6 h! m: q

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

9 U: ]9 l3 O, u4 o: ^+ s2 y& I# B

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


8 b3 Z- |8 X  g& ^6 ?

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


7 H6 w! g: K# H/ d" H1 O7 g

#include <linux/usb.h> /*USB设备相关函数定义*/

" [% s# B4 @) q" i
2.    定义键盘码表数组: 8 ?7 T4 C* H9 P9 R( m$ ]

/*使用第一套键盘扫描码表:A-1E;B-30;C-2E*/


  i6 b2 H: [) w

static unsigned char usb_kbd_keycode[256] = {


1 e4 {8 s1 G+ j# R( X

    0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,

: x' j* x: G4 H- j9 K

    50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,

$ s$ s7 _- X/ k$ Y1 A0 s/ n

    4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,

4 p3 K' e( Z" C2 o  p

    27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,


% c- G; a8 ]9 Y1 K! f3 Z8 U/ q1 G# W

    65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,

' a$ [: i1 u2 v

    105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,

0 e$ z0 A- M  L; }

    72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,

4 \( o- M- E8 w8 D- @

    191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,


, E$ ?. v4 I7 `. b1 B- ?/ j

    115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,

$ O! N# i7 }, ^& L' z/ X# q

    122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

  A# G' I$ p# Y

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


7 `' O! Z' w7 j, l3 V& T  e

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

3 b3 z0 N$ n: c* [

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

1 K( s6 |' m& m  I; I$ A

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

) Y2 y& n1 X" W0 t% r8 v+ D5 y

    29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,


- Z. a' a7 M* j8 c3 T* K

    150,158,159,128,136,177,178,176,142,152,173,140


' ~, p4 P& q7 v# M; M3 ^ }; 1 T7 }/ r7 W) n2 G' p2 O/ e4 ]. P
3.    编写设备ID表:
7 `! w" l) h! S  d# V% n

static struct usb_device_id usb_kbd_id_table [] = {


( ]; E9 e- t9 _" D) x

    { USB_INTERFACE_INFO(3, 1, 1) },/*3,1,1分别表示接口类,接口子类,接口协议;3,1,1为键盘接口类;鼠标为3,1,2*/


4 X2 H# ~* y# {$ e9 k

    { }           /* Terminating entry */

4 Y# J2 T: u6 W; w

};

" `% S( t5 v5 f+ R* e/ _! n' A
MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);/* 指定设备ID表*/ 9 z: C% X: Q5 e/ Y' f
4.    定义USB键盘结构体: 9 ?1 b, {, u* |& [% X

struct usb_kbd {


, b0 h5 `0 ]4 P8 j6 P) G

    struct input_dev *dev; /*定义一个输入设备*/

$ m. o/ R" g0 F9 L6 w# _

    struct usb_device *usbdev;/*定义一个usb设备*/

: B: U; y  S3 u1 A4 m+ f6 O5 `

    unsigned char old[8]; /*按键离开时所用之数据缓冲区*/


# W; g6 M3 }6 c$ X

    struct urb *irq/*usb键盘之中断请求块*/, *led/*usb键盘之指示灯请求块*/;

, {0 ?7 s( u3 _0 x3 l% F7 L

    unsigned char newleds;/*目标指定灯状态*/

5 a& g) A. }6 B# T0 F; f0 i

    char name[128];/*存放厂商名字及产品名字*/

) R/ e9 L1 j" Y

    char phys[64];/*设备之节点*/


! o* S) z) J% `( g
1 ~3 y/ M- f$ H- F

    unsigned char *new;/*按键按下时所用之数据缓冲区*/


: [  a2 L3 i# D- ]4 j& U9 o

    struct usb_ctrlrequest *cr;/*控制请求结构*/

2 t8 h" N# d! j* B* A4 t) T

    unsigned char *leds;/*当前指示灯状态*/


. J' \1 i) l* A; D+ w

    dma_addr_t cr_dma; /*控制请求DMA缓冲地址*/

  _; N1 k4 A5 \/ I

    dma_addr_t new_dma; /*中断urb会使用该DMA缓冲区*/


. R+ o8 E7 K# G0 g* {$ H

    dma_addr_t leds_dma; /*指示灯DAM缓冲地址*/

. O, z1 l! h5 v5 W
};
& `0 p. |% N2 Z4 m9 ~+ x$ w* C& |. O 5.    编写USB键盘驱动结构(任何一个LINUX下的驱动都有个类似的驱动结构)
# o. c- A# w9 C. H1 r9 u% L

/*USB键盘驱动结构体*/

' ]  X; K( L- d0 E- V

static struct usb_driver usb_kbd_driver = {


) \7 j0 T+ `' S( i

    .name =   "usbkbd",/*驱动名字*/


: D0 d  E4 k( G; H0 d; A

    .probe = usb_kbd_probe,/*驱动探测函数,加载时用到*/


9 A2 V" I( ~5 t& m: W" E

    .disconnect = usb_kbd_disconnect,/*驱动断开函数,在卸载时用到*/


( T* ?5 ^3 R( Y2 R& Q& r$ n: {/ Q8 o6 e

    .id_table =   usb_kbd_id_table,/*驱动设备ID表,用来指定设备或接口*/


# w# k  z% u" J, o

};

# L/ |! C7 b1 C- z% c$ `4 E# r' s
6.    编写模块加载函数(每个驱动都会有一个加载函数,由module_init调用)
7 J5 t& s* {0 ~1 m

/*驱动程序生命周期的开始点,向 USB core 注册这个键盘驱动程序。*/


% i" R, t: \& X% J: O0 m5 Q

static int __init usb_kbd_init(void)

- K( W* G" V6 S1 g8 ]

{

" \3 h/ y  x+ }; F0 D0 ]* Z: e

    int result = usb_register(&usb_kbd_driver);/*注册USB键盘驱动*/


6 F% }2 c( g8 h

    if (result == 0) /*注册失败*/

2 U% c) c  e0 O; M) p% z- S% {; j

    info(DRIVER_VERSION ":" DRIVER_DESC);

  ^) \1 i$ v8 S5 ]* |/ v8 L4 I

    return result;


5 N+ }, j! q/ `( l: o  a }
* j) z0 {" I- _( ~ 7.    编写模块卸载函数(每个驱动都会有一个卸载函数,由module_exit调用)
4 U& Z$ A' ~& u8 i

/* 驱动程序生命周期的结束点,向 USB core 注销这个键盘驱动程序。 */

, a1 V2 i4 K& ?$ D' S' p

static void __exit usb_kbd_exit(void)

% d4 i  R+ c* B( m1 m* M3 n

{

; g: e4 c* M: F

    printk("SUNWILL-USBKBD:usb_kbd_exit begin...\n");

) {% g5 j, z8 l/ P6 ^% V5 d& T

    usb_deregister(&usb_kbd_driver);/*注销USB键盘驱动*/


7 h, I% K* i% b1 j }
2 b' e; L( a! z7 s4 A9 T4 ? 8.    指定模块初始化函数(被指定的函数在insmod驱动时调用) ( k* @0 A2 f+ ]4 [! g
9 U0 `! `9 |! S
module_init(usb_kbd_init); " C0 S0 s/ C# q

/ f8 C& S. h" v7 d) R 9.    指定模块退出函数(被指定的函数在rmmod驱动时调用)
* a! G: O) N- o/ f7 @2 M 9 [- E* J' ]5 h9 `: J' O1 ]
module_exit(usb_kbd_exit); 2 q( R( p" ~) p- K

: M# l& G. \  I3 q) U 10.    编写中断请求处理函数:
" o( Z9 k5 }2 C! E0 V! I 4 `  x) a' R! G

/*中断请求处理函数,有中断请求到达时调用该函数*/

4 ?" c" ~! t3 ?; r/ J

static void usb_kbd_irq(struct urb *urb, struct pt_regs *regs)

6 \7 g; s0 P- H. p" R% a

{


5 c5 k( s% e% l0 Q

    struct usb_kbd *kbd = urb->context;


7 E+ O, j. V. W/ z

    int i;


% n7 y  J; @8 ?1 ~" \: ?* |1 b" L2 q

      switch (urb->status) {


$ d% D0 B3 U9 U% b0 a+ {

    case 0:       /* success */

, F1 e' H5 V, F9 j/ e

        break;


8 p8 c$ N. ~9 _8 P/ j

    case -ECONNRESET: /* unlink */

# @* z  B  P) ]0 Z) `7 \* ?' ]

    case -ENOENT:

. G: u* T- t/ g/ K$ z

    case -ESHUTDOWN:

& M4 M: x4 i: ~9 i3 E3 j6 M4 w8 {

        return;


; k1 |3 y4 Y0 i5 Y7 y

    /* -EPIPE: should clear the halt */

5 r+ t, }# X$ I1 O

    default:   /* error */

$ \9 p# Q  Z% }, Q

    goto resubmit;


5 m- t' h) |6 `7 T) ?

    }

4 U* E! A/ S: r3 _9 |5 R

; U. B3 }- i: E3 x

    //input_regs(kbd->dev, regs);


9 {4 R" A: J5 R. A1 N* G   s* X! W2 S1 h

    /*不知道其用意, 注释掉该部分仍可正常工作*/

; m# ~. R7 d/ l/ G$ ~* i

    for (i = 0; i < 8; i++)/*8次的值依次是:29-42-56-125-97-54-100-126*/

- K1 M8 e% N( |6 X6 y  h; Z

    {

5 q) ~% e- b$ E7 O" [; T

    input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);


5 ~9 H  Y9 |3 I6 G. Y3 K* X. b

    }

, S' p9 R# [3 ]2 j7 R  V

   


1 u) u; Y5 }! y7 i

/*若同时只按下1个按键则在第[2]个字节,若同时有两个按键则第二个在第[3]字节,类推最多可有6个按键同时按下*/


9 I3 j1 S0 W! r3 f0 N

    for (i = 2; i < 8; i++) {


( @) J  ^; R5 ~1 B: k4 H7 @

    /*获取键盘离开的中断*/


. R. v. ~6 l. B) S( y. Y: h- C) w7 r

    if (kbd->old > 3 && memscan(kbd->new + 2, kbd->old, 6) == kbd->new + 8) {/*同时没有该KEY的按下状态*/

; Z; C% X- I2 \+ |  c

        if (usb_kbd_keycode[kbd->old])

* L! H# K- [# m+ j" W$ R" j

        {

& x& t/ N/ ]" R# N% t, y3 v

        input_report_key(kbd->dev, usb_kbd_keycode[kbd->old], 0);


$ N& U) s# w: n8 @  w

        }


0 H* U# j8 i. |3 S, V

        else


* x7 R/ n8 J. {) ^  n- D0 s

          info("Unknown key (scancode %#x) released.", kbd->old);

5 ~6 q# \, @' o* o! X4 n7 _: V

    }


7 p! g9 Q  J8 ]% a
/ V/ V1 R+ ]2 y& n1 ^8 r

    /*获取键盘按下的中断*/


. Z5 D. s. ]& c. W& @& D  A- M

    if (kbd->new > 3 && memscan(kbd->old + 2, kbd->new, 6) == kbd->old + 8) {/*同时没有该KEY的离开状态*/

+ }" ]3 o- O7 W; |  Q

        if (usb_kbd_keycode[kbd->new])


6 E4 N! U. J3 w6 G

        {


: _" d' F2 F* p) c  p

          input_report_key(kbd->dev, usb_kbd_keycode[kbd->new], 1);

( c5 o) ?' J' K/ Y  t! M, @

        }

6 T5 _3 |( \! j9 Z! C

        else


2 o  b) @8 n0 _0 A' f  }( J. V  ^$ B& B* P

          info("Unknown key (scancode %#x) pressed.", kbd->new);


3 V" c, k& d/ D" a; Q! o- C5 K  Z

    }

. y( ~' ]9 P4 z0 j9 ~3 F

    }

/ u0 p' a) E' q9 p5 i/ n0 r6 M

    /*同步设备,告知事件的接收者驱动已经发出了一个完整的报告*/

3 m+ W4 T2 L2 H

    input_sync(kbd->dev);

0 m# t5 M5 m  S, Y/ v- x

    memcpy(kbd->old, kbd->new, 8);/*防止未松开时被当成新的按键处理*/


7 D- @% A5 M+ p! y' k

resubmit:

/ @5 J" _9 N2 P7 j! B) J% e* W) z+ b

    i = usb_submit_urb (urb, GFP_ATOMIC);/*发送USB请求块*/


1 X7 O% u) ~. J$ Q$ |

    if (i)

& R/ u" G/ d5 f% e

    err ("can't resubmit intr, %s-%s/input0, status %d",

, k8 w% x: J# i( N8 F

        kbd->usbdev->bus->bus_name,

: o/ C9 z* g1 r" X; x' I& G

        kbd->usbdev->devpath, i);

) M0 k$ D( ?5 L! p% ]
} 4 U! \- w9 T; r8 c0 p: i( b
11.    编写事件处理函数:
/ d. I# R5 c" M3 X8 w+ H

/*事件处理函数*/

# V' z9 H0 {: n7 F/ y+ ~; B0 k

static int usb_kbd_event(struct input_dev *dev, unsigned int type,

% W+ {; f' A- ~( D

      unsigned int code, int value)


  M# b  n3 e  L9 n& c' ?

{

# S% i; V5 Z3 Z7 H, J1 W

    struct usb_kbd *kbd = dev->private;


5 _! G' N2 J% W  c9 @6 e

if (type != EV_LED) /*不支持LED事件 */


# x6 k* [  ~7 w% f: B

    return -1;


" R0 A, U) l* E0 j1 ]1 }

    /*获取指示灯的目标状态*/

% C8 k6 r6 ]7 x0 U# y

    kbd->newleds = (!!test_bit(LED_KANA,   dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |

" w( o4 ^$ n2 Q2 [- Z

      (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL,   dev->led) << 1) |


1 F# c6 W( R/ M6 D

      (!!test_bit(LED_NUML,   dev->led));


9 o, U! e. n& d' W$ [3 q/ a: g 4 ^$ C( ^/ e, |9 A

    if (kbd->led->status == -EINPROGRESS)


% I8 b2 C- G0 r6 P

    return 0;

  X2 o  V  A' |* c% y- p
' v3 u8 `9 B" I! r

    /*指示灯状态已经是目标状态则不需要再做任何操作*/

6 U- ?2 i8 n8 E  K

    if (*(kbd->leds) == kbd->newleds)

9 Z9 |1 A, o5 K6 n

    return 0;

0 C6 M6 y2 L) F0 L( }9 W

3 R" g# _9 W" d3 @1 w5 @$ B2 E" z

    *(kbd->leds) = kbd->newleds;


, s  k+ _, L% T# u

    kbd->led->dev = kbd->usbdev;

) o, {7 Y# i4 C

    /*发送usb请求块*/


7 Z' u, b- C- Y8 A: @. [3 r

    if (usb_submit_urb(kbd->led, GFP_ATOMIC))


. R1 X: c8 o: U) {! s" T

    err("usb_submit_urb(leds) failed");


6 D- V5 d3 k. x2 T: B# n

    return 0;


$ C0 ~9 Y$ c! G$ p! W } $ ^/ M+ z5 t# J
12.    编写LED事件处理函数:
, g8 x3 D% n: s) H* g* A

/*接在event之后操作,该功能其实usb_kbd_event中已经有了,该函数的作用可能是防止event的操作失败,一般注释掉该函数中的所有行都可以正常工作*/

6 {9 x& i* C% q- W! H# Y% V

static void usb_kbd_led(struct urb *urb, struct pt_regs *regs)

& @2 ]/ q& J0 z4 c. R/ {8 ~

{

2 x. f' K( L4 u+ m; Y* O2 z- g: j5 O

    struct usb_kbd *kbd = urb->context;

+ p; L; {) o7 W8 N

2 V; G# s! O8 H2 }' V9 c' H4 U8 C

    if (urb->status)

; w! S& N- S" Q3 `* [8 b

    warn("led urb status %d received", urb->status);

7 M, ?, B$ [! f4 X5 _

3 r+ P: t6 x$ J2 ?3 F$ l6 ?

    if (*(kbd->leds) == kbd->newleds)/*指示灯状态已经是目标状态则不需要再做任何操作*/


; i- H0 q6 @: M# I

    return;

% W0 N" O) e4 C5 ^8 O8 S

  j6 a9 g9 ?, S, g  d) S+ k7 @' R

    *(kbd->leds) = kbd->newleds;


, L& h8 G9 z7 J- {5 |- F

    kbd->led->dev = kbd->usbdev;

) K. `7 {( b4 [! R/ l7 z

    if (usb_submit_urb(kbd->led, GFP_ATOMIC))


5 E. w% e" _/ _3 g# N5 I

      err("usb_submit_urb(leds) failed");


9 g6 b8 q: M! E. B6 B* Y }
# d: n' ~7 k4 I( o& L* ^, o# H 13.    编写USB设备打开函数:
, `+ r) u2 C" O3 f5 ]9 u, j

/*打开键盘设备时,开始提交在 probe 函数中构建的 urb,进入 urb 周期。 */


' O9 M0 c; D- q% Z. @

static int usb_kbd_open(struct input_dev *dev)


$ e- Q. |1 M% T/ `' V' E

{


* B( s3 M7 l4 Z. G4 O8 y

    struct usb_kbd *kbd = dev->private;

' f: e$ q8 ~' Q9 }' r- `. S

    kbd->irq->dev = kbd->usbdev;

9 j) J9 X/ l: p

    if (usb_submit_urb(kbd->irq, GFP_KERNEL))

, Q1 l  B+ U1 Y7 i" G1 k

      return -EIO;

$ E) _7 i# b. V2 L

    return 0;

3 W8 f2 F+ Q& y4 j/ n& M- F
}
$ D8 T9 M( D' ?; X! ]$ ^ 14.    编写USB设备关闭函数
1 R/ i( Z5 F% [# o' c

/*关闭键盘设备时,结束 urb 生命周期。 */

! s1 b4 j- l( g! W. V0 X/ K

static void usb_kbd_close(struct input_dev *dev)

; s3 A% q2 B$ z5 u+ q. o1 s

{

$ p; L- V. z9 b$ Y$ p% R# C' `

    struct usb_kbd *kbd = dev->private;

7 s4 ]3 m4 F' o) v& {1 m+ T

    usb_kill_urb(kbd->irq); /*取消kbd->irq这个usb请求块*/

$ }! ?/ ^# W6 u$ z
} - N0 t: v9 \: d5 w+ @+ B: E
15.    创建URB - i: w$ z8 v1 c# v" Z% Z6 c

/*分配URB内存空间即创建URB*/


0 O% V. Y2 n# c/ {7 M2 C

static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)


$ {4 d! G8 K# {5 `  U

{


2 M( D. d4 C: i

    if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL)))

' O/ w# M  g+ L" x* H0 y

    return -1;

+ O# B6 S( G) b

    if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL)))


0 `( ?7 l( W. p/ _1 m' A& P

    return -1;


3 g6 v* o; Q* c3 g  G) S; n, J6 J" k

    if (!(kbd->new = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &kbd->new_dma)))


+ z% k1 ^$ c5 s" u( `) [4 m

    return -1;

; A. M  i" n: y5 J( L4 F# C

    if (!(kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), GFP_ATOMIC, &kbd->cr_dma)))


3 q( l+ y, h# R* F' F

    return -1;

9 I' L0 P/ U- U! n/ ~! M- n

    if (!(kbd->leds = usb_buffer_alloc(dev, 1, GFP_ATOMIC, &kbd->leds_dma)))

) X) Q' c6 I7 e+ N3 E( \

    return -1;

6 Q7 C( {9 E4 d9 q* f) W

    return 0;


# ]% b8 Q5 K4 m) H4 j4 t+ F }
- F  _4 [$ ?6 D 16.    销毁URB 6 b0 l% b* G4 f

/*释放URB内存空间即销毁URB*/


' U  ], E# q  R# _* U8 u8 Q

static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd)

  j! k8 ]3 V/ b

{

) t. ]4 c  _5 S( t) A  F% X

    if (kbd->irq)


1 ]8 @  ~! g2 ?  Q% c6 i

      usb_free_urb(kbd->irq);


' v. y$ _! ?+ B; B5 o2 S

    if (kbd->led)


: ?1 D. m# A* L9 `$ o

      usb_free_urb(kbd->led);


' M. \/ e( P1 D) F8 m5 O# F

    if (kbd->new)


8 A/ @+ n' f# H8 x

      usb_buffer_free(dev, 8, kbd->new, kbd->new_dma);

* O; ?, k9 t' Q: [) ~2 U7 X

    if (kbd->cr)

( s! h' r0 w3 r( k2 X3 M

      usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma);

4 E. n: r+ i. U; s: n, Q6 ~

    if (kbd->leds)


# p. R" Z4 A: b$ Q6 ]

      usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma);

& W: h. [% ]7 I

}

) s# T( Y$ a/ n' U
17.    USB 键盘驱动探测函数:
* m3 |$ p! J5 v1 N0 O

/*USB键盘驱动探测函数,初始化设备并指定一些处理函数的地址*/


. V/ v& D* Z- ~6 H& s

static int usb_kbd_probe(struct usb_interface *iface,

2 j" _& ?6 u( s! ]

        const struct usb_device_id *id)


( o9 f' L/ c+ \% M" J

{


" y3 D8 X& E* e$ S

    struct usb_device *dev = interface_to_usbdev(iface);


, _( u8 x9 K4 @" C# s, O$ u' t

    struct usb_host_interface *interface;

$ C- r. [4 D) u# z+ S0 |

    struct usb_endpoint_descriptor *endpoint;


6 ?# h) [7 a9 u# f) c

    struct usb_kbd *kbd;


  B! S; {% i/ N  ^, F' ~. C

    struct input_dev *input_dev;


. ]' \; n& W# S& o

    int i, pipe, maxp;

0 ]) w/ o; D4 Y8 P7 H7 m

    /*当前选择的interface*/


; }0 f" P- h' m+ ~0 }7 N; O

    interface = iface->cur_altsetting;


; e  H; U5 f# H2 P' a6 S# d7 {

    /*键盘只有一个中断IN端点*/


3 w1 t& A5 U  t8 `7 q0 P

    if (interface->desc.bNumEndpoints != 1)

: l, C! [8 \7 w$ J

    return -ENODEV;


  \. g' a( U7 X

    /*获取端点描述符*/


; o2 P& _0 Y" r8 m2 O' ?- j

    endpoint = &interface->endpoint[0].desc;


. J* ^. s9 q0 v1 {  r6 X: f

    if (!(endpoint->bEndpointAddress & USB_DIR_IN))


. f4 u; ~: v$ V' K

      return -ENODEV;

$ d7 u0 d/ o+ v$ D6 ]

    if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT)


- @; c. p7 Z" O: N! }

      return -ENODEV;

7 O  s- X- T6 L6 F: |

    /*将endpoint设置为中断IN端点*/


8 s# Q, V8 g6 q' ]  H  C3 D

    pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);

. G* f0 U4 B5 d2 E6 x/ G; f

    /*获取包的最大值*/

/ q3 i! |1 k& p. x2 X! G: V

    maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));


( ]; {1 F7 g8 ]# S3 j) E# R

    kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);

- x" r% N7 h' t

    input_dev = input_allocate_device();


  y0 n# P" X" W( e  D' e

    if (!kbd || !input_dev)

1 c! y* H' [# F) p- |7 W. Y4 `

      goto fail1;


; |2 `! j# A1 _4 I8 X, D8 C
- d! z& T4 h* r, \0 s1 h4 D! b# _

    if (usb_kbd_alloc_mem(dev, kbd))

' C+ h; ?- @$ _7 U

      goto fail2;

( N1 j; H  ?7 X+ a

    /* 填充 usb 设备结构体和输入设备结构体 */

4 v4 g: y- C. M7 \- j: {

    kbd->usbdev = dev;


8 a$ v( D4 }. B. [) p. L) S* C7 V, b

    kbd->dev = input_dev;

0 ^& c, J* g/ w5 j( `4 }0 T2 I
1 g" Y* }0 R9 {5 x3 o# Z

    /*以"厂商名字 产品名字"的格式将其写入kbd->name*/

$ W: q- A% I; f' {1 M

    if (dev->manufacturer)

+ {9 q; Q" ?; E+ A: H

      strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));

. X" i% U- s* `
/ B8 {6 p1 ~  H% j6 \9 Q/ g3 b8 p1 x

    if (dev->product) {


9 d  u' H) N6 {' L

    if (dev->manufacturer)


; z" \, B/ \* y, V/ h  W

        strlcat(kbd->name, " ", sizeof(kbd->name));

% J2 s+ m- j/ D8 ?: \

    strlcat(kbd->name, dev->product, sizeof(kbd->name));

+ b, M: t4 Y) b

    }


2 d5 P$ e4 z# C) K! d7 ^: J
$ k+ O$ }& |; M, W0 A# H/ `8 M0 Q$ W

    /*检测不到厂商名字*/

# q8 U) X- Q9 M  X

    if (!strlen(kbd->name))

: d, R- C/ Z8 J; K( ]5 ?7 i6 ]

    snprintf(kbd->name, sizeof(kbd->name),

: W! j- F% T  p9 n7 W

        "USB HIDBP Keyboard %04x:%04x",

+ X& j/ ]; K! R/ z$ D/ e0 D

        le16_to_cpu(dev->descriptor.idVendor),

0 ~0 t* D2 W0 Y; o  ?* j9 d

        le16_to_cpu(dev->descriptor.idProduct));


5 l5 p8 C4 ]8 J9 G

    /*设备链接地址*/

3 O0 I0 [+ u4 R! [' ~0 K

    usb_make_path(dev, kbd->phys, sizeof(kbd->phys));

# E' Z9 l  B% ]% H# r

    strlcpy(kbd->phys, "/input0", sizeof(kbd->phys));     

. g2 f: I; h4 c# i

    input_dev->name = kbd->name;


$ N9 q3 o7 V& z: K, x. h
0 ^! l) ~" b2 O; h. r2 N

    input_dev->phys = kbd->phys;

7 f/ f* u* m% L7 a' {

    /*


& B# f" ^. J' z+ P* c& p: J) N

* input_dev 中的 input_id 结构体,用来存储厂商、设备类型和设备的编号,这个函数是将设备描述符


: [: J9 V* R  G' n+ o+ M! r

    * 中的编号赋给内嵌的输入子系统结构体

) Z* c3 ^% L7 M' `& m

    */

- ~: c" l9 Q  s5 ]- U$ g

    usb_to_input_id(dev, &input_dev->id);

' a& a/ L) f; D$ R% @  c0 C

    /* cdev 是设备所属类别(class device) */


/ d1 a) J4 O  j1 Z' M" d

    input_dev->cdev.dev = &iface->dev;


' ~6 T) r9 Z6 o. h. ?

/* input_dev 的 private 数据项用于表示当前输入设备的种类,这里将键盘结构体对象赋给它 */


, l9 x* `2 }9 e+ H0 o, Z5 w

    input_dev->private = kbd;


+ e, c$ _* H. [( G- ?

input_dev->evbit[0] = BIT(EV_KEY)/*键码事件*/ | BIT(EV_LED)/*LED事件*/ | BIT(EV_REP)/*自动重覆数值*/;

  T' }5 {3 }! i. i- N

input_dev->ledbit[0] = BIT(LED_NUML)/*数字灯*/ | BIT(LED_CAPSL)/*大小写灯*/ | BIT(LED_SCROLLL)/*滚动灯*/ | BIT(LED_COMPOSE) | BIT(LED_KANA);


; t6 J/ N3 R( O* r8 W ) R/ ?7 U! @4 n* L( p4 `

    for (i = 0; i < 255; i++)


& a( h8 S4 K4 l1 S4 H. m

    set_bit(usb_kbd_keycode, input_dev->keybit);

1 v' ?3 q2 a1 B! H: s

    clear_bit(0, input_dev->keybit);

/ c( j" `. R& T" A& [9 V. C

4 x4 p6 H9 [% e( p

    input_dev->event = usb_kbd_event;/*注册事件处理函数入口*/

8 D2 S7 u4 r/ {! r9 S

    input_dev->open = usb_kbd_open;/*注册设备打开函数入口*/


, p6 y  R( O1 L- q9 A& G: L$ @

    input_dev->close = usb_kbd_close;/*注册设备关闭函数入口*/

' G4 a1 @1 R4 L' c$ t0 ]/ _
" Q) Q# f; q8 A) Q- y* T

    /*初始化中断URB*/


4 r1 G* u% t7 N4 m! W! J

    usb_fill_int_urb(kbd->irq/*初始化kbd->irq这个urb*/, dev/*这个urb要发送到dev这个设备*/, pipe/*这个urb要发送到pipe这个端点*/,

4 G6 U* ~: k4 q( [+ O, |4 l

        kbd->new/*指向缓冲的指针*/, (maxp > 8 ? 8 : maxp)/*缓冲长度*/,


- p% |+ ]& I7 c- P

        usb_kbd_irq/*这个urb完成时调用的处理函数*/, kbd/*指向数据块的指针,被添加到这个urb结构可被完成处理函数获取*/, endpoint->bInterval/*urb应当被调度的间隔*/);

* E2 ^+ x- o* _

    kbd->irq->transfer_dma = kbd->new_dma; /*指定urb需要传输的DMA缓冲区*/


# D% ?  Q2 t* z

kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;/*本urb有一个DMA缓冲区需要传输*/

- ?/ x1 x! h  v% w/ S# g  I

    kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;/*操作的是类接口对象*/

+ U: l4 [- v8 ^# {4 ~8 U- S3 Z

    kbd->cr->bRequest = 0x09; /*中断请求编号*/


4 H8 ~$ q! F1 D; T. ]$ t

    kbd->cr->wValue = cpu_to_le16(0x200);


8 ?+ C5 d' ^; [# N

    kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);/*接口号*/


1 r5 C. p% ^0 q# M6 K9 `% S

    kbd->cr->wLength = cpu_to_le16(1);/*数据传输阶段传输多少个bytes*/


, Q- a" B% x' Q! \
: ~; v( D: a; p+ t

    /*初始化控制URB*/

# o' y- a7 j1 b! i- j, ^

    usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),


9 c% ]0 }$ H) O$ @

          (void *) kbd->cr, kbd->leds, 1,

2 E  U6 I) T+ ?! `& K

          usb_kbd_led, kbd);

6 d) `1 O7 G& f

    kbd->led->setup_dma = kbd->cr_dma;


+ P7 C" ]# g1 ^1 E2 N2 j& H

    kbd->led->transfer_dma = kbd->leds_dma;

3 A0 G) K& X5 B2 m) r- W

    kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP/*如果使用DMA传输则urb中setup_dma指针所指向的缓冲区是DMA缓冲区而不是setup_packet所指向的缓冲区*/);

* P1 P3 o( D; t4 r

4 M3 @9 @' N# J

    /*注册输入设备*/

( |. d5 b) K4 I* `8 Z6 }! [' R

    input_register_device(kbd->dev);

& `6 k% Z1 I0 v4 s

; w8 c$ v3 U6 \+ d2 c" H1 i! H

    usb_set_intfdata(iface, kbd);/*设置接口私有数据*/


1 M' ^# U: z2 t/ n7 l2 c) I2 n+ t6 P

    return 0;

# H, {" s7 s3 ~% c  M7 |$ k
; x5 ?$ u% s$ F" B1 a

fail2:   usb_kbd_free_mem(dev, kbd);

  X& _: l5 g3 s! i6 E

fail1:   input_free_device(input_dev);


& t% A4 g; \) e5 b) y

    kfree(kbd);


# q3 u+ T3 z6 d( S  C2 K

    return -ENOMEM;

/ y) N1 |3 p, q! q
}
: z, R/ Y( _+ L0 C* ?$ v 18.    编写断开连接的函数:
3 Q( L3 j( |3 n7 [

/*断开连接(如键盘设备拔出)的处理函数*/

( E, v1 R( q9 [9 Q& {: B

static void usb_kbd_disconnect(struct usb_interface *intf)

0 y. w* |( N: h

{

$ Z8 T, r8 e8 S2 h' x

    struct usb_kbd *kbd = usb_get_intfdata (intf);/*获取接口的私有数据给kbd*/


4 x% A: P/ r; g' F. \. L! h$ s

    usb_set_intfdata(intf, NULL);/*设置接口的私有数据为NULL*/

8 y* p0 `! l: j  X% e

    if (kbd) {


9 @" `3 P9 [$ Z+ D& V

    usb_kill_urb(kbd->irq);/*取消中断请求*/


/ ?- W, w5 ]  L8 Y: l

    input_unregister_device(kbd->dev);/*注销设备*/


: b1 A3 }; v7 Q+ g$ x

    usb_kbd_free_mem(interface_to_usbdev(intf), kbd);/*释放内存空间*/

4 U! Q8 v/ V1 R) [4 m, j3 m

    kfree(kbd);


4 a1 i# }. L3 S

    }


8 H7 p) x; ?- l, P$ x }
2 T  \( d7 S8 E" b) l* t3 b9 v 19.    编写Makefile: * k. Y9 y  K2 `

##############################

2 _2 `4 ?" D$ G* i! ~5 g& B% z. s

#usbkdb Makefile for linux


3 s0 V1 ?" Y# l! Y  w5 a/ r: r

##############################

3 ]0 |! W3 m  c9 r, f" d3 {$ p3 O

obj-m:=usbkbd.o


+ W$ J* I, l0 H$ }) C: v* D

KERNELDIR ?= /lib/modules/$(shell uname -r)/build


+ _# Q6 U% |3 O1 \

PWD:=$(shell pwd)

) C( J0 ?' F: b0 G* b  m

default:


' p  G/ @4 U, F! g1 z! l $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
2 ?9 {4 {  W% |2 l ! t) ], }0 W# A3 Y- L7 g4 a

: |* r/ O- r2 M  f$ V) I4 h 5 [* z9 t' X! ^% r3 ^2 ]
6 G1 W- z% M8 S2 p
usbkbd.rar 1.    指定USB键盘驱动所需的头文件:
0 ], K9 j! f; h/ T5 X+ J% h# h# P
/ i# U; Z2 Y0 A+ p' u# Q9 M3 G1 ^

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


! |) C+ Z5 b6 b$ g' E. p% C

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

! ]: _* Z# N) y# K$ i! W

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


8 p3 v1 ~$ c: b0 U  L) C/ v/ u! h4 A

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

) U# j3 m: m% I, W' F6 O( i9 f

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

& |2 D0 |: r- a9 c* N. l: [% O* f9 b

#include <linux/usb.h> /*USB设备相关函数定义*/


$ {# a; y7 O8 F1 ~* r) J3 r" ~# \ 2.    定义键盘码表数组:
* U' |6 s& B8 N' _/ J2 ^& P

/*使用第一套键盘扫描码表:A-1E;B-30;C-2E*/

! X* g; T/ P3 T9 C* Y  b& w; }

static unsigned char usb_kbd_keycode[256] = {


3 p  @) U" z  {9 z- O3 Y2 b. v; F

    0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,


6 A' C: O1 q) D, D: Q7 I$ B

    50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,

; z) L' z" p6 {" J  W1 d

    4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,


& A7 l8 Q+ \2 m2 ?% H4 z

    27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,

  {- {. E, @  d' \+ E

    65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,


. Z8 ~6 `( v' J) s- `7 x$ p

    105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,


$ o$ X, t9 |9 ]0 ~& n$ j

    72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,

; B" A5 |- `9 m$ E1 l

    191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,

# q! b2 ~$ Q& C$ ]+ X2 \6 x5 z  L

    115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,

1 Q8 y1 X8 n6 k7 x+ R& j

    122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


- [7 i# \$ k* o) h) b0 l

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

$ B. Y9 l* A( \5 C8 C# ]7 s

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

; U( {4 M2 u( ?9 M* v& @' n

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

- x5 O" c* u: q% u0 z; G, e

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

4 D  B- P2 V8 y

    29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,

; L9 b9 _) }* A. ]2 R; N7 U

    150,158,159,128,136,177,178,176,142,152,173,140


8 N& ^: F4 L5 Z* q7 J }; ; `* b. k+ I4 ]
3.    编写设备ID表: - O4 ]% L- d* s- o+ \

static struct usb_device_id usb_kbd_id_table [] = {


+ m/ A% w6 Y% r4 h' V

    { USB_INTERFACE_INFO(3, 1, 1) },/*3,1,1分别表示接口类,接口子类,接口协议;3,1,1为键盘接口类;鼠标为3,1,2*/


/ j2 Q8 C5 w% G- X, C* o

    { }           /* Terminating entry */


1 v- [( h6 D1 ~5 j* `- a! R8 h

};

+ q/ @0 L+ W$ u6 \' E# P
MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);/* 指定设备ID表*/
) b; C  R" X' x8 _ 4.    定义USB键盘结构体: ! j6 F+ T; ?& {7 V  k( @( z  `

struct usb_kbd {

8 G: G7 O; |2 @0 D+ e4 S

    struct input_dev *dev; /*定义一个输入设备*/


, f/ t2 u, l; j2 ~9 |1 i

    struct usb_device *usbdev;/*定义一个usb设备*/


" k) `; e8 y8 V2 r5 T; E

    unsigned char old[8]; /*按键离开时所用之数据缓冲区*/

, \- f5 o4 m: |

    struct urb *irq/*usb键盘之中断请求块*/, *led/*usb键盘之指示灯请求块*/;


7 {, L+ M" z, D

    unsigned char newleds;/*目标指定灯状态*/

; _, w/ u: `9 Z- |6 L( ~; d" F' Z

    char name[128];/*存放厂商名字及产品名字*/

) Y- R# x7 d) ?6 h1 t

    char phys[64];/*设备之节点*/


$ o6 H7 y& Z) C5 N) m0 D' B
+ j6 y+ y% K& a6 C: Q) W

    unsigned char *new;/*按键按下时所用之数据缓冲区*/


! [: c8 Q2 v* M5 J( J

    struct usb_ctrlrequest *cr;/*控制请求结构*/

4 X5 g: v8 @3 ^/ o

    unsigned char *leds;/*当前指示灯状态*/

7 A0 x/ ?9 O9 Y: r/ Q2 z

    dma_addr_t cr_dma; /*控制请求DMA缓冲地址*/


9 k% j) p1 }4 [* f* S

    dma_addr_t new_dma; /*中断urb会使用该DMA缓冲区*/


" c; F0 P  \" t1 @4 T& C* n+ I

    dma_addr_t leds_dma; /*指示灯DAM缓冲地址*/

  `) f( z+ }! f) K4 D0 y
};
8 J% T9 o# R! p$ Q3 v 5.    编写USB键盘驱动结构(任何一个LINUX下的驱动都有个类似的驱动结构)
" e. Y/ S( G, F4 |

/*USB键盘驱动结构体*/

$ z5 h2 x/ R7 B1 L1 G2 e

static struct usb_driver usb_kbd_driver = {

% l. P9 b$ p1 D6 K; a% D

    .name =   "usbkbd",/*驱动名字*/

6 S6 ^1 J! j0 h$ i9 m

    .probe = usb_kbd_probe,/*驱动探测函数,加载时用到*/

: `2 E/ V& I' N; Z- `+ l

    .disconnect = usb_kbd_disconnect,/*驱动断开函数,在卸载时用到*/


5 l2 g) C: e+ p

    .id_table =   usb_kbd_id_table,/*驱动设备ID表,用来指定设备或接口*/


$ B5 d% [2 w1 q5 f; Z

};

/ o, p9 m4 ^. T5 s& x
6.    编写模块加载函数(每个驱动都会有一个加载函数,由module_init调用)
3 p) E5 N. }3 w# F) G

/*驱动程序生命周期的开始点,向 USB core 注册这个键盘驱动程序。*/

6 C* }! @+ t* y! V8 M% y' W

static int __init usb_kbd_init(void)


" E6 E* h, u' d+ T. h9 n

{


  c: G: y# f, G. K$ h' d# K, o& U  i' C

    int result = usb_register(&usb_kbd_driver);/*注册USB键盘驱动*/


1 B/ N- D- U2 @$ d8 O

    if (result == 0) /*注册失败*/


* B1 E1 T% k7 r( G5 ]5 ]3 A5 _1 W  |

    info(DRIVER_VERSION ":" DRIVER_DESC);


  j% k8 ^4 h/ _0 ~

    return result;


! @; F. ?: c9 F } 9 a, G2 g  X) Z  U. D+ i
7.    编写模块卸载函数(每个驱动都会有一个卸载函数,由module_exit调用) % G8 s/ j8 z: A8 R+ ^( t

/* 驱动程序生命周期的结束点,向 USB core 注销这个键盘驱动程序。 */


8 E- q% K; r- k8 \3 r1 H$ i; {

static void __exit usb_kbd_exit(void)


; |! W  j% u* C: `% L1 T9 y  y$ @

{


, I  e/ B: R0 D4 G) a

    printk("SUNWILL-USBKBD:usb_kbd_exit begin...\n");

3 I( L* J* h4 M) k/ p+ V# b# V

    usb_deregister(&usb_kbd_driver);/*注销USB键盘驱动*/


- p" d6 n+ N7 D9 ?! Z& k }
7 _' B; ]' y; x: B; x9 Q 8.    指定模块初始化函数(被指定的函数在insmod驱动时调用)
5 m0 M8 r; }. ~, a+ h 8 \2 l8 ~' x& n' V. S
module_init(usb_kbd_init);
% }4 [2 s7 ]7 a1 q2 ?/ b: m& E1 v
! K: V, _) g  E  [& | 9.    指定模块退出函数(被指定的函数在rmmod驱动时调用) : o/ n* o2 j9 l* q, o

! M9 K; R4 W8 m0 _, L8 W' M  r' ~% F module_exit(usb_kbd_exit);
, n+ _& {2 `* o$ T 0 }0 z1 M% A% k  }) F( m
10.    编写中断请求处理函数:
3 G1 H! l# L& E
1 t& e7 K7 A5 w" v0 o5 Y+ K

/*中断请求处理函数,有中断请求到达时调用该函数*/

4 q5 W1 w0 g) s4 W5 P

static void usb_kbd_irq(struct urb *urb, struct pt_regs *regs)


, A3 ~; U( Q5 s: R' G1 u9 [: I* T

{

& a) K* g6 S2 v, H2 w6 C* M

    struct usb_kbd *kbd = urb->context;

# ?; r3 K' T- \, \

    int i;

- L7 t& h. \9 E$ i1 [) i% V

      switch (urb->status) {


6 J2 k: A8 E0 j, l) T

    case 0:       /* success */


8 P4 g* Z4 `# k

        break;

$ Z- I, b3 r9 g) c  B

    case -ECONNRESET: /* unlink */

: J5 `1 Z; }7 r# Y8 w/ I

    case -ENOENT:


+ |( q! U" R4 O1 q" Z5 w$ Q% \

    case -ESHUTDOWN:

0 G1 |" Y# V; o

        return;


0 s4 Q8 D; l; w/ R1 v4 m. Q

    /* -EPIPE: should clear the halt */


7 u( E1 D) [7 p7 i" d+ H% B+ z8 Z

    default:   /* error */

% q' \$ ?0 ?8 ?

    goto resubmit;


3 A9 E9 t9 `6 h* B& E) R* H

    }

" b# _0 h+ B- @( ]/ D$ N
; k" Z1 Q: R/ ~) i: y

    //input_regs(kbd->dev, regs);


; [# i/ \6 `3 j
* x; m7 S4 ~6 i

    /*不知道其用意, 注释掉该部分仍可正常工作*/


4 H6 y! b, ]6 e# _5 n

    for (i = 0; i < 8; i++)/*8次的值依次是:29-42-56-125-97-54-100-126*/


8 i4 H* F! p6 z) {2 E- S- C

    {


' K& g" M" q3 @5 e

    input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);


- [$ C6 s4 o# O( O0 u, y, u

    }

# D! J) }' Y+ F& P2 B; E

   


1 g5 n4 u- L% ~" i% m) \

/*若同时只按下1个按键则在第[2]个字节,若同时有两个按键则第二个在第[3]字节,类推最多可有6个按键同时按下*/


- b: Y3 U0 D, Z3 G5 r

    for (i = 2; i < 8; i++) {

; e/ _- A+ o8 X

    /*获取键盘离开的中断*/


5 i' P% k5 w- [$ ^7 m/ |

    if (kbd->old > 3 && memscan(kbd->new + 2, kbd->old, 6) == kbd->new + 8) {/*同时没有该KEY的按下状态*/


- `9 }4 R: G& j+ T% e

        if (usb_kbd_keycode[kbd->old])

' }& `1 M- b4 x1 Q2 A7 P

        {

# q- [  ^# Q7 W" F3 G5 g

        input_report_key(kbd->dev, usb_kbd_keycode[kbd->old], 0);


& u$ Z7 x+ s* M5 g( B9 y) ?

        }


6 o% h+ x8 f9 E8 J4 d9 n

        else


1 x* [/ H. }) O

          info("Unknown key (scancode %#x) released.", kbd->old);


7 S0 _8 B  O0 _/ I8 U

    }


* _4 ~2 m5 s$ q* l. m1 i+ c6 d+ O
- Z. p7 h6 c. m+ p1 z+ w

    /*获取键盘按下的中断*/

  m7 p! {) D8 Z# [

    if (kbd->new > 3 && memscan(kbd->old + 2, kbd->new, 6) == kbd->old + 8) {/*同时没有该KEY的离开状态*/


! M2 u+ Q; H7 D  l

        if (usb_kbd_keycode[kbd->new])

: j; G' \: `. _( b+ ]

        {


0 o+ _+ W; F; m- P% R! y8 r4 H8 R

          input_report_key(kbd->dev, usb_kbd_keycode[kbd->new], 1);


5 ?/ p' e+ l- B4 V. M

        }

' b- n% M8 D- {; N

        else

$ B2 X- }- I* L  ?  U. D

          info("Unknown key (scancode %#x) pressed.", kbd->new);

7 M- X+ g" ^. o2 w0 v3 ?

    }


+ H1 D3 J7 m. [4 C' m

    }

& \, z1 S1 W6 Z& Q/ V

    /*同步设备,告知事件的接收者驱动已经发出了一个完整的报告*/


2 q! k9 O$ u5 b' J; K2 O+ P

    input_sync(kbd->dev);


0 C/ I( P% E7 N

    memcpy(kbd->old, kbd->new, 8);/*防止未松开时被当成新的按键处理*/


# M" y% l  N1 P* l

resubmit:


: `7 d+ B# I3 n1 L/ ?  U

    i = usb_submit_urb (urb, GFP_ATOMIC);/*发送USB请求块*/

6 d0 [- D. W. k7 Q

    if (i)


9 v7 I( K9 J5 g9 f5 G9 c  X$ G

    err ("can't resubmit intr, %s-%s/input0, status %d",


: i( ~7 u& i% p4 d# R) Z7 ]4 l

        kbd->usbdev->bus->bus_name,

' c/ T, b- K3 a: ?% H+ r

        kbd->usbdev->devpath, i);

1 m  e$ X( V- v
}
  m5 S0 B* U* K! D 11.    编写事件处理函数: 0 @3 G: @% L+ b/ k1 m

/*事件处理函数*/

& V, h9 a! J1 q$ \5 [0 A

static int usb_kbd_event(struct input_dev *dev, unsigned int type,

! x! F: p. ?3 p: k" _

      unsigned int code, int value)


( P/ x, i! H* A, a8 L1 {8 D

{

' ]# f$ e' [# i

    struct usb_kbd *kbd = dev->private;


: X0 v' p6 t' r- E3 \

if (type != EV_LED) /*不支持LED事件 */


6 V! o! j" ]$ y( Q% [4 f7 c

    return -1;

5 c' ]& ^% P# [. ^. [, S, n: s

    /*获取指示灯的目标状态*/

, E1 f2 ^7 r7 R1 {

    kbd->newleds = (!!test_bit(LED_KANA,   dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |

5 w2 U8 j7 B2 J7 ^- o

      (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL,   dev->led) << 1) |


& E7 Z$ L, v  b3 I$ R0 M

      (!!test_bit(LED_NUML,   dev->led));


/ V, {2 w9 y+ {! d' n
4 e1 R5 _2 R/ m

    if (kbd->led->status == -EINPROGRESS)

- H1 h( N$ n2 b5 X

    return 0;


% I3 |0 N* a( u* p6 E
9 {3 l3 z* q$ a' e4 E

    /*指示灯状态已经是目标状态则不需要再做任何操作*/


3 _2 X' e5 \4 V; c3 s5 ?

    if (*(kbd->leds) == kbd->newleds)

% P9 {# N5 L+ ]

    return 0;

' H2 y; g& J" i) x/ V
* W8 z8 |9 k; p) k% |9 K/ E

    *(kbd->leds) = kbd->newleds;


& ]2 t9 n7 e) h* W2 X

    kbd->led->dev = kbd->usbdev;


$ L% p% t- }: b7 S

    /*发送usb请求块*/


: V4 [: {( j& X: d

    if (usb_submit_urb(kbd->led, GFP_ATOMIC))

0 z) }9 j9 j# ~# {

    err("usb_submit_urb(leds) failed");

$ H) D9 z) K( F3 O$ `7 j

    return 0;


* N: K# K: r6 a. P6 i }
# f" L: A# X1 O. V+ j4 j  `/ h1 B 12.    编写LED事件处理函数:   B$ h1 @! u& n4 B1 ~

/*接在event之后操作,该功能其实usb_kbd_event中已经有了,该函数的作用可能是防止event的操作失败,一般注释掉该函数中的所有行都可以正常工作*/


5 `# g9 J3 k6 S  [

static void usb_kbd_led(struct urb *urb, struct pt_regs *regs)


9 T6 r9 d0 k% |% ^$ |8 W5 @- K

{


; S' b' n6 F6 {+ h

    struct usb_kbd *kbd = urb->context;


" A$ b8 a: w: c9 x$ V4 O; n
0 G  I; P" J8 }4 Y, d* G

    if (urb->status)

5 n% I9 M% Q, J/ l5 F( |

    warn("led urb status %d received", urb->status);

+ C6 d: V$ n! a0 N
3 G$ ]8 {8 j6 `! y7 _

    if (*(kbd->leds) == kbd->newleds)/*指示灯状态已经是目标状态则不需要再做任何操作*/


! [: U2 ?  G9 E& ], X/ U$ C

    return;


, I$ ~# }8 f7 A! J 8 m% x5 m8 P! h! w4 u) x3 T" d* h* r

    *(kbd->leds) = kbd->newleds;

! g! S) c: e& P1 z! m. L

    kbd->led->dev = kbd->usbdev;


1 y' O+ X" e& X) D

    if (usb_submit_urb(kbd->led, GFP_ATOMIC))

, \1 R; k$ n. i/ v" @9 ~3 Q& l

      err("usb_submit_urb(leds) failed");


7 \! F. ]  j+ u! g5 ]( I }
4 k: h6 c8 C, V# N/ h 13.    编写USB设备打开函数: ; q* i! @0 s% o0 {5 t+ u! C

/*打开键盘设备时,开始提交在 probe 函数中构建的 urb,进入 urb 周期。 */


* P- V2 F7 Y3 K8 N5 Z: w

static int usb_kbd_open(struct input_dev *dev)

4 T4 X5 e- t' g- N( P8 C

{


8 u2 G! q: g! t3 T2 _

    struct usb_kbd *kbd = dev->private;

# X) V! U6 z9 o7 e

    kbd->irq->dev = kbd->usbdev;

+ h6 F! [5 M# r( Z) B. `

    if (usb_submit_urb(kbd->irq, GFP_KERNEL))


3 p1 ^; U; k! b+ J5 @( I

      return -EIO;

2 L3 ^8 {' o7 h% U6 F3 {* `1 i

    return 0;

) O, Y, y& r5 O, Q( B( }
}
# a* u6 [6 R+ Q! \8 {$ X! ~ 14.    编写USB设备关闭函数 4 ?/ n4 p; w/ y( H$ F0 x  g

/*关闭键盘设备时,结束 urb 生命周期。 */

$ k3 `4 t/ H! c9 q1 s# u

static void usb_kbd_close(struct input_dev *dev)


  ~4 U! \- K2 {1 h6 F

{

0 G- C) l" p4 B& N1 j

    struct usb_kbd *kbd = dev->private;


! g! }; J# W) j) B  u

    usb_kill_urb(kbd->irq); /*取消kbd->irq这个usb请求块*/


5 B" B4 a. i( o' `% M/ y- _& B8 m }   n1 F* V5 b- n) w7 E
15.    创建URB
* N, n+ R/ e4 I

/*分配URB内存空间即创建URB*/

! \8 ^' X' X0 m& \) K6 Q

static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)


* }* m' h- a- j! C4 ?

{

4 }0 q3 F  k2 l! J% U

    if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL)))


: `7 O+ Y: p2 e9 S  V9 u

    return -1;


- ~! _( z6 |! t( D  v6 P

    if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL)))


3 ^& i) o4 M  Y5 R, b

    return -1;

# {) {% S/ u: t3 @3 @

    if (!(kbd->new = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &kbd->new_dma)))


% e" C, S# F9 P

    return -1;

  J8 D! u/ n- S

    if (!(kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), GFP_ATOMIC, &kbd->cr_dma)))

2 [2 U0 z. g6 m5 ]/ D& T  K

    return -1;


* t- }8 {  k' e6 j0 [8 w

    if (!(kbd->leds = usb_buffer_alloc(dev, 1, GFP_ATOMIC, &kbd->leds_dma)))

5 L$ t- W7 F. q; A5 i  s4 M# ^

    return -1;

# c8 p; l7 I+ X  s' j; a8 F6 A8 H) q

    return 0;

1 E/ X9 E: I* e1 ]
} ! }3 S" F6 \( k6 e- ]1 g
16.    销毁URB
  b" J; v) K% j1 U' W- _

/*释放URB内存空间即销毁URB*/

5 O4 t8 y  [  X( z3 {

static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd)

% y+ m0 f, P) V5 |8 F

{

# I: X) p2 e0 Z+ }% |

    if (kbd->irq)


1 W& N/ G: O. l) I  U/ g/ `" ?' e, h

      usb_free_urb(kbd->irq);


) ~) g2 F) n5 ]- l9 ~2 J1 \- L

    if (kbd->led)

" n' W+ y" `( V% V0 c

      usb_free_urb(kbd->led);

; y. o+ h+ X* p; k* d  c

    if (kbd->new)

7 T. K; a% X5 A9 C" Q

      usb_buffer_free(dev, 8, kbd->new, kbd->new_dma);


+ L# A' a! B! k0 A. w( ?3 _8 T

    if (kbd->cr)


5 l" d# R! q1 l% L

      usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma);

, ?' ^/ X- d& V; B& _

    if (kbd->leds)


( I  P8 w7 Z- T1 s- R

      usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma);

( L( W/ Z1 e3 Q

}


! V- S3 |7 N  q7 g; ^* W 17.    USB 键盘驱动探测函数: & b# ?: ?0 x- x- |3 Z$ {% g3 U' V

/*USB键盘驱动探测函数,初始化设备并指定一些处理函数的地址*/

- ~4 o( D7 x9 r! _4 _5 t

static int usb_kbd_probe(struct usb_interface *iface,

9 g7 G" o; {8 ~8 X1 I+ t' U

        const struct usb_device_id *id)

0 R) Q! n! D; x; n: g

{

3 a+ E6 A/ x, q# `5 s+ T0 E! _

    struct usb_device *dev = interface_to_usbdev(iface);


  t, E( q. ?7 ?* n( e+ H) M; l

    struct usb_host_interface *interface;


0 l" v' q7 u# X" e8 r

    struct usb_endpoint_descriptor *endpoint;


! M4 L/ {9 w- \! z' x4 ]: H

    struct usb_kbd *kbd;


$ w% Z) T5 b0 m) c. {! F

    struct input_dev *input_dev;

) B! Q9 ~& `  ]! j! R. G

    int i, pipe, maxp;


# Z8 E8 d/ }7 C- h4 Y

    /*当前选择的interface*/

/ M* ]2 y$ `) m7 n* S+ z  D

    interface = iface->cur_altsetting;

& M+ H/ l. D/ U

    /*键盘只有一个中断IN端点*/


1 I0 d2 O7 F, p6 c  ?

    if (interface->desc.bNumEndpoints != 1)

2 _& p0 C: V3 G/ e+ ]' d# {+ s

    return -ENODEV;

) \. y0 `* z9 P& I9 d& u) |7 o

    /*获取端点描述符*/

9 N" R5 i* r$ y& {

    endpoint = &interface->endpoint[0].desc;


. W, [" Y  g, a. @9 X

    if (!(endpoint->bEndpointAddress & USB_DIR_IN))

% n& Z2 ^9 ]1 a, F

      return -ENODEV;


. \3 k. {# A; {  {: h9 `9 z( [

    if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT)


' b3 j. C2 S& x

      return -ENODEV;

1 {2 ]# `" _4 x

    /*将endpoint设置为中断IN端点*/

5 c, _7 s2 i6 f% Z5 i7 _

    pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);

! H$ ?, C, [9 C( Z

    /*获取包的最大值*/


3 S$ H* y; a( j5 f

    maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));


6 p8 |' q" n3 O+ W  Z7 y

    kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);

% S+ m9 L0 j* l7 D. S- b( N

    input_dev = input_allocate_device();

- i- e9 ~, f( j, h

    if (!kbd || !input_dev)

5 k" h. H9 l) I1 U$ X* E

      goto fail1;


5 O0 |3 F. @, m, o9 Z& H0 {
4 w# O( ~7 i6 [9 y

    if (usb_kbd_alloc_mem(dev, kbd))

- t) A- Y2 ^  i- x; U6 v

      goto fail2;

+ K0 Z+ h! M! A" ?% C% r7 Q5 N% m& a

    /* 填充 usb 设备结构体和输入设备结构体 */

  c. t; L  I! R$ \* Q

    kbd->usbdev = dev;


6 {0 k. L" M( M( M; h

    kbd->dev = input_dev;


. A! H4 |# m) H 9 h* v1 O* D0 f# c# g# g+ B0 H

    /*以"厂商名字 产品名字"的格式将其写入kbd->name*/

; K3 g- O3 j( v  ^- v

    if (dev->manufacturer)

4 e" e. z* v) d- s

      strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));


8 w. f% E! t  r' O3 D3 A) p 0 c' n$ j. ]2 W- {- d

    if (dev->product) {


8 j! ]! ^8 h2 U# h6 y2 R) V! L

    if (dev->manufacturer)

& u, N* C2 h# d# M, x' o% h* c: R

        strlcat(kbd->name, " ", sizeof(kbd->name));


9 B* _% R3 W, }9 U3 v2 W+ G

    strlcat(kbd->name, dev->product, sizeof(kbd->name));


+ z* _! ?$ H2 i2 v4 ~/ ?1 K

    }

4 G& r, @6 y- U; o8 G; ], C9 x* w# e  V

; x  D- {; O- Z# {, P) w7 ~- o

    /*检测不到厂商名字*/

. z1 _1 |2 `4 R& A% {

    if (!strlen(kbd->name))


* S/ x9 ]6 l2 d& w4 e. f& F  `

    snprintf(kbd->name, sizeof(kbd->name),

  o4 X' b- ?0 a4 X. l* U, F# j

        "USB HIDBP Keyboard %04x:%04x",


- G) l( I5 Y! U+ {# P5 ]3 ~

        le16_to_cpu(dev->descriptor.idVendor),

6 m: N" Y8 Y& V+ P. D+ ~: v

        le16_to_cpu(dev->descriptor.idProduct));

/ l% f( U% {/ c

    /*设备链接地址*/


, X+ B/ b) v4 _4 R7 X

    usb_make_path(dev, kbd->phys, sizeof(kbd->phys));

3 D' o6 i& U8 |

    strlcpy(kbd->phys, "/input0", sizeof(kbd->phys));     

9 U0 F2 z+ J0 G! {( ?: }  J

    input_dev->name = kbd->name;


# o. R! B& l" p1 `5 g ; M* b* M  ^* e0 l/ h3 c  z6 Q" ?' v- Z. H

    input_dev->phys = kbd->phys;


! j* k% F4 Q" D0 o! |

    /*


4 H. N; ~, |. j; d0 ?  W

* input_dev 中的 input_id 结构体,用来存储厂商、设备类型和设备的编号,这个函数是将设备描述符


) W% J/ M/ n3 j3 r

    * 中的编号赋给内嵌的输入子系统结构体

  C( }3 C; V6 v8 X: V$ P' N0 j# i

    */


" M$ f# ~4 I) W( X: p

    usb_to_input_id(dev, &input_dev->id);


' I" N9 B8 e( u3 D

    /* cdev 是设备所属类别(class device) */

; x  Q. U+ C4 V0 {1 G

    input_dev->cdev.dev = &iface->dev;

9 [/ H2 S! x- o! V* S# ~; U! o

/* input_dev 的 private 数据项用于表示当前输入设备的种类,这里将键盘结构体对象赋给它 */


2 L( W  h1 q7 f- v7 d$ x

    input_dev->private = kbd;

7 S2 g" X. m# g& |

input_dev->evbit[0] = BIT(EV_KEY)/*键码事件*/ | BIT(EV_LED)/*LED事件*/ | BIT(EV_REP)/*自动重覆数值*/;

$ V! c2 l) `8 z2 E* N8 a) v

input_dev->ledbit[0] = BIT(LED_NUML)/*数字灯*/ | BIT(LED_CAPSL)/*大小写灯*/ | BIT(LED_SCROLLL)/*滚动灯*/ | BIT(LED_COMPOSE) | BIT(LED_KANA);


5 E  e2 q! o: C, U9 ~1 L6 k
& m8 x; o. n% y# j: L' j* F2 }

    for (i = 0; i < 255; i++)


0 Z3 {; N  P3 P# c6 @

    set_bit(usb_kbd_keycode, input_dev->keybit);


# h# B( k5 y. F6 k; F0 @

    clear_bit(0, input_dev->keybit);

  q4 e# [+ e% V- a8 q. \
1 w$ N1 [" x" G, Q5 g

    input_dev->event = usb_kbd_event;/*注册事件处理函数入口*/

0 H. t+ C8 r- L1 O8 \

    input_dev->open = usb_kbd_open;/*注册设备打开函数入口*/


2 ~" p; _, X! x* m% k

    input_dev->close = usb_kbd_close;/*注册设备关闭函数入口*/


0 v8 Y( R. ?0 N, V  b . V1 k! `& l$ e3 O

    /*初始化中断URB*/


4 P) c, c3 v) E; n9 z0 e

    usb_fill_int_urb(kbd->irq/*初始化kbd->irq这个urb*/, dev/*这个urb要发送到dev这个设备*/, pipe/*这个urb要发送到pipe这个端点*/,

. U3 Q' n5 b. d$ z5 W$ T4 H

        kbd->new/*指向缓冲的指针*/, (maxp > 8 ? 8 : maxp)/*缓冲长度*/,

& Z2 b& N5 k0 |0 s. Z6 C

        usb_kbd_irq/*这个urb完成时调用的处理函数*/, kbd/*指向数据块的指针,被添加到这个urb结构可被完成处理函数获取*/, endpoint->bInterval/*urb应当被调度的间隔*/);


- E" ^0 m5 T3 A/ E

    kbd->irq->transfer_dma = kbd->new_dma; /*指定urb需要传输的DMA缓冲区*/


9 c" O' q  B" d0 R/ l

kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;/*本urb有一个DMA缓冲区需要传输*/


- w+ y) Y# K, V* f

    kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;/*操作的是类接口对象*/

  A" _6 A! i% i( t

    kbd->cr->bRequest = 0x09; /*中断请求编号*/

7 \  F" l; P4 c3 m4 V

    kbd->cr->wValue = cpu_to_le16(0x200);


2 f9 o' P' Z! e5 l! Z6 E

    kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);/*接口号*/


4 W" u4 `% C  w8 J- K

    kbd->cr->wLength = cpu_to_le16(1);/*数据传输阶段传输多少个bytes*/

# |- O( Z5 E0 h$ J
0 ?9 _1 s% {# w* @

    /*初始化控制URB*/


& t3 M0 W8 }2 \

    usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),

, Q1 `1 a$ D% ~2 Z1 H  C- w

          (void *) kbd->cr, kbd->leds, 1,

) l$ D% z1 L5 q/ C1 Q

          usb_kbd_led, kbd);

" [6 u+ Z& c% H( N

    kbd->led->setup_dma = kbd->cr_dma;

; i* n* E6 _1 U& \

    kbd->led->transfer_dma = kbd->leds_dma;


) o+ D' V' I6 S& Y' E

    kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP/*如果使用DMA传输则urb中setup_dma指针所指向的缓冲区是DMA缓冲区而不是setup_packet所指向的缓冲区*/);

( d8 ]& x+ a( U

3 M% O$ `. I2 A4 V9 s

    /*注册输入设备*/


, Q# N' |, S* M5 m) I

    input_register_device(kbd->dev);

$ R8 K, i9 T4 v

! p$ y: K- j0 R# u% I5 |

    usb_set_intfdata(iface, kbd);/*设置接口私有数据*/


% Q8 F! d- V& c( O

    return 0;

$ z4 y+ Y( I' r5 {$ F8 b  D

/ _3 L& x) V4 q3 `

fail2:   usb_kbd_free_mem(dev, kbd);


4 _# v6 _& ?- z1 o1 y

fail1:   input_free_device(input_dev);


  M" u5 @) a) ?9 |0 F* U

    kfree(kbd);

* ]5 C; S, \; D3 B& I8 }/ n

    return -ENOMEM;

' d1 D: ^1 s7 ^  y8 w/ f
} 8 m" C; S0 v. l
18.    编写断开连接的函数:
& D0 {7 B' l/ n2 `# V9 W

/*断开连接(如键盘设备拔出)的处理函数*/


! K: k- ]0 g. ~7 z7 S

static void usb_kbd_disconnect(struct usb_interface *intf)


3 ^& K3 n1 t6 y+ G/ H7 F

{


& o* ]% P5 P0 h

    struct usb_kbd *kbd = usb_get_intfdata (intf);/*获取接口的私有数据给kbd*/

% F8 V$ ]' Y) b- X; W0 k/ E

    usb_set_intfdata(intf, NULL);/*设置接口的私有数据为NULL*/


' U0 c6 f/ d0 O* f- X

    if (kbd) {

2 e' y6 C/ z% i$ r' G0 Q

    usb_kill_urb(kbd->irq);/*取消中断请求*/


4 ]0 k3 J- [8 u  c

    input_unregister_device(kbd->dev);/*注销设备*/


. f5 X% y: F3 o  C- O9 v

    usb_kbd_free_mem(interface_to_usbdev(intf), kbd);/*释放内存空间*/

7 Z; t& b! p4 N7 l6 D4 {

    kfree(kbd);

# V& r0 {0 @/ e" L6 T

    }


: Z" U' H6 l* j* u# C* L } " G7 P, M# J9 B7 ?
19.    编写Makefile: & D1 l* T6 B& h$ y5 }' @3 _

##############################


) B9 D3 |6 M/ E4 B7 ^

#usbkdb Makefile for linux

1 H7 m2 }; Q  k0 z$ }# _

##############################

+ Y) F: ^# m9 B- i) F

obj-m:=usbkbd.o

# ], N( f; U  z2 C4 M& [& l% O) h

KERNELDIR ?= /lib/modules/$(shell uname -r)/build


( Y$ M5 J8 Z2 b7 C& B$ y

PWD:=$(shell pwd)

& ^2 j5 B0 P- D5 @

default:


' O5 U1 z  j  `' ]; R# ^ $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值