【驱动】USB驱动实例·串口驱动·键盘驱动【转】

转自:http://www.cnblogs.com/lcw/p/3159370.html

Preface

   USB体系支持多种类型的设备。

   在 Linux内核,所有的USB设备都使用 usb_driver结构描述。

   对于不同类型的 USB设备,内核使用传统的设备驱动模型建立设备驱动描述,然后映射到 USB设备驱动,最终完成特定类型的 USB设备驱动

   USB驱动·入门:http://infohacker.blog.51cto.com/6751239/1226257

USB串口驱动

   USB串口驱动关键是向内核注册串口设备结构,并且设置串口的操作。

   下面是一个典型的USB设备驱动分析。

1、驱动初始化函数

   usb_serial_init()函数是一个典型的 USB设备驱动初始化函数,定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
static int __init usb_serial_init(void)
{
    int i;
    int result;
    usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS);    //申请 tty设备驱动描述
    if (!usb_serial_tty_driver)
        return -ENOMEM;
    /* Initialize our global data */
    for (i = 0; i < SERIAL_TTY_MINORS; ++i) {
        serial_table[i] = NULL;
    }
    result = bus_register(&usb_serial_bus_type);    //注册总线
    if (result) {
        err("%s - registering bus driver failed", __FUNCTION__);
        goto exit_bus;
    }
    usb_serial_tty_driver->owner = THIS_MODULE;  //初始化串口驱动描述
    usb_serial_tty_driver->driver_name = "usbserial";    //串口驱动名称
    usb_serial_tty_driver->name =    "ttyUSB";   //设备文件系统存放路径
    usb_serial_tty_driver->major = SERIAL_TTY_MAJOR; //串口设备主设备号
    usb_serial_tty_driver->minor_start = 0;  //串口设备从设备号起始 ID
    usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;    //设备类型
    usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL; //设备子类型
    usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; //设备初始化标志
    usb_serial_tty_driver->init_termios = tty_std_termios;   //串口设备描述
    usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;  //串口设备初始化参数
    tty_set_operations(usb_serial_tty_driver, &serial_ops); //串口设备操作函数
    result = tty_register_driver(usb_serial_tty_driver);    //注册串口驱动
    if (result) {
        err("%s - tty_register_driver failed", __FUNCTION__);
        goto exit_reg_driver;
    }
    /* register the USB driver */
    result = usb_register(&usb_serial_driver);  //注册 USB驱动
    if (result < 0) {
        err("%s - usb_register failed", __FUNCTION__);
        goto exit_tty;
    }
    /* register the generic driver, if we should */
    result = usb_serial_generic_register(debug);
    if (result < 0) {
        err("%s - registering generic driver failed", __FUNCTION__);
        goto exit_generic;
    }
    info(DRIVER_DESC);
    return result;
exit_generic:
    usb_deregister(&usb_serial_driver); //注销串口设备
exit_tty:
    tty_unregister_driver(usb_serial_tty_driver);   //注销 USB串口设备
exit_reg_driver:
    bus_unregister(&usb_serial_bus_type);   //注销总线
exit_bus:
    err ("%s - returning with error %d", __FUNCTION__, result);
    put_tty_driver(usb_serial_tty_driver);
    return result;
}
   函数首先调用 alloc_tty_driver()函数分配一个串口驱动描述符;然后设置串口驱动的属性,包括驱动的主从设备号、设备类型、串口初始化参数等;串口驱动描述符设置完毕后,调用 usb_register()函数注册 USB串口设备。

2、驱动释放函数

   驱动释放函数用来释放 USB串口设备驱动申请的内核资源,函数定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
