Linux设备驱动:USB设备驱动

本文介绍了Linux设备驱动的概念,特别是USB设备驱动的工作原理。详细分析了USB设备驱动的主体结构体,包括usb_skel、usb_device、接口描述以及探测函数probe。USB请求块urb作为USB通信的数据结构也进行了探讨。随着USB技术的发展,掌握USB驱动对于设备连接至关重要。

1、Linux设备驱动

设备驱动时操作系统内核和机器硬件之间的接口,为应用程序屏蔽了硬件的细节,由一组函数和一些私有数据组成,结构体内包含设备驱动所需要的所有资源,即驱动器对象所拥有的属性及成员。应用程序看待硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。设备驱动程序是内核的一部分,它主要完成以下功能:对设备进行初始化,使设备投入运行和退出服务;把设备从内核传送到设备和从设备中接受数据;以及检测设备出现的错误。
设备分为:字符、块、网络设备。字符设备是指存取时没有缓存的设备;块设备的读写都有缓存来支持,并且能够随机存取;网络设备在Linux里做专门的处理,基于BSD unix的socket机制,支持对发送数据和接收数据的缓存,提供流量控制机制以及对多协议的支持。

2、USB设备驱动

USB接口标准支持主机和外部设备之前的数据传送,主机预定了各种类型外部设备的总线带宽。每个USB设备会有一个或者多个逻辑连接点,每个连接点称为端口。每个端口有控制方式、等时方式、中断方式和批量大量方式。主机和设备端口连接称为管道。USB主机驱动由3部分组成:USB主机控制器驱动(HCD)、USB核心驱动(USBD)和不同种类的USB设备类驱动,USB驱动设备通过协议软件的抽象处理来完成与设备的不同功能接口之间的通信。 usb驱动 在Linux USB子系统中,HCD是直接和硬件进行交互的软件模块,是USB协议栈的最底层部分,是USB主机控制器硬件和数据传输的一种抽象。USBD部分是整个USB主机驱动的核心,主要实现的功能有:USB总线管理;USB总线设备管理、USB总线带宽管理、USB的4种类型数据传输、USB HUB驱动、为USB设备驱动提供相关接口、提供应用程序访问USB系统的文件接口等。其中USB HUB作为一类特殊的USB设备,其驱动程序被包含在USBD层。
USB设备驱动框架流程:插入设备->引起中断,驱动总线发现设备->为新设备分配地址(choose_address(udev))->告诉USB设备(usb_set_address),发出命令捕获描述符(usb_get_device_descriptor、usb_get_configuration)->device_add,将设备加入usb_bus_type的dev链中。遵循设备模型——总线、设备、驱动,usb设备驱动主要结构体struct usb_driver,他们向USB核心代码描述USB驱动程序。USB的驱动分为两块,一块是USB的bus驱动,形象得说,USB的bus驱动相当于铺出一条路来,让所有的信息都可以通过这条USB通道到达该到的地方,这部分工作由usb_core来完成。当USB设备接到USB控制器接口时,usb_core就检测该设备的一些信息,例如生产厂商ID和产品的ID,或者是设备所属的class、subclass跟protocol,以便确定应该调用哪一个驱动处理该设备。我们要做的是另一块工作——usb的设备驱动。也就是说,我们就等着usb_core告诉我们要工作了,我们才工作。主要是分配/设置usb_driver结构体,做好.probe函数。
在嵌入式Linux系统中,已经包含HCD模块和USB核心驱动USBD,不需要用户重新编写,用户仅仅需要完成USB设备类驱动即可。

3、主体结构体分析

