linux中的usb子系统

    usb协议由于考虑通用性,既可以支持大容量存储设备,音频设备,HID设备,CDC协议,DFU等等等。。。在设计整套协议的时候非常复杂,比如相关包类型,事务,端点等这些概念内容非常多,如果读者向深入分析整套协议栈,可以阅读<<圈圈教你玩usb>>,内容理论与实例相结合,基于51单片机和一块usb桥接芯片实现了usb众多设备。本篇不详细分析总线驱动程序,而着重分析设备驱动程序。

 

 

Linux系统中实现了如下以下设备类

  • 音频通信类 ,比如USB声卡,USB麦克风
  • 通信设备类,比如USB转虚拟串口
  • HID类,比如鼠标,键盘,游戏手柄
  • 打印设备类,比如打印机
  • 海量存储类,比如U盘

在Linux内核中,使用usb_driver结构体描述USB设备驱动,usb_driver结构体定义如下所示:

struct usb_driver {
	const char *name;

	int (*probe) (struct usb_interface *intf,
		      const struct usb_device_id *id);

	void (*disconnect) (struct usb_interface *intf);

	int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code,
			void *buf);

	int (*suspend) (struct usb_interface *intf, pm_message_t message);
	int (*resume) (struct usb_interface *intf);
	int (*reset_resume)(struct usb_interface *intf);

	int (*pre_reset)(struct usb_interface *intf);
	int (*post_reset)(struct usb_interface *intf);

	const struct usb_device_id *id_table;

	struct usb_dynids dynids;
	struct usbdrv_wrap drvwrap;
	unsigned int no_dynamic_id:1;
	unsigned int supports_autosuspend:1;
	unsigned int soft_unbind:1;
}

 usb总线驱动程序,在接入USB设备,会构造了一个新的usb_dev结构注册到usb_bus_type,这部分内容内核已经帮我们实现好了,设备驱动程序需要做的就是构造好usb_driver结构体,注册到usb_bus_type,usb_driver结构体中有一项id_table,表示能够支持哪些设备,当usb能匹配到id_table的某个设备时,就会调用usb_driver结构体的probe探测函数,当拔掉usb设备时,就会调用用usb_driver结构体的emove注销函数。

usb_device_id结构体定义如下所示,usb_device_id结构体包含有USB设备的PID,VID,版本,设备类,设备子类,接口类等以及匹配标志成员match_flags,以表明匹配的种类

struct usb_device_id {
	/* which fields to match against? */
	__u16		match_flags;

	/* Used for product specific matches; range is inclusive */
	__u16		idVendor;
	__u16		idProduct;
	__u16		bcdDevice_lo;
	__u16		bcdDevice_hi;

	/* Used for device class matches */
	__u8		bDeviceClass;
	__u8		bDeviceSubClass;
	__u8		bDeviceProtocol;

	/* Used for interface class matches */
	__u8		bInterfaceClass;
	__u8		bInterfaceSubClass;
	__u8		bInterfaceProtocol;

	/* not matched against */
	kernel_ulong_t	driver_info;
};

 

可以借助一些宏来生成usb_device_id结构体,比如USB_DEVICE(vendor,product),USB_DEIVICE_INFO(class,subclass,protocol),USB_INTERFACE_INFO(class,subclass,protocal)等

 

USB请求块是USB设备驱动中用来描述与USB设备通信所用的基本载体。非常类似于I2C设备驱动中的i2c_msg结构体,SPI设备驱动程序中的spi_message结构体。

struct urb {
	/* private: usb core and host controller only fields in the urb */
	struct kref kref;		/* reference count of the URB */
	void *hcpriv;			/* private data for host controller */
	atomic_t use_count;		/* concurrent submissions counter */
	atomic_t reject;		/* submissions will fail */
	int unlinked;			/* unlink error code */

	/* public: documented fields in the urb that can be used by drivers */
	struct list_head urb_list;	/* list head for use by the urb's
					 * current owner */
	struct list_head anchor_list;	/* the URB may be anchored */
	struct usb_anchor *anchor;
	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 */
	struct scatterlist *sg;		/* (in) scatter gather buffer list */
	int num_mapped_sgs;		/* (internal) mapped sg entries */
	int num_sgs;			/* (in) number of entries in the sg list */
	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结构体
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
//iso_packets=NULL表示不创建等时数据包
//开辟urb空间的mem标志,一般为GFP_KERNEL

 

  • 设置创建好的URB结构体,把这个URB分配给USB设备的特定端点
中断URB
static inline void usb_fill_int_urb(struct urb *urb,   //指向该URB
				    struct usb_device *dev, //指向该请求块要发送的usb设备,设备可以利用interface_to_usbdev函数根据probe中的 intf形参转换而来 
				    unsigned int pipe,      //特定端点,通过usb_rcvintpipe创建
				    void *transfer_buffer,  //指向数据缓冲区
				    int buffer_length,      //指向数据缓冲区长度
				    usb_complete_t complete_fn, //完成数据传输的回调函数
				    void *context,
				    int interval)            //调度间隔
批量URB,批量URB没有调度间隔
static inline void usb_fill_bulk_urb(struct urb *urb,
				     struct usb_device *dev,
				     unsigned int pipe,   //特定端点,通过usb_rcvbulkpipe创建
				     void *transfer_buffer,
				     int buffer_length,
				     usb_complete_t complete_fn,
				     void *context)    
控制URB
static inline void usb_fill_control_urb(struct urb *urb,
					struct usb_device *dev,
					unsigned int pipe,  //特定端点,通过usb_rcvctrlpipe创建
					unsigned char *setup_packet,  //指向将发送到端点的设置数据包
					void *transfer_buffer,
					int buffer_length,
					usb_complete_t complete_fn,
					void *context)  

 

等时URB没有以上三种传输的初始化函数,只能手动设置,然后提交给USB核心层