static int __init usb_serial_init(void)
{
    int i;
    int result;
    usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS);    //申请 tty设备驱动描述
    if (!usb_serial_tty_driver)
        return -ENOMEM;
    /* Initialize our global data */
    for (i = 0; i < SERIAL_TTY_MINORS; ++i) {
        serial_table[i] = NULL;
    }
    result = bus_register(&usb_serial_bus_type);    //注册总线
    if (result) {
        err("%s - registering bus driver failed", __FUNCTION__);
        goto exit_bus;
    }
    usb_serial_tty_driver->owner = THIS_MODULE;  //初始化串口驱动描述
    usb_serial_tty_driver->driver_name = "usbserial";    //串口驱动名称
    usb_serial_tty_driver->name =    "ttyUSB";   //设备文件系统存放路径
    usb_serial_tty_driver->major = SERIAL_TTY_MAJOR; //串口设备主设备号
    usb_serial_tty_driver->minor_start = 0;  //串口设备从设备号起始 ID
    usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;    //设备类型
    usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL; //设备子类型
    usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; //设备初始化标志
    usb_serial_tty_driver->init_termios = tty_std_termios;   //串口设备描述
    usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;  //串口设备初始化参数
    tty_set_operations(usb_serial_tty_driver, &serial_ops); //串口设备操作函数
    result = tty_register_driver(usb_serial_tty_driver);    //注册串口驱动
    if (result) {
        err("%s - tty_register_driver failed", __FUNCTION__);
        goto exit_reg_driver;
    }
    /* register the USB driver */
    result = usb_register(&usb_serial_driver);  //注册 USB驱动
    if (result < 0) {
        err("%s - usb_register failed", __FUNCTION__);
        goto exit_tty;
    }
    /* register the generic driver, if we should */
    result = usb_serial_generic_register(debug);
    if (result < 0) {
        err("%s - registering generic driver failed", __FUNCTION__);
        goto exit_generic;
    }
    info(DRIVER_DESC);
    return result;
exit_generic:
    usb_deregister(&usb_serial_driver); //注销串口设备
exit_tty:
    tty_unregister_driver(usb_serial_tty_driver);   //注销 USB串口设备
exit_reg_driver:
    bus_unregister(&usb_serial_bus_type);   //注销总线
exit_bus:
    err ("%s - returning with error %d", __FUNCTION__, result);
    put_tty_driver(usb_serial_tty_driver);
    return result;
}

3、串口操作函数

   USB串口设备驱动使用了一个 tty_operations类型的结构,该结构包含了串口的所有操作。定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static struct tty_operations serial_ops = {
    .open =         serial_open,    //打开串口
    .close =        serial_close,   //关闭串口
    .write =        serial_write,   //串口写操作
    .write_room =       serial_write_room,
    .ioctl =        serial_ioctl,   // I/O控制操作
    .set_termios =      serial_set_termios, //设置串口参数
    .throttle =     serial_throttle,
    .unthrottle =       serial_unthrottle,
    .break_ctl =        serial_break,   // break信号处理
    .chars_in_buffer =  serial_chars_in_buffer, //缓冲处理
    .read_proc =        serial_read_proc,   //串口读操作
    .tiocmget =     serial_tiocmget,    //获取 I/O控制参数
    .tiocmset =     serial_tiocmset,    //设置 I/O控制参数
};

   serial_ops结构变量设置的所有串口操作函数,均使用内核 USB核心提供的标准函数,定义在/drivers/usb/serial/generic.c文件中

USB键盘驱动

   USB键盘驱动与串口驱动结构类似,不同是的使用USB设备核心提供的usb_keyboard_driver结构作为设备核心结构。下面是 USB键盘驱动的重点部分。

1、驱动初始和注销

   USB键盘驱动初始化和注销函数定义如下:

1
2
3
4
5
6
7
8
9
10
11
static int __init usb_kbd_init(void)
{
    int result = usb_register(&usb_kbd_driver); //注册 USB设备驱动
    if (result == 0)
        info(DRIVER_VERSION ":" DRIVER_DESC);
    return result;
}
static void __exit usb_kbd_exit(void)
{
    usb_deregister(&usb_kbd_driver);    //注销 USB设备驱动
}

   usb_kbd_init()函数在驱动加载的时候调用,该函数使用 usb_register()函数向内核注册一个 USB设备驱动;usb_kbd_exit()函数在卸载驱动程序的时候调用,该函数使用 usb_deregister()函数注销 USB设备。初始化和注销函数使用了 usb_keyboard结构变量,用于描述 USB键盘驱动程序,定义如下:

