(二)USB驱动程序_USB设备驱动(Host)

Love The Way You Lie 2022-05-08 11:40 785阅读 0赞

USB设备驱动(Host)

  1. 深入,并且广泛
  2. -沉默犀牛

有了第一篇文章的基础,我们这篇文章来看一下USB设备驱动的源码。与其他的Driver一样,USB的driver也表现为一个结构体:struct usb_driver

驱动整体结构

在编写新的USB设备驱动时,主要应该完成的工作是probe()和disconnect()函数,它们分别在Device被插入和拔出的时候调用,用于初始化和释放软硬件资源。usb_driver结构体中的id_table成员描述了这个USB驱动所支持的USB设备列表,它指向一个usb_device_id数组,实例如下:

  1. static const struct usb_device_id skel_table[] = {
  2. { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
  3. { } /* Terminating entry */
  4. };
  5. MODULE_DEVICE_TABLE(usb, skel_table);
  6. static struct usb_driver skel_driver = {
  7. .name = "skeleton",
  8. .probe = skel_probe, //probe
  9. .disconnect = skel_disconnect, //disconnent
  10. .suspend = skel_suspend,
  11. .resume = skel_resume,
  12. .pre_reset = skel_pre_reset,
  13. .post_reset = skel_post_reset,
  14. .id_table = skel_table, //id_table
  15. .supports_autosuspend = 1,
  16. };

USB_DEVICE这个宏是可以根据Vendor ID和Product ID生成一个usb_device_id结构体的实例化。
当USB core检测到某个Device的属性和某个Drivre的usb_device_id结构体所携带的信息一致的时候,这个Driver的probe()函数就被执行(如果这个USB驱动是个模块的话,相关的.ko还应被Linux自动加载)。拔掉Device或者卸载Drivre模块后,USB核心就执行disconnect()函数来响应这个动作。

usb_driver结构体中的函数是USB设备驱动中与USB相关的部分,而USB只是一个总线,USB设备驱动真正的主体工作仍然是USB设备
本身所属类型的驱动,比如字符设备、tty设备、块设备、输入设备等。
因此USB设备驱动包含其作为
1.总线上挂接设备的驱动
2.本身所属设备类型的驱动两部分

尽管USB本身所属设备驱动的结构与其挂不挂在USB总线上没什么关系,但是据此在访问方式上却有很大的变化,例如,对于USB的TP而言,尽管仍然是中断服务函数、固件升级这些函数,但是在这些函数中,贯穿始终的是称为URB的USB请求块

举个书中的例子:
我们把整个USB构架(Host侧)看做一颗大叔,HostController是树根,树叶比作具体的USB设备,树干和树枝就是USB总线树叶本身与树枝是通过usb_driver结构体连接,而树叶本身的驱动(读写、控制)则需要通过树叶设备本身所属类设备驱动来完成。树根和树叶之间的“通信”依靠在树干和树枝中“流淌”的URB完成

由此可见,usb_driver本身只是有找到USB设备,管理USB设备连接和断开的作用,也就是说,它是公司入口处的“打卡机”,可以获得员工(USB设备)的上下班情况。树叶和员工一样,可以是研发工程师,也可以是销售,而作为USB设备的树叶可以是字符树叶、网络树叶或块树叶,因此必须实现相应设备类的驱动

URB介绍

URB结构体

根据上面的描述,我们知道了URB承载着USB设备和Host controller通信的核心数据,使用struct urb来描述:
仅注释了重要成员

  1. struct urb {
  2. /* private: usb core and host controller only fields in the urb */
  3. struct kref kref;
  4. void *hcpriv;
  5. atomic_t use_count;
  6. atomic_t reject;
  7. int unlinked;
  8. /* public: documented fields in the urb that can be used by drivers */
  9. struct list_head urb_list;
  10. struct list_head anchor_list;
  11. struct usb_anchor *anchor;
  12. struct usb_device *dev; //urb所要发送的目标 struct usb_device指针。该变量在urb可以被发送到
  13. USB core之前,必须由USB驱动程序初始化
  14. struct usb_host_endpoint *ep;
  15. unsigned int pipe; //urb所要发送的特定目标struct usb_device的端点信息。该变量在urb可以
  16. 被发送到USB core之前,必须由USB驱动程序初始化
  17. unsigned int stream_id;
  18. int status; //当urb结束之后,或者正在被USB core处理时,该变量被设置为urb的当前状态
  19. 这是为了防止当urbUSB core处理时竞态的发生。
  20. unsigned int transfer_flags; //该变量可以设为许多不同的位值,取决与USB驱动程序对urb的具体操作
  21. 详情见LDD3 P333
  22. void *transfer_buffer; //指向用于发送数据到设备(OUT urb)或者从设备接受数据(IN urb)的缓冲区
  23. 的指针,为了使Host controller可以正确的访问该缓冲区,必须使用kmalloc
  24. 来创建它,而不是在栈中或者静态内存中。对于控制端点,该缓冲区用于传输数
  25. 据的中转
  26. dma_addr_t transfer_dma; // 用于以DMA方式传输数据到USB设备的缓冲区
  27. struct scatterlist *sg;
  28. int num_mapped_sgs;
  29. int num_sgs;
  30. u32 transfer_buffer_length; //transfer_buffer或者transfer_dma变量所指向的缓冲区的大小(因为一个
  31. urb 只能使用其中的一个)。若值为0,两个传输缓冲区都没有被USB core使用
  32. u32 actual_length; //当urb结束之后,该变量被设置为urb所发出的数据(OUT urb)或者urb所接收
  33. 的数据(IN urb)的实际长度。对于IN urb,必须使用该变量而不是
  34. transfer_buffer_length变量,因为所接收的数据可能小于整个缓冲区的尺寸
  35. unsigned char *setup_packet; //指向控制urb的设置数据包的指针。它在传输缓冲区中的数据之前被传送。该变量
  36. 只对控制urb有效
  37. dma_addr_t setup_dma; //控制urb用于设置数据包的DMA缓冲区。它在普通传输 缓冲区中的数据之前被传送
  38. 该变量只对控制urb有效
  39. int start_frame; //设置或者返回初始的帧数量,用于等时传输
  40. int number_of_packets; //仅对等时urb有效,指定该urb所助理的等时传输缓冲区的数量。对于等时urb,在
  41. urb被发送到USB core之前,USB驱动程序必须设置该值
  42. int interval; //urb被轮询的时间间隔。仅对中断或者等时urb有效
  43. int error_count; //有USB core设置,仅用于等时urb结束之后。它表示报告了任何一种类型错误的等
  44. 时传输的数量
  45. void *context; //指向一个可以被USB驱动程序设置的数据块。它可以在结束处理程序中,当urb被返
  46. 回到驱动程序时使用。
  47. usb_complete_t complete; //指向一个结束处理程序的指针,让urb被完全传输 或者发生错误时,USB core将调
  48. 用该函数。在该函数中,USB驱动程序可以检查urb,释放它,或者把它重新提交到
  49. 另一个传输中去
  50. struct usb_iso_packet_descriptor iso_frame_desc[0];
  51. //仅对等时urb有效。
  52. };
