常见嵌入式驱动程序(嵌入式设备驱动程序基础笔记第24期)

Linux USB子系统分析

在Linux系统中,USB主机驱动程序由3部分组成:USB主机控制器驱动(HCD)USB核心驱动(USBD)不同种类的USB设备类驱动

相关概念

1. USB是主从结构

  • 所有的USB传输,都是从USB主机这方发起;USB设备没有"主动"通知USB主机的能力。
  • 例子:USB鼠标滑动一下立刻产生数据,但是它没有能力通知PC机来读数据,只能被动地等得PC机来读。

2. USB的传输类型:

  • a. 控制传输:可靠,时间有保证,比如:USB设备的识别过程
  • b. 批量传输: 可靠, 时间没有保证, 比如:U盘
  • c. 中断传输:可靠,实时,比如:USB鼠标
  • d. 实时传输:不可靠,实时,比如:USB摄像头

3. USB传输的对象:端点(endpoint)

我们说"读U盘"、"写U盘",可以细化为:把数据写到U盘的端点1,从U盘的端点2里读出数据

除了端点0外,每一个端点只支持一个方向的数据传输

端点0用于控制传输,既能输出也能输入

4. 每一个端点都有传输类型,传输方向

5. 术语里、程序里说的输入(IN)、输出(OUT) "都是" 基于USB主机的立场说的。

比如鼠标的数据是从鼠标传到PC机, 对应的端点称为"输入端点"

6,USB分类

  • UHCI: intel, 低速(1.5Mbps)/全速(12Mbps)
  • OHCI: microsoft 低速/全速
  • EHCI: 高速(480Mbps)
USB总线驱动程序的作用

1. 识别USB设备

  • 1.1 分配地址
  • 1.2 告诉USB设备(set address)
  • 1.3 发出命令获取描述符

描述符的信息可以在include\linux\usb\Ch9.h看到

2. 查找并安装对应的设备驱动程序

3. 提供USB读写函数

常见嵌入式驱动程序(嵌入式设备驱动程序基础笔记第24期)(1)

USB驱动程序概述

USB驱动程序框架: app: ------------------------------------------- USB设备驱动程序 // 知道数据含义 内核 -------------------------------------- USB总线驱动程序 // 1. 识别, 2. 找到匹配的设备驱动, 3. 提供USB读写函数 (它不知道数据含义) ------------------------------------------- USB主机控制器 UHCI OHCI EHCI 硬件 ----------- USB设备

更多有关usb子系统的介绍,参考下面这篇文章(非常详细的介绍了usb子系统)

https://www.oreilly.com/library/view/linux-device-drivers/0596005903/ch13.html

相关结构体

1,usb_device_id

struct usb_device_id { /* which fields to match against? */ __u16 match_flags; /* Used for product specific matches; range is inclusive */ __u16 idVendor; //厂家ID __u16 idProduct; __u16 bcdDevice_lo; __u16 bcdDevice_hi; /* Used for device class matches */ __u8 bDeviceClass; __u8 bDeviceSubClass; __u8 bDeviceProtocol; /* Used for interface class matches */ __u8 bInterfaceClass; __u8 bInterfaceSubClass; __u8 bInterfaceProtocol; /* not matched against */ kernel_ulong_t driver_info; };

2,usb_driver

struct usb_driver { const char *name; int (*probe) (struct usb_interface *intf, const struct usb_device_id *id); void (*disconnect) (struct usb_interface *intf); int (*ioctl) (struct usb_interface *intf, unsigned int code, void *buf); int (*suspend) (struct usb_interface *intf, pm_message_t message); int (*resume) (struct usb_interface *intf); void (*pre_reset) (struct usb_interface *intf); void (*post_reset) (struct usb_interface *intf); const struct usb_device_id *id_table; struct usb_dynids dynids; struct usbdrv_wrap drvwrap; unsigned int no_dynamic_id:1; unsigned int supports_autosuspend:1; };

3,usb_interface

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 */ int minor; /* minor number this interface is * bound to */ enum usb_interface_condition condition; /* state of binding */ unsigned is_active:1; /* the interface is not suspended */ unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */ struct device dev; /* interface specific device info */ struct device *usb_dev; /* pointer to the usb class's device, if any */ int pm_usage_cnt; /* usage counter for autosuspend */ };

4,usb_host_interface

struct usb_host_interface { struct usb_interface_descriptor desc; /* array of desc.bNumEndpoint endpoints associated with this * interface setting. these will be in no particular order. */ struct usb_host_endpoint *endpoint; char *string; /* iInterface string, if present */ unsigned char *extra; /* Extra descriptors */ int extralen; };

5,usb_interface_descriptor

struct usb_interface_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bInterfaceNumber; __u8 bAlternateSetting; __u8 bNumEndpoints; __u8 bInterfaceClass; __u8 bInterfaceSubClass; __u8 bInterfaceProtocol; __u8 iInterface; } __attribute__ ((packed));

6,usb_endpoint_descriptor