1
2
3
4
5
6
7
8
//usb_driver结构体
static struct usb_driver usb_keyboard =
{
    .name = "usbkbd",   //驱动名称
    .probe = usb_kbd_probe, //检测设备函数
    .disconnect = usb_kbd_disconnect,   //断开连接函数
    .id_table = usb_kbd_id_table,   //设备 ID
};

   从 usb_keyboard结构定义看出,usb_kbd_probe()函数是设备检测函数;

   usb_kbd_disconnect()函数是断开设备连接。

2、设备检测函数

   设备检测函数在插入 USB设备的时候被USB文件系统调用,负责检测设备类型是否与驱动相符。如果设备类型与驱动匹配,则向 USB核心注册设备。

   函数定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
static int usb_kbd_probe(struct usb_interface *iface,
             const struct usb_device_id *id)
{
    struct usb_device *dev = interface_to_usbdev(iface);
    struct usb_host_interface *interface;
    struct usb_endpoint_descriptor *endpoint;
    struct usb_kbd *kbd;
    struct input_dev *input_dev;
    int i, pipe, maxp;
    interface = iface->cur_altsetting;
    if (interface->desc.bNumEndpoints != 1)  //检查设备是否符合
        return -ENODEV;
    endpoint = &interface->endpoint[0].desc;
    if (!(endpoint->bEndpointAddress & USB_DIR_IN))
        return -ENODEV;
    if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT)
        return -ENODEV;
    pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);  //创建端点的管道
    maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
    kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);
    input_dev = input_allocate_device();    //分配 input_dev结构体
    if (!kbd || !input_dev) //分配设备结构占用的内存
        goto fail1;
    if (usb_kbd_alloc_mem(dev, kbd))
        goto fail2;
    kbd->usbdev = dev;
    kbd->dev = input_dev;
    if (dev->manufacturer)   //检查制造商名称
        strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));
    if (dev->product) {  //检查产品名称
        if (dev->manufacturer)
            strlcat(kbd->name, " ", sizeof(kbd->name));
        strlcat(kbd->name, dev->product, sizeof(kbd->name));
    }
    if (!strlen(kbd->name))
        snprintf(kbd->name, sizeof(kbd->name),
             "USB HIDBP Keyboard %04x:%04x",
             le16_to_cpu(dev->descriptor.idVendor),
             le16_to_cpu(dev->descriptor.idProduct));
    usb_make_path(dev, kbd->phys, sizeof(kbd->phys));
    strlcpy(kbd->phys, "/input0", sizeof(kbd->phys));
    //初始化输入设备
    input_dev->name = kbd->name;  //输入设备名称
    input_dev->phys = kbd->phys;  //输入设备物理地址
    usb_to_input_id(dev, &input_dev->id);    //输入设备 ID
    input_dev->cdev.dev = &iface->dev;
    input_dev->private = kbd;
    input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);
    input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA);
    for (i = 0; i < 255; i++)
        set_bit(usb_kbd_keycode[i], input_dev->keybit);
    clear_bit(0, input_dev->keybit);
    input_dev->event = usb_kbd_event;
    input_dev->open = usb_kbd_open;
    input_dev->close = usb_kbd_close;
    //初始化中断 urb
    usb_fill_int_urb(kbd->irq, dev, pipe,
             kbd->new, (maxp > 8 ? 8 : maxp),
             usb_kbd_irq, kbd, endpoint->bInterval);
    kbd->irq->transfer_dma = kbd->new_dma;
    kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
    kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
    kbd->cr->bRequest = 0x09;
    kbd->cr->wValue = cpu_to_le16(0x200);
    kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
    kbd->cr->wLength = cpu_to_le16(1);
    //初始化中断 urb
    usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),
                 (void *) kbd->cr, kbd->leds, 1,
                 usb_kbd_led, kbd);
    kbd->led->setup_dma = kbd->cr_dma;
    kbd->led->transfer_dma = kbd->leds_dma;
    kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
    input_register_device(kbd->dev); //注册输入设备
    usb_set_intfdata(iface, kbd);   //设置接口私有数据
    return 0;