3.1 usb驱动的骨架usb-skel(drivers/usb/usb-skeleton.c):

 struct usb_skel {
			      struct usb_device   *udev;          /* the usb device for this device */
			      struct usb_interface    *interface;     /* the interface for this device */
			      struct semaphore    limit_sem;      /* limiting the number of writes in progress */
			      struct usb_anchor   submitted;      /* in case we need to retract our submissions */
			      struct urb      *bulk_in_urb;       /* the urb to read data with */
			      unsigned char           *bulk_in_buffer;    /* the buffer to receive data */
			      size_t          bulk_in_size;       /* the size of the receive buffer */
			      size_t           bulk_in_filled;     /* number of bytes in the buffer */
			      size_t          bulk_in_copied;     /* already copied to user space */
			      __u8            bulk_in_endpointAddr;   /* the address of the bulk in endpoint */
			      __u8            bulk_out_endpointAddr;  /* the address of the bulk out endpoint */
			      int         errors;         /* the last request tanked */
			      bool            ongoing_read;       /* a read is going on */
			      spinlock_t      err_lock;       /* lock for errors */
			      struct kref     kref;
			      struct mutex        io_mutex;       /* synchronize I/O with disconnect */
			      wait_queue_head_t   bulk_in_wait;       /* to wait for an ongoing read */
			  };

Usb_skel:拥有描述usb设备的结构体udev,一个接口interface,用于并发控制的semaphore信号量limit_sem,用于接收数据缓存的bulk_in_buffer以及尺寸bulk_in_size,批量输入输出端口地址的bulk_in_endpointAddr、bulk_out_endpointAddr,内核使用的引用计数器kref。

3.2 描述usb设备的结构体usb_device(include/linux/usb.h):

/**
			 * struct usb_device - kernel's representation of a USB device
			 * Notes:
			 * Usbcore drivers should not set usbdev->state directly.  Instead use
			 * usb_set_device_state().
			 */
			struct usb_device {
			    int     devnum;         //设备号,还记得上一篇USB命名"设备号-端口号:配置.接口"命名方式,每插入一个新设备,USBCore会为其设置一个设备号
			    char    devpath[16]; //该设备在SysFS中的路径,一般为"/sys/devices/pci0000:00/0000:00:12.2/_usb_1"
			    u32     route;
			    enum usb_device_state   state;  //该设备的状态,如刚插上时为Attached
			    enum usb_device_speed   speed;  //速度级别 high full low
			
			    struct usb_tt   *tt;            //我们知道高速USB之前还存在全速和低速USB,那么高速USB设备怎么兼容其他
			    int     ttport;                 //低速设备,通过使用TT(Transaction Translator)--高速USB中的转换电路
			
			    unsigned int toggle[2];         //对于中断传输、批量传输、和控制传输,传输数据时,需要DATA0和DATA1交
			                                    //替进行,toggle就是标识0端点的IN 和 OUT的DATAx的状态
			    struct usb_device *parent;      //父hub,如果是roothub,就是NULL
			    struct usb_bus *bus;            //设备所在的总线
			    struct usb_host_endpoint ep0;   //端点0被特殊对待,在结构体中静态存在
			
			    struct device dev;              //嵌入到usb_device中的device
			
			    struct usb_device_descriptor descriptor;    //设备描述符,描述该设备信息,后面会分析
			    struct usb_host_bos *bos;
			    struct usb_host_config *config;             //所有的配置信息列表
			
			    struct usb_host_config *actconfig;          //当前活跃的信息
			    struct usb_host_endpoint *ep_in[16];        //该设备的输入端点
			    struct usb_host_endpoint *ep_out[16];       //输出端点
			
			    char **rawdescriptors;
			
			    unsigned short bus_mA;                      //能够从总线得到的电流值
			    u8 portnum;
			    u8 level;
			
			    unsigned can_submit:1;
			    unsigned persist_enabled:1;
			    unsigned have_langid:1;
			    unsigned authorized:1;
			    unsigned authenticated:1;
			    unsigned wusb:1;
			    unsigned lpm_capable:1;
			    unsigned usb2_hw_lpm_capable:1;
			    unsigned usb2_hw_lpm_besl_capable:1;
			    unsigned usb2_hw_lpm_enabled:1;
			    unsigned usb2_hw_lpm_allowed:1;
			    unsigned usb3_lpm_u1_enabled:1;
			    unsigned usb3_lpm_u2_enabled:1;
			    int string_langid;
			
			    /* static strings from the device */
			    char *product;
			    char *manufacturer;
			    char *serial;
			
			    struct list_head filelist;
			
			    int maxchild;
			
			    u32 quirks;
			    atomic_t urbnum;
			
			    unsigned long active_duration;
			
			#ifdef CONFIG_PM
			    unsigned long connect_time;
			
			    unsigned do_remote_wakeup:1;
			    unsigned reset_resume:1;
			    unsigned port_is_suspended:1;
			#endif
			    struct wusb_dev *wusb_dev;
			    int slot_id;
			    enum usb_device_removable removable;
			    struct usb2_lpm_parameters l1_params;
			    struct usb3_lpm_parameters u1_params;
			    struct usb3_lpm_parameters u2_params;
			    unsigned lpm_disable_count;
			};