URB处理流程

一个典型的urb生命周期如下:
1.由USB设备驱动程序创建
2.分配给一个特定的USB设备的特定端点
3.由USB设备驱动程序递交给USB core
4.由USB core递交到特定设备的特定USB Host controller驱动程序
5.由USB Host controller驱动程序处理,它从设备进行USB传送
6.当urb结束之后,USB Host controller驱动程序通知USB设备驱动程序

urb可以在任何时候被递交该urb的驱动程序取消掉,或者被USB核心取消,如果该设备已从系统中移除。urb被动态的创建,它包含一个内部引用计数,使得它们可以在最后一个使用者释放它们时自动地销毁

实例分析

我们刚刚分析了USB驱动的整体结构,也说明了URB的用途和处理流程,接下来我们就找一个实际的例子来看看以下是kernel/drivers/input/touchscreen/usbtouchscreen.c文件中的probe函数
(只保留了有关input 和 urb处理的部分)

  1. static int usbtouch_probe(struct usb_interface *intf,
  2. const struct usb_device_id *id)
  3. {
  4. struct usbtouch_usb *usbtouch;
  5. struct input_dev *input_dev;
  6. struct usb_endpoint_descriptor *endpoint;
  7. struct usb_device *udev = interface_to_usbdev(intf);
  8. struct usbtouch_device_info *type;
  9. int err = -ENOMEM;
  10. ...
  11. input_dev = input_allocate_device(); //input
  12. ...
  13. usbtouch->type = type;
  14. if (!type->process_pkt)
  15. type->process_pkt = usbtouch_process_pkt; //之后会在urb完成处理函数中调用
  16. ...
  17. usbtouch->irq = usb_alloc_urb(0, GFP_KERNEL); //urb的创建,(urb流程的第一步)
  18. ...
  19. input_dev->name = usbtouch->name;
  20. input_dev->phys = usbtouch->phys;
  21. usb_to_input_id(udev, &input_dev->id);
  22. input_dev->dev.parent = &intf->dev;
  23. input_set_drvdata(input_dev, usbtouch);
  24. input_dev->open = usbtouch_open;
  25. input_dev->close = usbtouch_close;
  26. input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
  27. input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
  28. input_set_abs_params(input_dev, ABS_X, type->min_xc, type->max_xc, 0, 0);
  29. input_set_abs_params(input_dev, ABS_Y, type->min_yc, type->max_yc, 0, 0);
  30. if (type->max_press)
  31. input_set_abs_params(input_dev, ABS_PRESSURE, type->min_press,
  32. type->max_press, 0, 0)
  33. ...
  34. //中间这一部分是关于input的设置
  35. if (usb_endpoint_type(endpoint) == USB_ENDPOINT_XFER_INT)
  36. usb_fill_int_urb(usbtouch->irq, udev, //分配urb给特定USB的特定端点(urb处理流程第二步)
  37. usb_rcvintpipe(udev, endpoint->bEndpointAddress),
  38. usbtouch->data, usbtouch->data_size,
  39. usbtouch_irq, usbtouch, endpoint->bInterval); //usbtouch_irq就是urb处理完成函数
  40. else
  41. usb_fill_bulk_urb(usbtouch->irq, udev,
  42. usb_rcvbulkpipe(udev, endpoint->bEndpointAddress),
  43. usbtouch->data, usbtouch->data_size,
  44. usbtouch_irq, usbtouch);
  45. ...
  46. err = input_register_device(usbtouch->input); //注册到input子系统
  47. ...
  48. if (usbtouch->type->irq_always) { //this can‘t fail 一定会进这个if
  49. /* this can't fail */
  50. usb_autopm_get_interface(intf);
  51. err = usb_submit_urb(usbtouch->irq, GFP_KERNEL); //递交给USB core (urb处理流程第四步)
  52. ...
  53. }
  54. return 0;
  55. ...
  56. }