fail2:  usb_kbd_free_mem(dev, kbd);
fail1:  input_free_device(input_dev);
    kfree(kbd);
    return -ENOMEM;
}

   函数一开始检测设备类型,如果与驱动程序匹配,则创建 USB设备端点,分配设备驱动结构占用的内存。分配好设备驱动使用的结构后,申请一个键盘设备驱动节点,然后设置键盘驱动,最后设置 USB设备的中断 URB和控制 URB,供 USB设备核心使用。

3、设备断开连接函数

   在设备断开连接的时候,USB文件系统会调用 usb_kbd_disconnect()函数,释放设备占用的资源。

   函数定义如下:

1
2
3
4
5
6
7
8
9
10
11
static void usb_kbd_disconnect(struct usb_interface *intf)
{
    struct usb_kbd *kbd = usb_get_intfdata (intf);
    usb_set_intfdata(intf, NULL);   //设置接口私有数据为 NULL
    if (kbd) {
        usb_kill_urb(kbd->irq);  //终止 URB
        input_unregister_device(kbd->dev);   //注销输入设备
        usb_kbd_free_mem(interface_to_usbdev(intf), kbd);   //释放设备驱动占用的内存
        kfree(kbd);
    }
}

   usb_kbd_disconnect()函数释放 USB键盘设备占用的 URB资源,然后注销设备,最后调用usb_kbd_free_mem()函数,释放设备驱动结构变量占用的内存。

 

Preface

 

   USB体系支持多种类型的设备。

   在 Linux内核,所有的USB设备都使用 usb_driver结构描述。

   对于不同类型的 USB设备,内核使用传统的设备驱动模型建立设备驱动描述,然后映射到 USB设备驱动,最终完成特定类型的 USB设备驱动

   USB驱动·入门http://infohacker.blog.51cto.com/6751239/1226257

 

 



USB串口驱动

 

   USB串口驱动关键是向内核注册串口设备结构,并且设置串口的操作。

   下面是一个典型的USB设备驱动分析。

 



1、驱动初始化函数

 

   usb_serial_init()函数是一个典型的 USB设备驱动初始化函数,定义如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

static int __init usb_serial_init(void)

{

    int i;

    int result;

    usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS);    //申请 tty设备驱动描述

    if (!usb_serial_tty_driver)

        return -ENOMEM;

    /* Initialize our global data */

    for (i = 0; i < SERIAL_TTY_MINORS; ++i) {

        serial_table[i] = NULL;

    }

    result = bus_register(&usb_serial_bus_type);    //注册总线

    if (result) {

        err("%s - registering bus driver failed", __FUNCTION__);

        goto exit_bus;

    }

    usb_serial_tty_driver->owner = THIS_MODULE;  //初始化串口驱动描述

    usb_serial_tty_driver->driver_name = "usbserial";    //串口驱动名称

    usb_serial_tty_driver->name =    "ttyUSB";   //设备文件系统存放路径

    usb_serial_tty_driver->major = SERIAL_TTY_MAJOR; //串口设备主设备号

    usb_serial_tty_driver->minor_start = 0;  //串口设备从设备号起始 ID

    usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;    //设备类型

    usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL; //设备子类型

    usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; //设备初始化标志

    usb_serial_tty_driver->init_termios = tty_std_termios;   //串口设备描述

    usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;  //串口设备初始化参数

    tty_set_operations(usb_serial_tty_driver, &serial_ops); //串口设备操作函数

    result = tty_register_driver(usb_serial_tty_driver);    //注册串口驱动

    if (result) {

        err("%s - tty_register_driver failed", __FUNCTION__);

        goto exit_reg_driver;

    }

    /* register the USB driver */

    result = usb_register(&usb_serial_driver);  //注册 USB驱动

    if (result < 0) {

        err("%s - usb_register failed", __FUNCTION__);

        goto exit_tty;

    }

    /* register the generic driver, if we should */

    result = usb_serial_generic_register(debug);

    if (result < 0) {

        err("%s - registering generic driver failed", __FUNCTION__);

        goto exit_generic;

    }

    info(DRIVER_DESC);

    return result;

