摘自: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 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" i2. 定义键盘码表数组: 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 K50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
$ s$ s7 _- X/ k$ Y1 A0 s/ n4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
4 p3 K' e( Z" C2 o p27, 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 v105,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# q122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
A# G' I$ p# Y0, 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$ A0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
) Y2 y& n1 X" W0 t% r8 v+ D5 y29, 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' AMODULE_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 Lunsigned char newleds;/*目标指定灯状态*/
5 a& g) A. }6 B# T0 F; f0 ichar name[128];/*存放厂商名字及产品名字*/
) R/ e9 L1 j" Ychar 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) Tunsigned char *leds;/*当前指示灯状态*/
. J' \1 i) l* A; D+ w
dma_addr_t cr_dma; /*控制请求DMA缓冲地址*/
_; N1 k4 A5 \/ Idma_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- Vstatic 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' s6. 编写模块加载函数(每个驱动都会有一个加载函数,由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: eint 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% {; jinfo(DRIVER_VERSION ":" DRIVER_DESC);
^) \1 i$ v8 S5 ]* |/ v8 L4 Ireturn 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' pstatic void __exit usb_kbd_exit(void)
% d4 i R+ c* B( m1 m* M3 n{
; g: e4 c* M: Fprintk("SUNWILL-USBKBD:usb_kbd_exit begin...\n");
) {% g5 j, z8 l/ P6 ^% V5 d& Tusb_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/ Jstatic 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/ ebreak;
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$ zcase -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 Odefault: /* error */
$ \9 p# Q Z% }, Qgoto 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$ ~* ifor (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" [; Tinput_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 \+ | cif (usb_kbd_keycode[kbd->old])
* L! H# K- [# m+ j" W$ R" j{
& x& t/ N/ ]" R# N% t, y3 vinput_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; | Qif (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! Celse
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 Hinput_sync(kbd->dev);
0 m# t5 M5 m S, Y/ v- xmemcpy(kbd->old, kbd->new, 8);/*防止未松开时被当成新的按键处理*/
7 D- @% A5 M+ p! y' k
resubmit:
/ @5 J" _9 N2 P7 j! B) J% e* W) z+ bi = usb_submit_urb (urb, GFP_ATOMIC);/*发送USB请求块*/
1 X7 O% u) ~. J$ Q$ |
if (i)
& R/ u" G/ d5 f% eerr ("can't resubmit intr, %s-%s/input0, status %d",
, k8 w% x: J# i( N8 Fkbd->usbdev->bus->bus_name,
: o/ C9 z* g1 r" X; x' I& Gkbd->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 kstatic int usb_kbd_event(struct input_dev *dev, unsigned int type,
% W+ {; f' A- ~( Dunsigned int code, int value)
M# b n3 e L9 n& c' ?
{
# S% i; V5 Z3 Z7 H, J1 Wstruct 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# ykbd->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 Kif (*(kbd->leds) == kbd->newleds)
9 Z9 |1 A, o5 K6 nreturn 0;
0 C6 M6 y2 L) F0 L( }9 W3 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% Vstatic 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 Ostruct usb_kbd *kbd = urb->context;
+ p; L; {) o7 W8 N2 V; G# s! O8 H2 }' V9 c' H4 U8 C
if (urb->status)
; w! S& N- S" Q3 `* [8 bwarn("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 Sj6 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 zif (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- `. Skbd->irq->dev = kbd->usbdev;
9 j) J9 X/ l: pif (usb_submit_urb(kbd->irq, GFP_KERNEL))
, Q1 l B+ U1 Y7 i" G1 kreturn -EIO;
$ E) _7 i# b. V2 Lreturn 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/ Kstatic 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+ Tusb_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 yreturn -1;
+ O# B6 S( G) bif (!(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# Cif (!(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- nif (!(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) Wreturn 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% Xif (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 Xif (kbd->cr)
( s! h' r0 w3 r( k2 X3 Musb_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' U17. 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$ Jreturn -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: Vmaxp = 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' tinput_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 Ugoto 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 I1 g" Y* }0 R9 {5 x3 o# Z
/*以"厂商名字 产品名字"的格式将其写入kbd->name*/
$ W: q- A% I; f' {1 Mif (dev->manufacturer)
+ {9 q; Q" ?; E+ A: Hstrlcpy(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 Xif (!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 Dle16_to_cpu(dev->descriptor.idVendor),
0 ~0 t* D2 W0 Y; o ?* j9 dle16_to_cpu(dev->descriptor.idProduct));
5 l5 p8 C4 ]8 J9 G
/*设备链接地址*/
3 O0 I0 [+ u4 R! [' ~0 Kusb_make_path(dev, kbd->phys, sizeof(kbd->phys));
# E' Z9 l B% ]% H# rstrlcpy(kbd->phys, "/input0", sizeof(kbd->phys));
. g2 f: I; h4 c# iinput_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$ gusb_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- Ninput_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: sclear_bit(0, input_dev->keybit);
/ c( j" `. R& T" A& [9 V. C4 x4 p6 H9 [% e( p
input_dev->event = usb_kbd_event;/*注册事件处理函数入口*/
8 D2 S7 u4 r/ {! r9 Sinput_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 lkbd->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 Ikbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;/*操作的是类接口对象*/
+ U: l4 [- v8 ^# {4 ~8 U- S3 Zkbd->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+ ?! `& Kusb_kbd_led, kbd);
6 d) `1 O7 G& fkbd->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- Wkbd->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 r4 M3 @9 @' N# J
/*注册输入设备*/
( |. d5 b) K4 I* `8 Z6 }! [' Rinput_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 Efail1: 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& {: Bstatic void usb_kbd_disconnect(struct usb_interface *intf)
0 y. w* |( N: h{
$ Z8 T, r8 e8 S2 h' xstruct 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% eif (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 mkfree(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 Oobj-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 mdefault:
' 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 d4, 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' \+ E65, 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 l191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
# q! b2 ~$ Q& C$ ]+ X2 \6 x5 z L115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,
1 Q8 y1 X8 n6 k7 x+ R& j122,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 s0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
; U( {4 M2 u( ?9 M* v& @' n0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- x5 O" c* u: q% u0 z; G, e0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4 D B- P2 V8 y29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
; L9 b9 _) }* A. ]2 R; N7 U150,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# PMODULE_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 Sstruct 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' Zchar name[128];/*存放厂商名字及产品名字*/
) Y- R# x7 d) ?6 h1 tchar 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 ^/ ounsigned char *leds;/*当前指示灯状态*/
7 A0 x/ ?9 O9 Y: r/ Q2 zdma_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 estatic 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& x6. 编写模块加载函数(每个驱动都会有一个加载函数,由module_init调用):
3 p) E5 N. }3 w# F) G
/*驱动程序生命周期的开始点,向 USB core 注册这个键盘驱动程序。*/
6 C* }! @+ t* y! V8 M% y' Wstatic 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# Vusb_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 Pstatic 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* Mstruct usb_kbd *kbd = urb->context;
# ?; r3 K' T- \, \int i;
- L7 t& h. \9 E$ i1 [) i% Vswitch (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 Bcase -ECONNRESET: /* unlink */
: J5 `1 Z; }7 r# Y8 w/ Icase -ENOENT:
+ |( q! U" R4 O1 q" Z5 w$ Q% \
case -ESHUTDOWN:
0 G1 |" Y# V; oreturn;
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 ginput_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- {; Nelse
$ B2 X- }- I* L ? U. Dinfo("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 Qif (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+ rkbd->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 Astatic 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' [# istruct 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 Xreturn 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 jreturn 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 N3 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. Lkbd->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& lerr("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 ekbd->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 ireturn 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# ustatic void usb_kbd_close(struct input_dev *dev)
~4 U! \- K2 {1 h6 F
{
0 G- C) l" p4 B& N1 jstruct 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 Qstatic 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% Uif (!(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- Sif (!(kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), GFP_ATOMIC, &kbd->cr_dma)))
2 [2 U0 z. g6 m5 ]/ D& T Kreturn -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) qreturn 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 cusb_free_urb(kbd->led);
; y. o+ h+ X* p; k* d cif (kbd->new)
7 T. K; a% X5 A9 C" Qusb_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 tstatic int usb_kbd_probe(struct usb_interface *iface,
9 g7 G" o; {8 ~8 X1 I+ t' Uconst 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. Gint i, pipe, maxp;
# Z8 E8 d/ }7 C- h4 Y
/*当前选择的interface*/
/ M* ]2 y$ `) m7 n* S+ z Dinterface = 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# {+ sreturn -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, Freturn -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( Ninput_dev = input_allocate_device();
- i- e9 ~, f( j, hif (!kbd || !input_dev)
5 k" h. H9 l) I1 U$ X* Egoto 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 vgoto fail2;
+ K0 Z+ h! M! A" ?% C% r7 Q5 N% m& a/* 填充 usb 设备结构体和输入设备结构体 */
c. t; L I! R$ \* Qkbd->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 ^- vif (dev->manufacturer)
4 e" e. z* v) d- sstrlcpy(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: Rstrlcat(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+ ~: vle16_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! {( ?: } Jinput_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 Ginput_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) vinput_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 Hkbd->new/*指向缓冲的指针*/, (maxp > 8 ? 8 : maxp)/*缓冲长度*/,
& Z2 b& N5 k0 |0 s. Z6 Cusb_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( tkbd->cr->bRequest = 0x09; /*中断请求编号*/
7 \ F" l; P4 c3 m4 Vkbd->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$ J0 ?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 Qusb_kbd_led, kbd);
" [6 u+ Z& c% H( Nkbd->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( U3 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 }/ nreturn -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/ Eusb_set_intfdata(intf, NULL);/*设置接口的私有数据为NULL*/
' U0 c6 f/ d0 O* f- X
if (kbd) {
2 e' y6 C/ z% i$ r' G0 Qusb_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) Fobj-m:=usbkbd.o
# ], N( f; U z2 C4 M& [& l% O) hKERNELDIR ?= /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