  • 提交URB给USB核心
int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
//struct urb *urb   指向该URB
//gfp_t mem_flags   开辟urb空间的mem标志,一般为GFP_KERNEL

 

简单的URB使用流程

在一些批量与控制传输的场合,有时设备驱动只是从usb上传输一些简单的控制消息,可以不需要使用urb,而使用一些更简单的函数来完成usb数据的传输

批量传输
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
		 void *data, int len, int *actual_length, int timeout)
 控制传输  
 int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
		    __u8 requesttype, __u16 value, __u16 index, void *data,
		    __u16 size, int timeout)
 

 

实例:USB鼠标(linux2.6 测试环境s3c2440)

Linux系统中,鼠标属于标准输入设备,其驱动包含两部分:usb_driver结构体的设置以及输入设备驱动input_dev的设置。

该驱动程序模块加载与卸载中,分别注册与注销usb_driver结构体

static struct usb_device_id usbmouse_id_table [] = {
	{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
		USB_INTERFACE_PROTOCOL_MOUSE) },
	{ }	/* Terminating entry */
};

/*  分配/设置usb_driver */
static struct usb_driver usbmouse_as_key_driver = {
	.name		= "usbmouse",
	.probe		= usbmouse_probe,
	.disconnect	= usbmouse_disconnect,
	.id_table	= usbmouse_id_table,
};


static int usbmouse_init(void)
{
	/* 2. 注册 */
	usb_register(&usbmouse_driver);
	return 0;
}

static void usbmouse_exit(void)
{
	usb_deregister(&usbmouse_driver);	
}

 

在usb_driver中的probe()函数中,对输入设备进行初始化并注册,设置创建好的URB结构体并提交给USB核心层

static int usbmouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
	struct usb_device *dev = interface_to_usbdev(intf); //获取usb设备
	struct usb_host_interface *interface;
	struct usb_endpoint_descriptor *endpoint;   
	int pipe;
	
	interface = intf->cur_altsetting;
	endpoint = &interface->endpoint[0].desc;

	/* a. 分配一个input_dev */
	uk_dev = input_allocate_device();
	
	/* b. 设置 */
	/* b.1 能产生哪类事件 */
	set_bit(EV_KEY, uk_dev->evbit);
	set_bit(EV_REP, uk_dev->evbit);
	
	/* b.2 能产生哪些事件 */
	set_bit(KEY_L, uk_dev->keybit);
	set_bit(KEY_S, uk_dev->keybit);
	set_bit(KEY_ENTER, uk_dev->keybit);
	
	/* c. 注册 */
	input_register_device(uk_dev);
	
	
	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); //创建中断输入端点

	
	len = endpoint->wMaxPacketSize;   //端点的包长

	
	usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys);//分配usb缓冲区

	
	/* 分配URB */
	uk_urb = usb_alloc_urb(0, GFP_KERNEL);
	//设置URB
	usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_irq, NULL, endpoint->bInterval);
	uk_urb->transfer_dma = usb_buf_phys;
	uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

	/* 向核心层提交URB */
	usb_submit_urb(uk_urb, GFP_KERNEL);
	
	return 0;
}

 

在usb_driver断开函数中,终止已提交的URB,并注销输入设备

static void usbmouse_disconnect(struct usb_interface *intf)
{
	struct usb_device *dev = interface_to_usbdev(intf);

	//printk("disconnect usbmouse!\n");
	usb_kill_urb(uk_urb);
	usb_free_urb(uk_urb);

	usb_buffer_free(dev, len, usb_buf, usb_buf_phys);
	input_unregister_device(uk_dev);
	input_free_device(uk_dev);
}

 

usb鼠标数据传输属于中断传输,在鼠标中断的URB完成函数,会通过input_report_key()报告按键时间

static void usbmouse_as_key_irq(struct urb *urb)
{
	static unsigned char pre_val;

	/* USB鼠标数据含义
	 * data[0]: bit0-左键, 1-按下, 0-松开
	 *          bit1-右键, 1-按下, 0-松开
	 *          bit2-中键, 1-按下, 0-松开 
	 *
     */
	if ((pre_val & (1<<0)) != (usb_buf[0] & (1<<0)))
	{
		/* 左键发生了变化 */
		input_event(uk_dev, EV_KEY, KEY_L, (usb_buf[0] & (1<<0)) ? 1 : 0);
		input_sync(uk_dev);
	}

	if ((pre_val & (1<<1)) != (usb_buf[0] & (1<<1)))
	{
		/* 右键发生了变化 */
		input_event(uk_dev, EV_KEY, KEY_S, (usb_buf[0] & (1<<1)) ? 1 : 0);
		input_sync(uk_dev);
	}

	if ((pre_val & (1<<2)) != (usb_buf[0] & (1<<2)))
	{
		/* 中键发生了变化 */
		input_event(uk_dev, EV_KEY, KEY_ENTER, (usb_buf[0] & (1<<2)) ? 1 : 0);
		input_sync(uk_dev);
	}
	
	pre_val = usb_buf[0];

	/* 重新提交urb */
	usb_submit_urb(uk_urb, GFP_KERNEL);
}

 

从USB鼠标驱动例子可以看出,usb_driver结构体本省只是起一个挂载总线的作用,具体设备类型的驱动模型根据具体设备类型额定,鼠标就是input子系统,USB声卡就是ALSA框架,USB摄像头就是V4L2框架,而这些具体设备的底层硬件操作时,都是调用的URB相关接口。USB核心层API的存在无需关系底层USB主机控制器的具体细节

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值