exit_generic:

    usb_deregister(&usb_serial_driver); //注销串口设备

exit_tty:

    tty_unregister_driver(usb_serial_tty_driver);   //注销 USB串口设备

exit_reg_driver:

    bus_unregister(&usb_serial_bus_type);   //注销总线

exit_bus:

    err ("%s - returning with error %d", __FUNCTION__, result);

    put_tty_driver(usb_serial_tty_driver);

    return result;

}

   函数首先调用 alloc_tty_driver()函数分配一个串口驱动描述符;然后设置串口驱动的属性,包括驱动的主从设备号、设备类型、串口初始化参数等;串口驱动描述符设置完毕后,调用 usb_register()函数注册 USB串口设备。

 



2、驱动释放函数

 

   驱动释放函数用来释放 USB串口设备驱动申请的内核资源,函数定义如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

static int __init usb_serial_init(void)

{

    int i;

    int result;

    usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS);    //申请 tty设备驱动描述

    if (!usb_serial_tty_driver)

        return -ENOMEM;

    /* Initialize our global data */

    for (i = 0; i < SERIAL_TTY_MINORS; ++i) {

        serial_table[i] = NULL;

    }

    result = bus_register(&usb_serial_bus_type);    //注册总线

    if (result) {

        err("%s - registering bus driver failed", __FUNCTION__);

        goto exit_bus;

    }

    usb_serial_tty_driver->owner = THIS_MODULE;  //初始化串口驱动描述

    usb_serial_tty_driver->driver_name = "usbserial";    //串口驱动名称

    usb_serial_tty_driver->name =    "ttyUSB";   //设备文件系统存放路径

    usb_serial_tty_driver->major = SERIAL_TTY_MAJOR; //串口设备主设备号

    usb_serial_tty_driver->minor_start = 0;  //串口设备从设备号起始 ID

    usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;    //设备类型

    usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL; //设备子类型

    usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; //设备初始化标志

    usb_serial_tty_driver->init_termios = tty_std_termios;   //串口设备描述

    usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;  //串口设备初始化参数

    tty_set_operations(usb_serial_tty_driver, &serial_ops); //串口设备操作函数

    result = tty_register_driver(usb_serial_tty_driver);    //注册串口驱动

    if (result) {

        err("%s - tty_register_driver failed", __FUNCTION__);

        goto exit_reg_driver;

    }

    /* register the USB driver */

    result = usb_register(&usb_serial_driver);  //注册 USB驱动

    if (result < 0) {

        err("%s - usb_register failed", __FUNCTION__);

        goto exit_tty;

    }

    /* register the generic driver, if we should */

    result = usb_serial_generic_register(debug);

    if (result < 0) {

        err("%s - registering generic driver failed", __FUNCTION__);

        goto exit_generic;

    }

    info(DRIVER_DESC);

    return result;

exit_generic:

    usb_deregister(&usb_serial_driver); //注销串口设备

exit_tty:

    tty_unregister_driver(usb_serial_tty_driver);   //注销 USB串口设备

exit_reg_driver:

    bus_unregister(&usb_serial_bus_type);   //注销总线

exit_bus:

    err ("%s - returning with error %d", __FUNCTION__, result);

    put_tty_driver(usb_serial_tty_driver);

    return result;

}

 



3、串口操作函数

 

   USB串口设备驱动使用了一个 tty_operations类型的结构,该结构包含了串口的所有操作。定义如下:

 


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

static struct tty_operations serial_ops = {

    .open =         serial_open,    //打开串口

    .close =        serial_close,   //关闭串口

    .write =        serial_write,   //串口写操作

    .write_room =       serial_write_room,

    .ioctl =        serial_ioctl,   // I/O控制操作

    .set_termios =      serial_set_termios, //设置串口参数

    .throttle =     serial_throttle,

    .unthrottle =       serial_unthrottle,

