USB驱动——设备、配置、接口、设置、端点之前的关系以及它们的获取过程分析

大家常说,一个设备通常有多个配置,配置通常有多个接口,接口通常有多个端点。接口代表逻辑上的设备,比如声卡分为 录音和播放。访问设备时,访问的是某个接口(逻辑设备)。除了端点0之外,每个端点只支持一个传输方向,一种性质的传输传输数据时,读写某个端点,端点是数据通道。

本文首先分析设备、配置、接口、设置、端点之间的关系,然后根据 2440-ochi 驱动程序,分析一个设备注册到内核时,它的这些描述符的获取过程。

一、设备、配置、接口、设置、端点之间的关系

在内核中,一个 USB 设备,无论是 hub 还是普通的USB鼠标等等,它们都使用一个 usb_device 结构体来描述,在 usb_device 结构体中,包含了一个设备描述符和这个设备支持的多个配置。

struct usb_device {
	...
	struct device dev;

	struct usb_device_descriptor descriptor;	// 设备描述符
	struct usb_host_config *config;		// 支持的配置

	struct usb_host_config *actconfig;	// 当前的配置
	...
};

设备描述符

struct usb_device_descriptor {
	__u8  bLength;			// 描述符长度
	__u8  bDescriptorType;	//描述符类型

	__le16 bcdUSB;		//USB版本号
	__u8  bDeviceClass;	//USB分配的设备类
	__u8  bDeviceSubClass;	//USB分配的子类
	__u8  bDeviceProtocol;	//USB分配的协议
	__u8  bMaxPacketSize0;	//endpoint0最大包大小
	__le16 idVendor;	//厂商编号
	__le16 idProduct;	//产品编号
	__le16 bcdDevice;	//设备出厂编号
	__u8  iManufacturer;	//描述厂商字符串的索引
	__u8  iProduct;			//描述产品字符串的索引
	__u8  iSerialNumber;	//描述设备序列号字符串的索引
	__u8  bNumConfigurations;	//可能的配置数量
} __attribute__ ((packed));

设备所包含的配置,配置里包含一个配置描述符,以及该配置所拥有的接口。

struct usb_host_config {
	struct usb_config_descriptor	desc;	// 配置描述符

	char *string;
	struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS];
	struct usb_interface *interface[USB_MAXINTERFACES];	// 接口
	struct usb_interface_cache *intf_cache[USB_MAXINTERFACES];
	unsigned char *extra;
	int extralen;
};

配置描述符,注意它的 wTotalLength ,我们通常将一个配置以及它所包含的接口,接口所包含的端点所有的描述符一次性都获取到,wTotalLength 就是它们全部的长度。

struct usb_config_descriptor {
	__u8  bLength;	//描述符长度
	__u8  bDescriptorType;	//描述符类型编号

	__le16 wTotalLength;	//请求配置所返回的所有数据的大小,当前配置的所有描述符包括所包含的接口、端点描述符
	__u8  bNumInterfaces;	//配置所支持的接口数
	__u8  bConfigurationValue;	//Set_Configuration 命令需要的参数值
	__u8  iConfiguration;	//描述该配置的字符串的索引值
	__u8  bmAttributes;	//供电模式选择
	__u8  bMaxPower;	//设备从总线提取的最大电流
} __attribute__ ((packed));

配置所包含的接口

struct usb_interface {

	struct usb_host_interface *altsetting;	// 一个接口可能有多个设置(一个接口多种功能),也就是这些接口所包含的端点凑起来可能有多种功能
	struct usb_host_interface *cur_altsetting;	// 当前的设置
	unsigned num_altsetting;	/* number of alternate settings */

	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 is_active:1;		/* the interface is not suspended */
	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;

	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_host_interface {
	struct usb_interface_descriptor	desc;	// 接口描述符

	struct usb_host_endpoint *endpoint;

	char *string;		/* iInterface string, if present */
	unsigned char *extra;   /* Extra descriptors */
	int extralen;
};

接口描述符

struct usb_interface_descriptor {
	__u8  bLength;			//描述符长度
	__u8  bDescriptorType;	//描述符类型

	__u8  bInterfaceNumber;	//接口的编号
	__u8  bAlternateSetting;	//备用的接口描述符编号
	__u8  bNumEndpoints;	//该接口使用的端点数,不包括端点0
	__u8  bInterfaceClass;	//接口类型
	__u8  bInterfaceSubClass;	//接口子类型
	__u8  bInterfaceProtocol;	//接口所遵循的协议
	__u8  iInterface;	//描述该接口的字符串的索引值
} __attribute__ ((packed));

端点

struct usb_host_endpoint {
	struct usb_endpoint_descriptor	desc;	// 端点描述符
	struct list_head		urb_list;	// 该端点的 urb 队列
	void				*hcpriv;
	struct ep_device 		*ep_dev;	/* For sysfs info */
	struct usb_host_ss_ep_comp	*ss_ep_comp;	/* For SS devices */