struct usb_endpoint_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bEndpointAddress; __u8 bmAttributes; __le16 wMaxPacketSize; __u8 bInterval; /* NOTE: these two are _only_ in audio endpoints. */ /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */ __u8 bRefresh; __u8 bSynchAddress; } __attribute__ ((packed));

7,usb_device

struct usb_device { int devnum; /* Address on USB bus */ char devpath [16]; /* Use in messages: /port/port/... */ enum usb_device_state state; /* configured, not attached, etc */ enum usb_device_speed speed; /* high/full/low (or error) */ struct usb_tt *tt; /* low/full speed dev, highspeed hub */ int ttport; /* device port on that tt hub */ unsigned int toggle[2]; /* one bit for each endpoint * ([0] = IN, [1] = OUT) */ struct usb_device *parent; /* our hub, unless we're the root */ struct usb_bus *bus; /* Bus we're part of */ struct usb_host_endpoint ep0; struct device dev; /* Generic device interface */ struct usb_device_descriptor descriptor;/* Descriptor */ struct usb_host_config *config; /* All of the configs */ struct usb_host_config *actconfig;/* the active configuration */ struct usb_host_endpoint *ep_in[16]; struct usb_host_endpoint *ep_out[16]; char **rawdescriptors; /* Raw descriptors for each config */ unsigned short bus_mA; /* Current available from the bus */ u8 portnum; /* Parent port number (origin 1) */ u8 level; /* Number of USB hub ancestors */ unsigned discon_suspended:1; /* Disconnected while suspended */ unsigned have_langid:1; /* whether string_langid is valid */ int string_langid; /* language ID for strings */ /* static strings from the device */ char *product; /* iProduct string, if present */ char *manufacturer; /* iManufacturer string, if present */ char *serial; /* iSerialNumber string, if present */ struct list_head filelist; #ifdef CONFIG_USB_DEVICE_CLASS struct device *usb_classdev; #endif #ifdef CONFIG_USB_DEVICEFS struct dentry *usbfs_dentry; /* usbfs dentry entry for the device */ #endif /* * Child devices - these can be either new devices * (if this is a hub device), or different instances * of this same device. * * Each instance needs its own set of data structures. */ int maxchild; /* Number of ports if hub */ struct usb_device *children[USB_MAXCHILDREN]; int pm_usage_cnt; /* usage counter for autosuspend */ u32 quirks; /* quirks of the whole device */ #ifdef CONFIG_PM struct delayed_work autosuspend; /* for delayed autosuspends */ struct mutex pm_mutex; /* protects PM operations */ unsigned long last_busy; /* time of last use */ int autosuspend_delay; /* in jiffies */ unsigned auto_pm:1; /* autosuspend/resume in progress */ unsigned do_remote_wakeup:1; /* remote wakeup should be enabled */ unsigned autosuspend_disabled:1; /* autosuspend and autoresume */ unsigned autoresume_disabled:1; /* disabled by the user */ #endif };

8,usb_host_config

struct usb_host_config { struct usb_config_descriptor desc; char *string; /* iConfiguration string, if present */ /* the interfaces associated with this configuration, * stored in no particular order */ struct usb_interface *interface[USB_MAXINTERFACES]; /* Interface information available even when this is not the * active configuration */ struct usb_interface_cache *intf_cache[USB_MAXINTERFACES]; unsigned char *extra; /* Extra descriptors */ int extralen; };

9,usb_bus

struct usb_bus { struct device *controller; /* host/master side hardware */ int busnum; /* Bus number (in order of reg) */ char *bus_name; /* stable id (PCI slot_name etc) */ u8 uses_dma; /* Does the host controller use DMA? */ u8 otg_port; /* 0, or number of OTG/HNP port */ unsigned is_b_host:1; /* true during some HNP roleswitches */ unsigned b_hnp_enable:1; /* OTG: did A-Host enable HNP? */ int devnum_next; /* Next open device number in * round-robin allocation */ struct usb_devmap devmap; /* device address allocation map */ struct usb_device *root_hub; /* Root hub */ struct list_head bus_list; /* list of busses */ int bandwidth_allocated; /* on this bus: how much of the time * reserved for periodic (intr/iso) * requests is used, on average? * Units: microseconds/frame. * Limits: Full/low speed reserve 90%, * while high speed reserves 80%. */ int bandwidth_int_reqs; /* number of Interrupt requests */ int bandwidth_isoc_reqs; /* number of Isoc. requests */ #ifdef CONFIG_USB_DEVICEFS struct dentry *usbfs_dentry; /* usbfs dentry entry for the bus */ #endif struct class_device *class_dev; /* class device for this bus */ #if defined(CONFIG_USB_MON) struct mon_bus *mon_bus; /* non-null when associated */ int monitored; /* non-zero when monitored */ #endif };

上述结构体之间的关系,如下图:

常见嵌入式驱动程序(嵌入式设备驱动程序基础笔记第24期)(2)

USB设备(usb_device)

包括配置(Configurations)、接口(Interfaces)和端点endpoint),以及USB驱动程序如何绑定到USB接口

常见嵌入式驱动程序(嵌入式设备驱动程序基础笔记第24期)(3)

1,端点(endpoint):USB通信最基本的形式叫做端点。

端点分类:Control,lsochronous,Interrupt和bulk

(1)控制传输模式(Control)

控制传输模式支持双向传输,用来处理从usb主机端口到usb设备端口的数据传输,用于控制指令,设备状态查询以及确认命令。

(2)等时传输方式(lsochronous)

等时传输是一种周期性的连续性的意向传输模式,通常用于对时间有着密切关系的信息的传输,对准确性要求不高,但对时间要求极为敏感的设备,如视频,音频的传输。

(3)中断传输模式(Interrupt)

中断传输模式用于非周期性的,自然发生的,数据量小的传输,数据传输的方向是从设备到主机。如usb键盘和鼠标

(4)批量传输模式(bulk)

批量传输模式是一种单向的,用于大量数据传输的模式,该方式用来传输正确无误的数据。通常打印机,扫描仪,数码相机以这种方式与主机连接

备注:usb_host_endpoint 结构体里的 usb_endpoint_descriptor 结构体 就包含了端点的描述:

usb_endpoint_descriptor