    .break_ctl =        serial_break,   // break信号处理

    .chars_in_buffer =  serial_chars_in_buffer, //缓冲处理

    .read_proc =        serial_read_proc,   //串口读操作

    .tiocmget =     serial_tiocmget,    //获取 I/O控制参数

    .tiocmset =     serial_tiocmset,    //设置 I/O控制参数

};

 

   serial_ops结构变量设置的所有串口操作函数,均使用内核 USB核心提供的标准函数,定义在/drivers/usb/serial/generic.c文件中

 

 

 



USB键盘驱动

 

   USB键盘驱动与串口驱动结构类似,不同是的使用USB设备核心提供的usb_keyboard_driver结构作为设备核心结构。下面是 USB键盘驱动的重点部分。

 



1、驱动初始和注销

 

   USB键盘驱动初始化和注销函数定义如下:


1

2

3

4

5

6

7

8

9

10

11

static int __init usb_kbd_init(void)

{

    int result = usb_register(&usb_kbd_driver); //注册 USB设备驱动

    if (result == 0)

        info(DRIVER_VERSION ":" DRIVER_DESC);

    return result;

}

static void __exit usb_kbd_exit(void)

{

    usb_deregister(&usb_kbd_driver);    //注销 USB设备驱动

}

 

   usb_kbd_init()函数在驱动加载的时候调用,该函数使用 usb_register()函数向内核注册一个 USB设备驱动;usb_kbd_exit()函数在卸载驱动程序的时候调用,该函数使用 usb_deregister()函数注销 USB设备。初始化和注销函数使用了 usb_keyboard结构变量,用于描述 USB键盘驱动程序,定义如下:

 


1

2

3

4

5

6

7

8

//usb_driver结构体

static struct usb_driver usb_keyboard =

{

    .name = "usbkbd",   //驱动名称

    .probe = usb_kbd_probe, //检测设备函数

    .disconnect = usb_kbd_disconnect,   //断开连接函数

    .id_table = usb_kbd_id_table,   //设备 ID

};

 

   从 usb_keyboard结构定义看出,usb_kbd_probe()函数是设备检测函数;

   usb_kbd_disconnect()函数是断开设备连接。

 



2、设备检测函数

 

   设备检测函数在插入 USB设备的时候被USB文件系统调用,负责检测设备类型是否与驱动相符。如果设备类型与驱动匹配,则向 USB核心注册设备。

   函数定义如下:

 


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

static int usb_kbd_probe(struct usb_interface *iface,

             const struct usb_device_id *id)

{

    struct usb_device *dev = interface_to_usbdev(iface);

    struct usb_host_interface *interface;

    struct usb_endpoint_descriptor *endpoint;

    struct usb_kbd *kbd;

    struct input_dev *input_dev;

    int i, pipe, maxp;

    interface = iface->cur_altsetting;

    if (interface->desc.bNumEndpoints != 1)  //检查设备是否符合

        return -ENODEV;

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

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

        return -ENODEV;

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

        return -ENODEV;

    pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);  //创建端点的管道

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

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

    input_dev = input_allocate_device();    //分配 input_dev结构体

    if (!kbd || !input_dev) //分配设备结构占用的内存

        goto fail1;

    if (usb_kbd_alloc_mem(dev, kbd))

        goto fail2;

    kbd->usbdev = dev;

    kbd->dev = input_dev;

    if (dev->manufacturer)   //检查制造商名称

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

    if (dev->product) {  //检查产品名称

        if (dev->manufacturer)

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

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

    }

    if (!strlen(kbd->name))

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

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

             le16_to_cpu(dev->descriptor.idVendor),

             le16_to_cpu(dev->descriptor.idProduct));

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

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

    //初始化输入设备

    input_dev->name = kbd->name;  //输入设备名称

    input_dev->phys = kbd->phys;  //输入设备物理地址

    usb_to_input_id(dev, &input_dev->id);    //输入设备 ID

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

    input_dev->private = kbd;

    input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);

    input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA);

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

        set_bit(usb_kbd_keycode[i], input_dev->keybit);

    clear_bit(0, input_dev->keybit);

    input_dev->event = usb_kbd_event;

    input_dev->open = usb_kbd_open;

    input_dev->close = usb_kbd_close;

    //初始化中断 urb

    usb_fill_int_urb(kbd->irq, dev, pipe,

             kbd->new, (maxp > 8 ? 8 : maxp),

             usb_kbd_irq, kbd, endpoint->bInterval);

    kbd->irq->transfer_dma = kbd->new_dma;

    kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

    kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;

    kbd->cr->bRequest = 0x09;

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

    kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);

    kbd->cr->wLength = cpu_to_le16(1);

    //初始化中断 urb

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

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

                 usb_kbd_led, kbd);

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

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

    kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);

    input_register_device(kbd->dev); //注册输入设备

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

    return 0;