上述urb处理流程的第4、 5步由USB core来处理,等到urb处理完,会调用urb处理完成函数,如下:

  1. static void usbtouch_irq(struct urb *urb)
  2. {
  3. struct usbtouch_usb *usbtouch = urb->context;
  4. struct device *dev = &usbtouch->interface->dev;
  5. int retval;
  6. switch (urb->status) { //在完成处理函数中,一般都会判断一下urb的状态
  7. case 0:
  8. /* success */
  9. break;
  10. case -ETIME:
  11. /* this urb is timing out */
  12. dev_dbg(dev,
  13. "%s - urb timed out - was the device unplugged?\n",
  14. __func__);
  15. return;
  16. case -ECONNRESET:
  17. case -ENOENT:
  18. case -ESHUTDOWN:
  19. case -EPIPE:
  20. /* this urb is terminated, clean up */
  21. dev_dbg(dev, "%s - urb shutting down with status: %d\n",
  22. __func__, urb->status);
  23. return;
  24. default:
  25. dev_dbg(dev, "%s - nonzero urb status received: %d\n",
  26. __func__, urb->status);
  27. goto exit;
  28. }
  29. usbtouch->type->process_pkt(usbtouch, usbtouch->data, urb->actual_length);
  30. //还记得上面说的拿个(要被urb完成处理函数调用的)函数吗?
  31. ...
  32. }

以下就是usbtouch->type->process_pkt所指向的函数

  1. static void usbtouch_process_pkt(struct usbtouch_usb *usbtouch,
  2. unsigned char *pkt, int len)
  3. { //明显就是上报点的函数
  4. struct usbtouch_device_info *type = usbtouch->type;
  5. if (!type->read_data(usbtouch, pkt))
  6. return;
  7. input_report_key(usbtouch->input, BTN_TOUCH, usbtouch->touch);
  8. if (swap_xy) {
  9. input_report_abs(usbtouch->input, ABS_X, usbtouch->y);
  10. input_report_abs(usbtouch->input, ABS_Y, usbtouch->x);
  11. } else {
  12. input_report_abs(usbtouch->input, ABS_X, usbtouch->x);
  13. input_report_abs(usbtouch->input, ABS_Y, usbtouch->y);
  14. }
  15. if (type->max_press)
  16. input_report_abs(usbtouch->input, ABS_PRESSURE, usbtouch->press);
  17. input_sync(usbtouch->input);
  18. }

这样我们就用这个usbtouchscreen.c文件中的代码理清了USB设备驱动的用法,一般的挂载在i2c总线上的TP是通过触发中断来执行中断处理函数,在中断处理函数的下半部完成报点,而挂载在USB总线后,是通过urb处理完成,来出发urb完成处理函数,在urb完成处理函数中,完成报点。

发表评论

表情:
评论列表 (有 0 条评论,785人围观)

还没有评论,来说两句吧...

相关阅读