	unsigned char *extra;   /* Extra descriptors */
	int extralen;
	int enabled;
};

端点描述符

struct usb_endpoint_descriptor {
	__u8  bLength;	//描述符长度
	__u8  bDescriptorType;	//描述符类型

	__u8  bEndpointAddress;	//端点地址:0~3位为端点号,第7位为传输方向
	__u8  bmAttributes;		// 端点属性 bit 0-1 00控制 01 同步 02批量 03 中断
	__le16 wMaxPacketSize;	//本端点接收或发送的最大信息包的大小
	__u8  bInterval;	//轮询数据断端点的时间间隔
						//批量传送的端点,以及控制传送的端点,此域忽略
						//对于中断传输的端点,此域的范围为1~255

	/* NOTE:  these two are _only_ in audio endpoints. */
	/* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
	__u8  bRefresh;
	__u8  bSynchAddress;
} __attribute__ ((packed));

二、描述符的获取过程

1、usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);

int usb_get_device_descriptor(struct usb_device *dev, unsigned int size)
{
	struct usb_device_descriptor *desc;
	int ret;

	if (size > sizeof(*desc))
		return -EINVAL;
	desc = kmalloc(sizeof(*desc), GFP_NOIO);
	if (!desc)
		return -ENOMEM;

	ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, size);
	if (ret >= 0)
		memcpy(&dev->descriptor, desc, size);
	kfree(desc);
	return ret;
}
int usb_get_descriptor(struct usb_device *dev, unsigned char type,
		       unsigned char index, void *buf, int size)
{
	int i;
	int result;

	memset(buf, 0, size);	/* Make sure we parse really received data */

	for (i = 0; i < 3; ++i) {
		/* retry on length 0 or error; some devices are flakey */
		result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
				USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
				(type << 8) + index, 0, buf, size,
				USB_CTRL_GET_TIMEOUT);
		if (result <= 0 && result != -ETIMEDOUT)
			continue;
		if (result > 1 && ((u8 *)buf)[1] != type) {
			result = -ENODATA;
			continue;
		}
		break;
	}
	return result;
}

2、usb_configure_device

static int usb_configure_device(struct usb_device *udev)
{
	usb_get_configuration(udev);
}
int usb_get_configuration(struct usb_device *dev)
{
	struct device *ddev = &dev->dev;
	int ncfg = dev->descriptor.bNumConfigurations;
	int result = 0;
	unsigned int cfgno, length;
	unsigned char *buffer;
	unsigned char *bigbuffer;
	struct usb_config_descriptor *desc;

	cfgno = 0;

	if (ncfg > USB_MAXCONFIG) {
		dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;
	}

	length = ncfg * sizeof(struct usb_host_config);
	dev->config = kzalloc(length, GFP_KERNEL);

	length = ncfg * sizeof(char *);
	dev->rawdescriptors = kzalloc(length, GFP_KERNEL);

	buffer = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);

	desc = (struct usb_config_descriptor *)buffer;

	result = 0;
	for (; cfgno < ncfg; cfgno++) {
		/* We grab just the first descriptor so we know how long the whole configuration is */
		result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, USB_DT_CONFIG_SIZE);
		/* 长度为当前配置所有描述符的长度 */
		length = max((int) le16_to_cpu(desc->wTotalLength), USB_DT_CONFIG_SIZE);

		/* Now that we know the length, get the whole thing */
		bigbuffer = kmalloc(length, GFP_KERNEL);
<span style="white-space:pre">		</span>/* 获取描述符 */
		result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bigbuffer, length);

		dev->rawdescriptors[cfgno] = bigbuffer;
		/* 解析配置描述符 */
		result = usb_parse_configuration(&dev->dev, cfgno, &dev->config[cfgno], bigbuffer, length);
	}
	result = 0;

	return result;
}

至此,所有的描述符获取完毕。

时间: 2024-08-26 17:48:56

USB驱动——设备、配置、接口、设置、端点之前的关系以及它们的获取过程分析的相关文章

USB驱动——键盘驱动(控制传输)