fail2:  usb_kbd_free_mem(dev, kbd);

fail1:  input_free_device(input_dev);

    kfree(kbd);

    return -ENOMEM;

}

 

   函数一开始检测设备类型,如果与驱动程序匹配,则创建 USB设备端点,分配设备驱动结构占用的内存。分配好设备驱动使用的结构后,申请一个键盘设备驱动节点,然后设置键盘驱动最后设置 USB设备的中断 URB和控制 URB,供 USB设备核心使用。

 



3、设备断开连接函数

 

   在设备断开连接的时候,USB文件系统会调用 usb_kbd_disconnect()函数,释放设备占用的资源。

   函数定义如下:

 


1

2

3

4

5

6

7

8

9

10

11

static void usb_kbd_disconnect(struct usb_interface *intf)

{

    struct usb_kbd *kbd = usb_get_intfdata (intf);

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

    if (kbd) {

        usb_kill_urb(kbd->irq);  //终止 URB

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

        usb_kbd_free_mem(interface_to_usbdev(intf), kbd);   //释放设备驱动占用的内存

        kfree(kbd);

    }

}

 

   usb_kbd_disconnect()函数释放 USB键盘设备占用的 URB资源,然后注销设备,最后调用usb_kbd_free_mem()函数,释放设备驱动结构变量占用的内存。

时间: 2024-08-30 10:57:52

【驱动】USB驱动实例&#183;串口驱动&#183;键盘驱动【转】的相关文章

录音-Yamaha steinberg USB driver 1.6.0以上Mac版驱动

问题描述 Yamaha steinberg USB driver 1.6.0以上Mac版驱动 Yamaha steinberg USB driver 1.6.0以上Mac版驱动安装后,可以连接雅马哈01V96i调音台,并使用Protools10.3.10进行录音吗?

驱动人生为您一键解决iPhone 6S手机驱动问题

  首先,下载安装一个专业的驱动软件,比如驱动人生6,安装好软件后,打开主界面,建议点击"开始检测"按钮,检测下当前电脑驱动是否有异常或需要更新.如检测出有驱动需要更新,建议可以不点击"立即更新"按钮更新,因为驱动程序并不是最新的就是最好的,只要自己的电脑运转正常,不需要更新驱动也是可以的. 点击打开外设驱动界面,可以看到,电脑所有外部接入的usb输入设备,都会出现在了这个界面.点击某个设备,都可以查看设备的信息,对于外设设备的管理方便.快捷.如某个外设设备驱动出现

驱动人生6教您如何管理好电脑驱动

显卡驱动.声卡驱动.无线网卡驱动.摄像头驱动--驱动程序一大堆,您是否会觉得不知道怎么去管理?当驱动人生6检测出有好几个驱动需要更新的时候,您不知道该先更新哪个?下面小编将告诉您如何用驱动人生6来管理好自己的电脑驱动. 只要您打开驱动人生,它就会自动给您的电脑驱动进行检测,如果检测出有需要更新的驱动,则会在检测结果里面显示出来.所以,您想要知道您的电脑驱动是否运行正常,在首页进行检测就可以完成. 在驱动人生6的本机驱动分页,分为推荐驱动和最新驱动.推荐驱动是驱动人生官方优先推荐给用户的电脑驱动,