  • bEndpointAddress 端点的地址
  • bmAttributes 端点类型
  • wMaxPacketSize 端点一次可以处理的最大字节大小
  • bInterval 如果端点是中断类型的端点,则此值中断请求之间的时间间隔

2,接口(Interfaces)

USB端点被捆绑到接口中,USB接口只能处理一种类型的USB逻辑连接,例如鼠标、键盘或音频流。一些USB设备有多个接口,例如USB扬声器可能由两个接口组成:一个用于按钮的USB键盘和一个USB音频流。因为USB接口代表基本功能,每个USB驱动程序控制一个接口;因此,以扬声器为例,Linux需要为一个硬件设备提供两个不同的驱动程序。

在内核中用 struct usb_interface 结构描述USB接口。这个结构是USB核心传递给USB驱动程序的,也是USB驱动程序负责控制的。该结构中的重要元素包括:

  • struct usb_host_interface *altsetting 包含可为此接口选择的所有备用设置的接口结构数组
  • unsigned num_altsetting altsetting 指针指向的备用设置数
  • struct usb_host_interface *cur_altsetting 指向数组altsetting的指针,表示此接口的当前活动设置
  • int minor 如果绑定到此接口的USB驱动程序使用USB主设备号,则此变量包含USB核心分配给接口的次设备号

3,配置(Configurations)

USB接口本身被捆绑到配置中。USB设备可以有多种配置,并且可以在它们之间切换以更改设备的状态。例如,一些允许下载固件的设备包含多个配置来实现这一点。单个配置只能在一个时间点启用。Linux不能很好地处理多个配置的USB设备,但是它们很少。

Linux使用struct usb_host_config 结构描述USB配置,并使用struct usb_device 结构描述整个USB设备。

USB设备驱动程序通常不需要读取或写入这些结构中的任何值,因此这里不详细定义它们。但可以在内核源代码树的include/linux/usb.h文件中找到它们的描述。

总结上述,USB设备非常复杂,由许多不同的逻辑单元组成。这些单元之间的关系可以简单地描述如下:
  • 1,设备(usb_device)通常有一个或多个配置(Configurations)。
  • 2,配置通常有一个或多个接口(Interfaces)。
  • 3,接口通常有一个或多个设置(Setting )。
  • 4,接口具有零个或多个端点(Endpoint)。
补充:

1,第一个USB设备是根集线器(root hub)。这是USB控制器,通常包含在PCI设备中。控制器之所以这样命名是因为它控制连接到它的整个USB总线。控制器是PCI总线和USB总线之间的桥梁,也是该总线上的第一个USB设备。所有根集线器都由USB核心分配一个唯一的编号。

2,Linux内核中的USB设备使用urb(USB请求块)与所有USB设备通信。这个请求块是用struct urb结构描述的,可以在include/linux/usb.h文件中找到。

3,urb用于以异步方式向特定USB设备上的特定USB端点发送数据或从该端点接收数据。USB设备驱动程序可以为单个端点分配多个urb,或者可以根据驱动程序的需要为多个不同端点重用单个urb。设备中的每个端点都可以处理一个urb队列,以便在队列为空之前将多个urb发送到同一端点。

4,urb典型生命周期如下:

  1. 由USB设备驱动程序创建。
  2. 分配给特定USB设备的特定端点。
  3. 通过USB设备驱动程序提交到USB核心。
  4. 由USB核心提交到指定设备的特定USB主机控制器驱动程序。
  5. 由USB主机控制器驱动程序处理,该驱动程序将USB传输到设备。
  6. 当urb完成时,USB主机控制器驱动程序通知USB设备驱动程序。

5,编写USB设备驱动程序的方法类似于pci驱动程序:驱动程序向USB子系统注册其驱动程序对象,然后使用供应商和设备标识符来判断其硬件是否已安装。

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页