本文以 usbkbd.c 为例,分析 usb 键盘驱动程序. static int __init usb_kbd_init(void) { int result = usb_register(&usb_kbd_driver); if (result == 0) printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" DRIVER_DESC "\n"); return result;

USB鼠标设备简单驱动设计

USB(Universal Serial Bus ),通用串行总线,是一种外部总线标准,用于规范电脑与外部设备的连接和通讯.是在1994年底由英特尔.康柏.IBM.Microsoft等多家公司联合提出的,自1996年推出后,已成功替代串口和并口,成为当今个人电脑和大量智能设备的必配接口之一. linux内核支持两种类型的USB驱动程序,一种是PC机的USB驱动程序控制插入其中的USB设备,另一种是USB设备和主机通信. 第一种软件架构                               

usb由于其配置信息(注册表中的)不完整或已损坏,Windows 无法启动这个硬件设备(代码 19)

今天解决了一晚上,USB驱动的问题: 在设备管理器的usb设备的属性中,显示提示“由于其配置信息(注册表中的)不完整或已损坏,Windows 无法启动这个硬件设备”.注册表坏了.经过查询,解决方法如下: 方法:打开注册表编辑器(开始-->运行-->regedit),依次展开HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Class/在这下面有很多用“{}”括起来的项,一个一个的点开,看右面空口有没有“通用串行总线控制器”这些文字,在右面窗

android下调试3G之USB串口驱动集成配置

一.修改Android内核的编译配置 vi ~/myandroid/kernel_imx/.config文件中,确保下面的的配置项已经被选定. (假如源码在~/myandroid目录下) 1. USB电源管理特性的相关配置项: CONFIG_USB_SUSPEND=y 2. USB串口驱动相关的配置项: CONFIG_USB_SERIAL=y CONFIG_USB_SERIAL_OPTION=y CONFIG_USB_SERIAL_WWAN=y 3. PPP拨号的相关配置项: CONFIG_PP

Linux USB 驱动开发(一)—— USB设备基础概念【转】

本文转载自:http://blog.csdn.net/zqixiao_09/article/details/50984074 在终端用户看来,USB设备为主机提供了多种多样的附加功能,如文件传输,声音播放等,但对USB主机来说,它与所有USB设备的接口都是一致的.一个USB设备由3个功能模块组成:USB总线接口.USB逻辑设备和功能单元: a -- 这里的USB总线接口指的是USB设备中的串行接口引擎(SIE): b -- USB逻辑设备被USB系统软件看作是一个端点的集合: c -- 功能单元

Linux USB驱动程序设计

1. USB发展史 USB(Universal Serial Bus ),通用串行总线,是一种外部总线标准,用于规范电脑与外部设备的连接和通讯. USB是在1994年底由英特尔.康柏.IBM.Microsoft等多家公司联合提出的,自1996年推出后,已成功替代串口和并口,成为当今个人电脑和大量智能设备的必配接口之一. ?USB 1.0出现在1996年的,速度只有1.5Mb/s1998年升级为USB 1.1,速度也提升到12Mb/s,称之为"full speed" ?USB 2.0规范

Linux USB 驱动开发(三)—— 编写USB 驱动程序

前面学习了USB驱动的一些基础概念与重要的数据结构,那么究竟如何编写一个USB 驱动程序呢?编写与一个USB设备驱动程序的方法和其他总线驱动方式类似,驱动程序把驱动程序对象注册到USB子系统中,稍后再使用制造商和设备标识来判断是否安装了硬件.当然,这些制造商和设备标识需要我们编写进USB 驱动程序中. USB 驱动程序依然遵循设备模型 -- 总线.设备.驱动.和I2C 总线设备驱动编写一样,所有的USB驱动程序都必须创建的主要结构体是 struct usb_driver,它们向USB 核心代码描

USB驱动开发大全【转】

本文转载自:http://www.360doc.com/content/12/0504/19/8363527_208666082.shtml 编写USB驱动程序步骤:1所有usb驱动都必须创建主要结构体struct usb_driverstruct usb_driver->struct module *owner   (有他可正确对该驱动程序引用计数,应为THIS_MODULE)->const char *name   (驱动名字,运行时可在查看 /sys/bus/usb/drivers/)-

Linux USB驱动框架分析 【转】

转自:http://blog.chinaunix.net/uid-11848011-id-96188.html 初次接触与OS相关的设备驱动编写,感觉还挺有意思的,为了不至于忘掉看过的东西,笔记跟总结当然不可缺,更何况我决定为嵌入式卖命了.好,言归正传,我说一说这段时间的收获,跟大家分享一下Linux的驱动开发.但这次只先针对Linux的USB子系统作分析,因为周五研讨老板催货.当然,还会顺带提一下其他的驱动程序写法. 事实上,Linux的设备驱动都遵循一个惯例——表征驱动程序(用driver更