驱动签名-为什么组策略已经允许未签名驱动。但是还是不能加载驱动

问题描述 为什么组策略已经允许未签名驱动.但是还是不能加载驱动 开机按F8选择禁用强制驱动签名..这样是可以加载驱动的.. 但是设置组策略却不行.... cmd命令 bcdedit.exe -set loadoptions DDISABLE_INTEGRITY_CHECKS 也不行.. 这是为什么呢 解决方案 试试这个呢. 是用管理员权限执行的命令吗 Bcdedit.exe /set nointegritychecks ON 解决方案二: 这是微软为了安全设计的,也就是"64位系统驱动程序强制签

系统-驱动开发中,设备是如何挂接到驱动上的???(大致的过程即可)

问题描述 驱动开发中,设备是如何挂接到驱动上的???(大致的过程即可) 问题: 1.基于dts/dtsi的内核系统,是通过dts/dtsi的读取来注册设备的吗?如果不是,那设备注册/挂接的 操作在哪实现的? 2.在static struct i2c_driver 结构中, .driver = { .name .owner = THIS_MODULE, .of_match_table }, .id_table }; of_match_table 与id_table 的作用??? 解决方案 基于dt

《Android深度探索(卷1):HAL与驱动开发》——6.3节第一个Linux驱动:统计单词个数

6.3 第一个Linux驱动:统计单词个数Android深度探索(卷1):HAL与驱动开发源程序目录:<光盘根目录>/sources/word_count本节将给出我们的第1个Linux驱动的例子.这个驱动程序并没有访问硬件,而是利用设备文件作为介质与应用程序进行交互.应用程序通过向设备文件传递一个由空格分隔的字符串(每一个被空格隔开的子字符串称为一个单词),然后从设备文件读出来的是该字符串包含的单词数.本例的驱动程序使用C语言实现,源代码文件路径如下. 6.3.1 编写Linux驱动程序前的

SATA串口硬盘Vista系统驱动安装实录

自从Vista的种子在网上刚刚出现的时候,我就下载了.可是一直就安装不上,每次安装时的第一次重启都会蓝屏,原来是没有加载SATA驱动. 网友们说跟本装不上的,可是我就是不死心,研究了二十天,昨天终于装上了.原来可以在安装重启后从XP下手动将SATA驱动和注册信息添加到Vista的临时目录. 首先,我们的中心思想是在要在Vista安装目录中手动添加个我门自己的磁盘控制器驱动(废话).这需要有2个步骤来完成: 1.在Vista 临时安装目录中,放置我门自己的驱动程序文件. 2.在Vista 的注册表

驱动人生5助端午节购机 给力品牌电脑驱动

又到端午,3天的假期,让很多想买电脑的童鞋终于有了悉心挑选的时间.相对于自己动手DIY电脑,品牌电脑选择起来更有保障.很多品牌电脑重装了系统后,会出现屏幕显示异常或者是电脑没有声音等问题,其实这些都是由于没有安装好对应的品牌机驱动造成的.全新上阵的驱动人生5,新版本特别加强了对品牌驱动引擎的优化和品牌驱动库的补充.目前市面上主流的品牌电脑驱动都能精确查找到,并即时提示更新,推荐相应的驱动进行安装,让你的电脑性能领先一步. 打开驱动人生5,找到"驱动"界面中的"推荐驱动&quo

无线鼠标键盘驱动盘丢了怎么办

  第1步:打开网页浏览器,并直接使用搜索引擎搜索自己鼠标中的制造商,并找到无线鼠标键盘制造商的网站. 第2步:在无线鼠标键盘输入制造商的官方网站中找到"帮助"或"支持"一类并单击,然后在此网页中找到自己无线鼠桔键盘中的具体型号并打开. 第3步:选择相应无线鼠标键盘型号后,然后再选择符合自己操作系统,并单击官方网站所提供的下载连接. 注:一般都有XP.WIN7.WIN8各版本的系统, 第4步:从无线鼠标键盘的官方网站下载完驱动程序之后,并按照驱动安装程序提示安全安装