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

简介:

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()函数,释放设备驱动结构变量占用的内存。



本文转自infohacker 51CTO博客,原文链接:http://blog.51cto.com/liucw/1227045

相关文章
|
6月前
嵌入式开发板串口驱动框架
嵌入式开发板串口驱动框架
64 0
【打印机驱动】
解决无法连接打印机
84 0
【打印机驱动】
|
Linux
一文搞懂 USB 设备端驱动框架
hello 大家好,今天带领大家学习一下USB 设备端驱动 内核版本:4.4.94
1162 0
|
Linux 调度
嵌入式实践教程--设备树驱动下的中断开发
嵌入式实践教程--设备树驱动下的中断开发