版权声明:本文为博主原创文章,未经博主允许不得转载。
https://blog.csdn.net/huangweiqing80/article/details/82999949
USB设备固件
固件(Firmware)就是写入EROM或EEPROM中的程序。通俗的理解就是“固化的软件”。更简单的说,固件即使BIOS(基本输入输出软件)的软件,但又与平台软件完全不同,它是固化在集成电路内部的程序代码,负责控制和协调集成电路的功能。USB固件中包含了USB设备的出厂消息,标识该设备的厂商ID、产品ID、主版本号和次版本号等。这就是为什么当我们把U盘插入USB口的时候主机就能知道这是一个U盘设备
另外固件中还包含一组程序,这组程序主要完成两个任务:USB协议的处理和设备的读写操作。例如将数据从设备发送到总线上,或从总线中将数据读取到设备存储器中。对设备的读写需要固件程序来完成,所以固件程序应该了解对设备读写的方法。**我们的驱动程序只是将USB规范定义的请求发送给固件程序,固件程序负责将数据写入设备的存储器中。**现在的一些U盘病毒,例如exe文件夹图标病毒,可以破坏USB固件中的程序,导致U盘损害,在使用U盘是,需要引起读者的主要。
USB设备固件和USB驱动之间通信的规范是通过USB协议来完成的。通俗的讲,USB协议规定了USB设备之间是如何通信的。
USB 设备驱动模型同样采用的是Linux中的总线-驱动-设备模型,所以它同样有三个部分:
- USB Bus
- USB Device
- USB Driver
二、USB Bus(usb_bus_type和usb_bus)
每一条USB总线对应一个struct usb_bus结构体变量.
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
.pm = &usb_bus_pm_ops,
};
struct bus_type表示总线的类型,而usb_bus_type定义了一种usb总线类型,通过bus_register(&usb_bus_type)让系统知道有usb这么一个类型的总线。
/drivers/usb/core/usb.c
static int __init usb_init(void)
{
int retval;
if (nousb) {
pr_info("%s: USB support disabled\n", usbcore_name);
return 0;
}
retval = usb_debugfs_init();
if (retval)
goto out;
usb_acpi_register();
retval = bus_register(&usb_bus_type);
...
}
subsys_initcall(usb_init);
而一个总线类型(usb_bus_type)和一条总线(usb_bus)是两码子事儿。从硬件上来讲,一个host controller就会连出一条usb总线,而从软件上来讲,不管你有多少个host controller,或者说有多少条总线,它们通通属于usb_bus_type这么一个类型,只是每一条总线对应一个struct usb_bus结构体变量,这个变量在host controller的驱动程序中去申请。
HCD is bus. struct usb_hcd包含一个struct usb_bus.
二、USB Device
当USB设备插入USB插槽时,会引起一个电信号的变化,主机控制器捕获这个电信号,并命令USB核心处理对设备的加载工作。USB核心读取USB设备固件中的设备的信息,分配一个usb_device,并将设备固件中的设备信息填充到这个usb_device结构体中,并将这个usb_device挂载到USB总线中的设备链表中。
最后与挂载在USB总线上的驱动程序相比较,如果找到合适的驱动程序usb_driver,就会调用驱动程序的probe函数。
-
USB设备的逻辑结构
无论是硬件设计人员,还是软件设计人员,在设计USB硬件或者软件时,都会参考USB协议。没有人能够凭空想象出一种USB硬件,也没有人可以不参考USB协议就能编写驱动USB设备的软件。
USB协议规定,USB设备的逻辑结构包含设备、配置、接口和端口
-
设备(usb_device)
在Linux内核中,一个USB设备(包括复合设备)用usb_device结构体表示。复合设备是指多功能设备,例如多功能打印机,其有扫描、复印、打印功能。usb_device结构体表示封装在一起的整个设备(多功能打印机这个设备)。这与设备驱动模型中的device结构体不同。
从设备驱动模型的观念来看,复合设备的每一个功能都可以用一个device结构体表示。所以从多功能打印机的例子来看,这就表示整体的usb_device包含3个局部功能的device结构体。但实际的usb_device结构体中只包含device结构体,代码如下
struct usb_device {
...
struct device dev;
...
}
所以驱动开发者引入了一个新的接口结构体(usb_interface)来代替device中的功能。这样在本例中,这个复合设备(usb_device)就有3个usb_interface结构体。
- 配置(usb_host_config)
一个配置就是一组不同功能的组合。一个USB设备(usb_device)可以有多个配置(usb_host_config),配置之间可以切换以改变设备的状态。例如,对于上面介绍的多功能打印机有3种功能,可以将这3种功能分为2个配置。第1个配置包含扫描功能,第2个配置包含复印和打印功能。一般情况下,Linux系统在同一时刻只能激活一个配置。
例如,对于一个允许下载固件升级的MP3来说,一般可以有3种配置。第一种是播放配置0,第2种是充电配置1,第3种是下载弓箭配置2。当需要下载固件时,需要将MP3设置为配置2状态。
在linux中,使用usb_host_config结构体表示配置。USB设备驱动程序通常不需要操作usb_host_config结构体,该结构体中的成员有USB core维护,所以这里就不详细介绍了。
struct usb_host_config {
struct usb_config_descriptor desc;
char *string; /* iConfiguration string, if present */
/* List of any Interface Association Descriptors in this
* configuration. */
struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS];
/* 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;
};
- 接口(usb_interface)
在USB协议中,接口(usb_interface)代表一个基本的功能。USB接口只处理一种USB逻辑连接,例如鼠标、键盘或者音频流。上文的多功能打印机包含3个基本的功能,所以具有3个接口:一个扫描功能接口、一个复印功能接口和一个打印功能接口。因为一个USB接口代表一种基本的功能,而根据设备驱动模型的观念,每一个USB驱动程序(usb_driver)控制一个接口。因此,以多功能打印机为例,Linux需要3个不同的驱动程序处理硬件设备。
内核使用struct usb_interface结构体来表述USB接口。USB核心在设备插入时,会读取USB设备的信息,并创建一个usb_device结构体。然后读取USB设备接口的信息,并创建usb_interface结构体。接着USB核心在USB总线上找到合适的USB驱动程序,并调用驱动程序的probe()函数,将usb_interface传递给驱动程序(match函数调用probe的时候传进这个usb_interface参数)。probe()函数在前面已经反复讲过,它的原型是:
该函数的第一个参数就是指向USB核心分配的usb_interface结构体的指针,驱动程序从这里得到这个接口结构体,并负责控制该结构体。因为一个接口代表一种基本的功能,所以驱动程序也只负责该接口所代表的功能。probe()函数的第二个参数从设备读取usb_device_id信息,用来与驱动程序匹配。int (*probe) (struct usb_interface *intf,const struct usb_device_id *id);
USB核心处理usb_interface中大量的成员,只有少数几个成员驱动程序会用到,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 */
/* 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 */
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 reset_running:1;
unsigned resetting_device:1; /* true: bandwidth alloc after reset */
struct device dev; /* interface specific device info */
struct device *usb_dev;
atomic_t pm_usage_cnt; /* usage counter for autosuspend */
struct work_struct reset_ws; /* for resets in atomic context */
};
一个接口可以有多个设置,struct usb_interface 结构体中的struct usb_host_interface *altsetting就是一个设置数组,它代表了一个或多个设置。
下面我们来看一下usb_host_interface 这个结构体
/* host-side wrapper for one interface setting's parsed descriptors */
struct usb_host_interface {
struct usb_interface_descriptor desc;
int extralen;
unsigned char *extra; /* Extra descriptors */
/* 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 */
};
里面有一个重要的结构体struct usb_interface_descriptor desc,这是一个接口描述符。接口描述符是描述接口本身的信息。因为一个接口可以有多个设置,使用不同的设置。描述接口的信息也会有所不同,所以接口描述符并没有放在struct usb_interface 结构体中,而是放在表示接口设置的struct usb_host_interface 结构中。usb_host_interface 中的desc成员就是接口的某个设置的描述符
struct usb_interface_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bInterfaceNumber;
__u8 bAlternateSetting;
__u8 bNumEndpoints;
__u8 bInterfaceClass;
__u8 bInterfaceSubClass;
__u8 bInterfaceProtocol;
__u8 iInterface;
} __attribute__ ((packed));
- 端点(usb_host_endpoint)
端点是USB通信的最基本形式。主机只能通过端点与USB设备进行通信,也就是只能通过端点传输数据。USB只能向一个方向传输数据,或者从主机到设备,或者从设备到主机。从这个特性来看,端点就像一个单向的管道,只负责数据的单向传输。
从主机到设备传输数据的端点,叫输出端点;相反,从设备到主机传输数据的端点,叫做输入端点。对于U盘这种可以存取数据的设备,至少需要一个输入端点,一个输出端点。另外还包含一个端点0,叫做控制端点,用来控制初始化U盘的参数等工作。所以U盘应该有3个端点,其中对于任何设备来说端点0是不可缺少的。后面对端点0进行详细的介绍。usb_host_endpoint的定义如下
struct usb_host_endpoint {
struct usb_endpoint_descriptor desc;
struct usb_ss_ep_comp_descriptor ss_ep_comp;
struct list_head urb_list;
void *hcpriv;
struct ep_device *ep_dev; /* For sysfs info */
unsigned char *extra; /* Extra descriptors */
int extralen;
int enabled;
};
我们看到在struct usb_host_endpoint结构体中有一个重要的成员desc,他是struct usb_endpoint_descriptor类型的,下面我们来看一下usb_endpoint_descriptor这个结构体
- 端点描述符(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));
成员bEndpointAddress和成员bmAttributes非常重要,他们决定了端点的端点地址,端点号和端点的传输方式。
端点的传输方式有4种。注意不是有4个,它们分别是控制传输、中断传输、批量传输和等时传输。
- USB的数据通信是基于传输(Transfer)的,传输的类型有:中断传输、批量传输、同步传输和控制传输。
- 一次传输由一个或多个事务(transaction)构成,事务可分为:In事务、Out事务和Setup事务
- 一个事务由一个或多个包(packet)构成,包可分为:令牌包(setup)、数据包(data)、握手包(ACK)和特殊包
- 一个包由多个域构成,域可分为同步域(SYNC)、标识域(PID)、地址域(ADDR)、端点域(ENDP)、帧号域(FRAM)、数据域(DATA)、校验域(CRC)
下面来看一张抓到的USB传输包,看一下USB传输包的构成是否如此
- 总结
USB描述符里存储了USB设备的名字、生产厂商和型号等。USB描述符主要有4种,分别是设备描述符、配置描述符、接口描述符和端点描述符。USB协议中规定一个USB设备是必现支持这4个描述符的,当然也有其他的一些描述符(字符串描述符)让设备显得个性些,但这4个描述符是一个都不能少的。这些USB描述符作为USB固件的一部分,被存储在USB设备的EEPROM中,一般通过量产工具对这些设备描述符进行设置。
实际上,USB设备的EEPROM中,固化的固件就是这些描述符和一些程序,当USB设备插入USB插槽时,会引起一个电信号的变化,主机控制器捕获这个电信号,并命令USB核心处理对设备的加载工作。USB核心读取USB设备固件中的设备的信息,分配一个usb_device,并将设备固件中的设备信息填充到这个usb_device结构体中,并将这个usb_device挂载到USB总线中的设备链表中。最后与挂载在USB总线上的驱动程序相比较,如果找到合适的驱动程序usb_driver,就会调用驱动程序的probe函数。实际上USB核心读取的就是USB设备固件中的这些描述符。
USB核心读取设备固件中的设备信息,叫做USB设备的枚举。USB设备在正常工作前,第一件要做的事就是枚举。枚举是让主机认得这个USB设备,并且为该设备准备资源,建立号主机与设备之间的数据传递通道。
一次USB设备枚举需要通过哪些操作:
- 获取设备描述符
- 复位
- 设置地址
- 再次获取设备描述符
- 获取配置描述符
- 获取接口、端点描述符
- 获取字符串描述符
- 选择设备配置
那么USB主机控制器又是如何去获取这些描述符的呢?
先来看一下USB主机控制器是如何来获取USB设备描述符的
从这我们也可以知道一次传输也并不是一次性发送的,它需要由多个发送包和多个接收包来完成。这边获取设备描述符就是主机控制器先发送请求包给USB设备,USB设备在把描述符打包成一个包发送过来。
下面再来看一下USB设备中的重要结构体:
10. struct device:
The Basic Device Structure, generic device interface(所有设备的抽象)
-struct bus_type bus; / type of bus device is on */
-struct device_driver driver; / which driver has allocated this device */
- struct usb_device:
kernel’s representation of a USB device (它包含struct device,USB设备)
-struct device dev; /* generic device interface */
-struct usb_device_descriptor descriptor; /* USB device descriptor */
- struct usb_bus bus; / bus we’re part of /
-struct usb_host_endpoint ep0; / endpoint 0 data (default control pipe) */
-struct usb_host_config config; / all of the device’s configs */
-struct usb_host_config actconfig; / the active configuration */
-struct usb_host_endpoint ep_in[16]; / array of IN endpoints */
-struct usb_host_endpoint ep_out[16]; / array of OUT endpoints */
- struct usb_host_config
representation of a device’s configuration
/* array of pointers to usb_interface structures, one for each interface in the configuration. These pointers are valid only while the the configuration is active.*/
-struct usb_interface *interface[USB_MAXINTERFACES];
/* array of pointers to usb_interface_cache structures, one for each interface in the configuration. These structures exist for the entirelife of the device. Interface information availableeven when this isnot activeconfiguration */
-struct usb_interface_cache *intf_cache[USB_MAXINTERFACES];
- struct usb_interface
what usb device drivers talk to
/* array of alternate settings for this interface, stored in no particular order. one for each alternate setting that may be selected. Each one includes a set of endpoint configurations. */
-struct usb_host_interface *altsetting;
-struct usb_host_interface cur_altsetting; / the currently
-
struct usb_host_interface
host-side wrapper for one interface setting’s parsed descriptors
-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; -
struct usb_host_endpoint
host-side endpoint descriptor and queue
/* descriptor for this endpoint, e.g.: wMaxPacketSize */
-struct usb_endpoint_descriptor desc;
/urbs queued to this endpoint; maintained by usbcore/
-struct list_head urb_list;
/* for use by HCD; typically holds hardware dma queue head (QH) */
-void *hcpriv;
/* ep_device for sysfs info*/
-struct ep_device *ep_dev;
- usb_alloc_dev
usb device constructor (usbcore-internal)
struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
@parent: hub to which device is connected; null to allocate a root hub
@bus: bus used to access the device
@port1: one-based index of port; ignored for root hubs