USB驱动——描述符、URB、管道

25 篇文章 41 订阅
11 篇文章 11 订阅

    大家常说,一个设备通常有多个配置,配置通常有多个接口,接口通常有多个端点。接口代表逻辑上的设备,比如声卡分为 录音和播放。访问设备时,访问的是某个接口(逻辑设备)。除了端点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;
}
    至此,所有的描述符获取完毕。

三、URB

  URB(USB Request Block,USB请求块)是USB数据传机制使用的核心数据结构。URB供USB协议栈使用。URB在include/linux/usb.h 文件中定义。

struct urb {
	struct kref kref;		/* reference count of the URB */
	...
	struct usb_device *dev;		/* (in) pointer to associated device */
	struct usb_host_endpoint *ep;	/* (internal) pointer to endpoint */
	unsigned int pipe;		/* (in) pipe information */
	unsigned int stream_id;		/* (in) stream ID */
	int status;			/* (return) non-ISO status */
	unsigned int transfer_flags;	/* (in) URB_SHORT_NOT_OK | ...*/
	void *transfer_buffer;		/* (in) associated data buffer */
	dma_addr_t transfer_dma;	/* (in) dma addr for transfer_buffer */
	...	
	u32 transfer_buffer_length;	/* (in) data buffer length */
	u32 actual_length;		/* (return) actual transfer length */
	unsigned char *setup_packet;	/* (in) setup packet (control only) */
	dma_addr_t setup_dma;		/* (in) dma addr for setup_packet */
	int start_frame;		/* (modify) start frame (ISO) */
	int number_of_packets;		/* (in) number of ISO packets */
	int interval;			/* (modify) transfer interval
					 * (INT/ISO) */
	int error_count;		/* (return) number of ISO errors */
	void *context;			/* (in) context for completion */
	usb_complete_t complete;	/* (in) completion routine */
	struct usb_iso_packet_descriptor iso_frame_desc[0];
					/* (in) ISO ONLY */
};
  URB 使用分三步,分配内存,初始化,提交。URB的内存分配是调用 usb_alloc_urb()方法来分配,该函数分配内存并将其至零,之后初始化URB相关的kobject和用于保护的URB自旋锁。USB核心提供下列辅助函数来完成URB的初始化工作。
usb_fill_[int|control|bulk]_urb(
	struct urb * urb, 		// URB pointer
	struct usb_device * dev, 	// USB device structure
	unsigned int pipe, 		// pipe encoding
	void * transfer_buffer, 	// Buffer for I/O
	int buffer_length, 		// I/O Buffer length
	usb_complete_t complete_fn, 	// Callback routine
	void * context, 		// For usb by completion_fn
	int interval			// For int URBS only
)
  complete_fn 是回调函数,回调函数在URB提交过程后被调用,负责检查提交状态、释放传输输出缓冲区等。为了提交URB以便进行数据传输,需要调用 usb_submit_urb()函数。该函数异步提交URB。

  USB 核心也提供了公布提交 URB 的接口函数:

usb_[interrupt|control|bulk]_msg(
	struct usb_device * usb_dev, 
	unsigned int pipe, 
	void * data, 
	int len, 
	int * actual_length, 
	int timeout
)
  创建一个URB 之后提交,如果没有成功则会一直等待。该函数不需要传递回调函数地址,一个通用的完成回调函数将会实现此功能。也不需要另外创建和初始化,因为这个函数在没有增加任何开销的情况下连这些都已经做了。

  URB 的任务完成以后,usb_free_urb()函数释放该实例。usb_unlink_urb()取消一个等待处理的URB.

四、管道

  管道包含以下几部分:

    断点地址;

    数据传输方向;

    数据传输模式:控制模式、中断模式、批量模式、实时模式;

  管道是URB的重要成员,为USB数据传输提供地址信息。USB核心提供现成的宏来创建管道。

   usb_[rcv|snd][ctrl|int|bulk|isoc]pipe(struct usb_device *usb_dev,_u8 endpointAddress)

五、传输模式

  控制传输模式:用来传送外设和主机之间的控制、状态、配置等信息

  批量传输模式:传输大量延时要求不高的数据

  中断传输模式:传输数据量小,但是对传输延时要求较高的的情况,比如键盘

  实时传输模式:传输实时数据,传输速率要预先可知

  














  • 5
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
linux系统下USB键盘驱动源码+使用文档说明 如何编写Linux下的USB键盘驱动 1. 指定USB键盘驱动所需的头文件: #include /*内核头文件,含有内核一些常用函数的原型定义*/ #include /*定义内存分配的一些函数*/ #include /*模块编译必须的头文件*/ #include /*输入设备相关函数的头文件*/ #include /*linux初始化模块函数定义*/ #include /*USB设备相关函数定义*/ 2. 定义键盘码表数组: /*使用第一套键盘扫描码表:A-1E;B-30;C-2E…*/ static unsigned char usb_kbd_keycode[256] = { 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26, 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106, 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190, 191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113, 115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0, 122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113, 150,158,159,128,136,177,178,176,142,152,173,140 }; 3. 编写设备ID表: static struct usb_device_id usb_kbd_id_table [] = { { USB_INTERFACE_INFO(3, 1, 1) },/*3,1,1分别表示接口类,接口子类,接口协议;3,1,1为键盘接口类;鼠标为3,1,2*/ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);/*指定设备ID表*/ 4. 定义USB键盘结构体: struct usb_kbd { struct input_dev *dev; /*定义一个输入设备*/ struct usb_device *usbdev;/*定义一个usb设备*/ unsigned char old[8]; /*按键离开时所用之数据缓冲区*/ struct urb *irq/*usb键盘之中断请求块*/, *led/*usb键盘之指示灯请求块*/; unsigned char newleds;/*目标指定灯状态*/ char name[128];/*存放厂商名字及产品名字*/ char phys[64]
根据提供的引用内容,我可以为您介绍USB URB请求超时的情况。 在USB通信中,URBUSB Request Block)是用于描述和管理USB传输的数据结构。URB包含了传输的相关信息,例如传输类型、数据缓冲区、传输方向等。当主机与USB设备进行通信时,会使用URB来发送和接收数据。 当一个URB请求被提交给USB驱动程序后,驱动程序会将该请求发送给USB控制器进行处理。在正常情况下,USB控制器会及时地完成请求并返回结果。然而,有时候由于各种原因,URB请求可能会超时。 URB请求超时可能是由以下原因引起的: 1. USB设备未正确响应:USB设备可能出现故障或异常,导致无法及时响应URB请求。 2. USB总线负载过重:当USB总线上的设备数量过多或数据传输量过大时,可能会导致URB请求超时。 3. USB驱动程序错误:USB驱动程序可能存在bug或错误,导致URB请求无法正常完成。 当URB请求超时时,可以采取以下措施: 1. 重新提交URB请求:可以尝试重新提交URB请求,以便再次发送请求并等待响应。 2. 检查USB设备状态:检查USB设备是否正常工作,是否存在故障或异常情况。 3. 检查USB总线负载:检查USB总线上的设备数量和数据传输量,确保不超过总线的承载能力。 4. 更新USB驱动程序:如果存在USB驱动程序的bug或错误,可以尝试更新驱动程序版本或使用其他可靠的驱动程序。 请注意,具体的处理方法可能因系统和设备的不同而有所差异。建议在遇到URB请求超时问题时,参考相关文档或咨询厂商或开发者以获取更准确的解决方案。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值