#define to_usb_device(d) container_of(d, struct usb_device, dev)

其中:struct usb_device *parent//表示设备从root hub开始一个个往外连,每个口连一个usb设备,一个hub可以有多个口,一级级的往下连。
usb_device结构是所有USB专门的描述符的根节点,通过扩展或内嵌标准的USB描述符来说明描述符的分级结构,有助于保存指向可选配置和接口的指针。向usb平台描述usb驱动程序。
其中发现有两处调用usb_device :usb_alloc_dev(usb.c分配一个dev usb_hcd主机控制器驱动)usb_set_configuaration(message.c设置一个特定设备为当前设备,激活当前的配置作为默认工作配置)

3.3 接口usb_interfaceusb_device(include/linux/usb.h):

			/**
			   * struct usb_interface - what usb device drivers talk to
			*/
			struct usb_interface {
			      /* array of alternate settings for this interface,
			       * stored in no particular order */
			      struct usb_host_interface *altsetting; //包含所有可用于该接口的选项设置
			
			      struct usb_host_interface *cur_altsetting;  /* the currently
			                       * active alternate setting */ //当前激活的接口配置
			      unsigned num_altsetting;    /* number of alternate settings *///可选择的接口设置
			
			      /* If there is an interface association descriptor then it will list
			       * the associated interfaces */
			      struct usb_interface_assoc_descriptor *intf_assoc;
			
			      int minor;          /* minor number this interface is
			                       * bound to */ //当捆绑该接口的USB驱动使用主设备时,usb core分配的次设备号。
			      enum usb_interface_condition condition;     /* state of binding */
			      unsigned sysfs_files_created:1; /* the sysfs attributes exist */
			      unsigned ep_devs_created:1; /* endpoint "devices" exist */
			      unsigned unregistering:1;   /* unregistration is in progress */
			      unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */
			      unsigned needs_altsetting0:1;   /* switch to altsetting 0 is pending */
			      unsigned needs_binding:1;   /* needs delayed unbind/rebind */
			      unsigned resetting_device:1;    /* true: bandwidth alloc after reset */
			      unsigned authorized:1;      /* used for interface authorization */
			
			      struct device dev;      /* interface specific device info */
			      struct device *usb_dev;
			      struct work_struct reset_ws;    /* for resets in atomic context */
			  };
  #define to_usb_interface(d) container_of(d, struct usb_interface, dev)

在逻辑上,一个USB设备的功能是通过接口来完成的,接口通过struct usb_interface来描述

3.4 探测函数probe:

.probe = hub_probe
当一个设备被安装而USB核心认为i该驱动程序应该处理时,探测函数被调用。探测函数应该检查传递给他的设备信息,确认驱动程序是否适合该设备。确认后struct usb_interface 应恰当初始化,然后返0,如果出错则返负值。
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id),分别USB设备的接口描述(一般时该设备的第0号接口,该接口的默认设置也是0号)跟它的设备ID描述(包括Vendor ID、Production ID等)。USB驱动程序应该初始化任何可能用于控制USB设备的局部结构体,把所需的任何设备相关信息保存到局部结构体。
功能如下: 探测设备的端点地址、缓冲区大小,初始化任何可能用于控制USB设备的数据结构; 把已经初始化数据结构的指针保存到接口设备中;注册USB设备

