USB设备驱动

版权声明:本文为博主原创文章,未经博主允许不得转载。
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中的总线-驱动-设备模型,所以它同样有三个部分:

  1. USB Bus
  2. USB Device
  3. 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函数。

  1. USB设备的逻辑结构
    无论是硬件设计人员,还是软件设计人员,在设计USB硬件或者软件时,都会参考USB协议。没有人能够凭空想象出一种USB硬件,也没有人可以不参考USB协议就能编写驱动USB设备的软件。
    USB协议规定,USB设备的逻辑结构包含设备、配置、接口和端口
    在这里插入图片描述

  2. 设备(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结构体。

  1. 配置(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;
};
  1. 接口(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()函数在前面已经反复讲过,它的原型是:
    int (*probe) (struct usb_interface *intf,const struct usb_device_id *id);
    
    该函数的第一个参数就是指向USB核心分配的usb_interface结构体的指针,驱动程序从这里得到这个接口结构体,并负责控制该结构体。因为一个接口代表一种基本的功能,所以驱动程序也只负责该接口所代表的功能。probe()函数的第二个参数从设备读取usb_device_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));
  1. 端点(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这个结构体

  1. 端点描述符(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个,它们分别是控制传输、中断传输、批量传输和等时传输。
在这里插入图片描述
在这里插入图片描述

  1. 总结
    在这里插入图片描述
    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设备中的重要结构体:

  1. 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 */

  1. 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 */

  1. 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];

  1. 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

  1. 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;

  2. 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;

  1. 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

猜你喜欢

转载自blog.csdn.net/huangweiqing80/article/details/82999949