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的数据通信是基于传输(Transfer)的,传输的类型有:中断传输、批量传输、同步传输和控制传输。
  2. 一次传输由一个或多个事务(transaction)构成,事务可分为:In事务、Out事务和Setup事务
  3. 一个事务由一个或多个包(packet)构成,包可分为:令牌包(setup)、数据包(data)、握手包(ACK)和特殊包
  4. 一个包由多个域构成,域可分为同步域(SYNC)、标识域(PID)、地址域(ADDR)、端点域(ENDP)、帧号域(FRAM)、数据域(DATA)、校验域(CRC)

下面来看一张抓到的USB传输包,看一下USB传输包的构成是否如此

在这里插入图片描述

  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核心读取设备固件中的设备信息,叫做USB设备的枚举。USB设备在正常工作前,第一件要做的事就是枚举。枚举是让主机认得这个USB设备,并且为该设备准备资源,建立号主机与设备之间的数据传递通道。

一次USB设备枚举需要通过哪些操作:

  1. 获取设备描述符
  2. 复位
  3. 设置地址
  4. 再次获取设备描述符
  5. 获取配置描述符
  6. 获取接口、端点描述符
  7. 获取字符串描述符
  8. 选择设备配置
    那么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 */

  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

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值