3.5 USB请求块urb——USB通信的数据结构:

             /**
			   * struct urb - USB Request Block
			 */
			  struct urb {
			      /* private: usb core and host controller only fields in the urb */
			      struct kref kref;       /* reference count of the URB */
			      void *hcpriv;           /* private data for host controller */
			      atomic_t use_count;     /* concurrent submissions counter */
			      atomic_t reject;        /* submissions will fail */
			      int unlinked;           /* unlink error code */
			
			      /* public: documented fields in the urb that can be used by drivers */
			      struct list_head urb_list;  /* list head for use by the urb's
			                       * current owner */
			      struct list_head anchor_list;   /* the URB may be anchored */
			      struct usb_anchor *anchor;
			      struct usb_device *dev;     /* (in) pointer to associated device */
			      struct usb_host_endpoint *ep;   /* (internal) pointer to endpoint */
			      unsigned int pipe;      /* (in) pipe information */
			      unsigned int stream_id;     /* (in) stream ID */
			      int status;         /* (return) non-ISO status */
			      unsigned int transfer_flags;    /* (in) URB_SHORT_NOT_OK | ...*/
			      void *transfer_buffer;      /* (in) associated data buffer */
			      dma_addr_t transfer_dma;    /* (in) dma addr for transfer_buffer */
			      struct scatterlist *sg;     /* (in) scatter gather buffer list */
			      int num_mapped_sgs;     /* (internal) mapped sg entries */
			      int num_sgs;            /* (in) number of entries in the sg list */
			      u32 transfer_buffer_length; /* (in) data buffer length */
			      u32 actual_length;      /* (return) actual transfer length */
			      unsigned char *setup_packet;    /* (in) setup packet (control only) */
			      dma_addr_t setup_dma;       /* (in) dma addr for setup_packet */
			      int start_frame;        /* (modify) start frame (ISO) */
			      int number_of_packets;      /* (in) number of ISO packets */
			      int interval;           /* (modify) transfer interval
			                       * (INT/ISO) */
			      int error_count;        /* (return) number of ISO errors */
			      void *context;          /* (in) context for completion */
			      usb_complete_t complete;    /* (in) completion routine */
			      struct usb_iso_packet_descriptor iso_frame_desc[0];
			                      /* (in) ISO ONLY */
                 };

urb以一种异步方式同一个特定的USB设备的特定端点发送和接收数据。分配多个urb给一个端点和重用单个urb给多个不同的端点。一个urb的流程如下:(1)被创建;(2)被分配给一个特定的USB设备端点;(3)被提交给USB核心;(4)被USB核心提交给特定设备的特定USB主机控制器驱动;(5)被USB主机控制器驱动处理,并传送给设备;(6)USB主机控制器驱动通知USB设备驱动。

4、总结

目前,今天学习主要是简单的USB驱动概念,USB的应用越来越广泛,传输速率越来越高,很多设备都是USB驱动,USB总线与各种新型设备互连成为可能。以后将扩展相关usb驱动(通用和特殊)以及摄像头相关驱动和应用(人脸识别、高拍仪等)处理。

5、参考链接

http://www.oh100.com/kaoshi/caozuoxitong/605956.html
https://blog.csdn.net/jimmy_1986/article/details/5838297
https://www.sohu.com/a/201480566_777180
https://blog.csdn.net/zuodenghuakai/article/details/83892902
https://www.cnblogs.com/general001/articles/2319552.html

以下是新的个人博客的地址:http://106.53.224.102:8080/wordpress-zh/feng_8071/linux设备驱动:usb设备驱动/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值