Linux libusb USB开发(一)—— USB设备基础概念

在终端用户看来,USB设备为主机提供了多种多样的附加功能,如文件传输,声音播放等,但对USB主机来说,它与所有USB设备的接口都是一致的。一个USB设备由3个功能模块组成:USB总线接口USB逻辑设备功能单元:

a -- 这里的USB总线接口指的是USB设备中的串行接口引擎(SIE);

b -- USB逻辑设备被USB系统软件看作是一个端点的集合

c -- 功能单元被客户软件看作是一个接口的集合。SIE、端点和接口都是USB设备的组成单元;

 为了更好地描述USB设备的特征,USB提出了设备架构的概念。从这个角度来看,可以认为USB设备是由一些配置、接口和端点组成,即一个USB设备可以含有一个或多个配置,在每个配置中可含有一个或多个接口,在每个接口中可含有若干个端点。其中,配置和接口是对USB设备功能的抽象,实际的数据传输由端点来完成。在使用USB设备前,必须指明其采用的配置和接口。这个步骤一般是在设备接入主机时设备进行枚举时完成的。

这些单元之间的关系如下:

设备通常有一个或多个配置;

配置通常有一个或多个接口;

接口通常有一个或多个设置;

接口有零或多个端点。

 这样的概念太抽象了,可以这样看:有一个设备,如支持视频和音频的一个播放器。那么,对于上面提到的4个描述符,对它们设置的时候,它们分别对于哪一个描述符呢?  

从我现在的理解来看,这样一个设备对应一个设备描述符,支持视频的功能对应一个接口描述符,支持音频功能的对应一个接口描述符。为了支持视频,在下层有多个端口同时工作为提供视频数据传输的支持,所以有多个端点描述符。

1、设备描述符

设备代表一个USB设备,它由一个或多个配置组成。设备描述符用于说明设备的总体信息,并指明其所含的配置的个数。一个USB设备只能有一个设备描述符

struct usb_device_descriptor
{
	_ _u8 bLength; //描述符长度
	_ _u8 bDescriptorType; //描述符类型编号
 
	_ _le16 bcdUSB; //USB版本号
	_ _u8 bDeviceClass; //USB分配的设备类code
	_ _u8 bDeviceSubClass;// USB分配的子类code
	_ _u8 bDeviceProtocol; //USB分配的协议code
	_ _u8 bMaxPacketSize0; //endpoint0最大包大小
	_ _le16 idVendor; //厂商编号
	_ _le16 idProduct; //产品编号
	_ _le16 bcdDevice; //设备出厂编号
	_ _u8 iManufacturer; //描述厂商字符串的索引
	_ _u8 iProduct; //描述产品字符串的索引
	_ _u8 iSerialNumber; //描述设备序列号字符串的索引
	_ _u8 bNumConfigurations; //可能的配置数量
} _ _attribute_ _ ((packed));

2、配置描述符

一个USB设备可以包含一个或多个配置,如USB设备的低功耗模式和高功耗模式可分别对应一个配置。在使用USB设备前,必须为其选择一个合适的配置。配置描述符用于说明USB设备中各个配置的特性,如配置所含接口的个数等。USB设备的每一个配置都必须有一个配置描述符。

struct usb_config_descriptor
{
	_ _u8 bLength; //描述符长度
	_ _u8 bDescriptorType; //描述符类型编号
	
	_ _le16 wTotalLength; //配置所返回的所有数据的大小
	_ _u8 bNumInterfaces; // 配置所支持的接口数
	_ _u8 bConfigurationValue; //Set_Configuration命令需要的参数值
	_ _u8 iConfiguration; //描述该配置的字符串的索引值
	_ _u8 bmAttributes; //供电模式的选择
	_ _u8 bMaxPower; //设备从总线提取的最大电流
} _ _attribute_ _ ((packed));

3、接口描述符

 一个配置可以包含一个或多个接口,例如对一个光驱来说,当用于文件传输时,使用其大容量存储接口;而当用于播放CD时,使用其音频接口。接口是端点的集合,可以包含一个或多个可替换设置,用户能够在USB处于配置状态时改变当前接口所含的个数和特性。接口描述符用于说明设备中各个接口的特性,如接口所属的设备类及其子类等。USB设备的每个接口都必须有一个接口描述符

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));

4、端点描述符

端点是USB设备中的实际物理单元,USB数据传输就是在主机和USB设备各个端点之间进行的。端点一般由USB接口芯片提供,例如Freescale公司的MC68HC908JB8和MC9S12UF32。USB设备中的每一个端点都有唯一的端点号,每个端点所支持的数据传输方向一般而言也是确定的:或是输入(IN),或是输出(OUT)。也有些芯片提供的端点的数据方向是可以配置的,例如MC68HC908JB8包含有两个用于数据收发的端点:端点1和端点2。其中端点1只能用于数据发送,即支持输入(IN)操作;端点2既能用于数据发送,也可用于数据接收,即支持输入(IN)和输出(OUT)操作。而MC9S12UF32具有6个端点。

 利用设备地址、端点号和传输方向就可以指定一个端点,并与它进行通信。端点的传输特性还决定了其与主机通信是所采用的传输类型,例如控制端点只能使用控制传输。根据端点的不同用途,可将端点分为两类:0号端点和非0号端点。

0号端点比较特殊,它有数据输入IN和数据输出OUT两个物理单元,且只能支持控制传输。所有的USB设备都必须含有一个0号端点,用作默认控制管道。USB系统软件就是使用该管道与USB逻辑设备进行配置通信的。0号端点在USB设备上的以后就可以使用,而非0号端点必须要在配置以后才可以使用。

 根据具体应用的需要,USB设备还可以含有多个除0号端点以外的其他端点。对于低速设备,其附加的端点数最多为2个;对于全速/高速设备,其附加的端点数最多为15个。

struct usb_endpoint_descriptor
{
	_ _u8 bLength; //描述符长度
	_ _u8 bDescriptorType; //描述符类型
	_ _u8 bEndpointAddress; //端点地址:0~3位是端点号,第7位是方向(0-OUT,1-IN)
	_ _u8 bmAttributes; //端点属性:bit[0:1] 的值为00表示控制,为01表示同步,为02表示批量,为03表示中断
	_ _le16 wMaxPacketSize;  本端点接收或发送的最大信息包的大小
	_ _u8 bInterval;//轮询数据传送端点的时间间隔
	                       //对于批量传送的端点以及控制传送的端点,此域忽略
	                    //对于同步传送的端点,此域必须为1
	_ _u8 bRefresh;
	_ _u8 bSynchAddress;
} _ _attribute_ _ ((packed));

5、字符串描述符

在USB设备中通常还含有字符串描述符,以说明一些专用信息,如制造商的名称、设备的序列号等。它的内容以UNICODE的形式给出,且可以被客户软件所读取。对USB设备来说,字符串描述符是可选的。

struct usb_string_descriptor
{
	_ _u8 bLength; //描述符长度
	_ _u8 bDescriptorType; //描述符类型
	
	_ _le16 wData[1];
} _ _attribute_ _ ((packed));

6、管道

在USB系统结构中,可以认为数据传输时在USB主机软件与USB设备的各个端点之间直接进行的,它们之间的连接称为管道。管道是在USB设备的配置过程中建立的。管道是对USB主机与USB设备间通信流的抽象,表示USB主机的数据缓冲区与USB设备的端点之间存在着逻辑数据传输,而实际的数据传输是由USB总线接口层来完成的。

管道与USB设备中的端点一一对应。一个USB设备含有多少个端点,其与USB主机进行通信时就可以使用多少条管道,且端点的类型决定了管道中数据的传输类型,例如中断端点对应中断管道,且该管道只能进行中断传输。不论存在着多少条管道,在各个管道中进行的数据传输都是相互独立的。

7、USB端点分类

USB 通讯的最基本形式是通过端点。一个USB端点只能向一个方向传输数据(从主机到设备(称为输出端点)或者从设备到主机(称为输入端点))。端点可被看作一个单向的管道。

USB 端点有 4 种不同类型, 分别具有不同的数据传送方式:

1) 控制CONTROL 

控制端点被用来控制对USB设备的不同部分访问. 通常用作配置设备、获取设备信息、发送命令到设备或获取设备状态报告。这些端点通常较小。每个 USB 设备都有一个控制端点称为"端点 0", 被 USB 核心用来在插入时配置设备。USB协议保证总有足够的带宽留给控制端点传送数据到设备.

2) 中断INTERRUPT 

每当 USB 主机向设备请求数据时,中断端点以固定的速率传送小量的数据。此为USB 键盘和鼠标的主要的数据传送方法。它还用以传送数据到USB设备来控制设备。通常不用来传送大量数据。USB协议保证总有足够的带宽留给中断端点传送数据到设备.

3) 批量BULK

批量端点用以传送大量数据。这些端点通常比中断端点大得多. 它们普遍用于不能有任何数据丢失的情况。USB 协议不保证传输在特定时间范围内完成。如果总线上没有足够的空间来发送整个BULK包,它被分为多个包进行传输。这些端点普遍用于打印机、USB Mass Storage和USB网络设备上。

4) 等时ISOCHRONOUS 

等时端点也批量传送大量数据, 但是这个数据不被保证能送达。这些端点用在可以处理数据丢失的设备中,并且更多依赖于保持持续的数据流。如音频和视频设备等等。

控制和批量端点用于异步数据传送,而中断和等时端点是周期性的。这意味着这些端点被设置来在固定的时间连续传送数据,USB 核心为它们保留了相应的带宽。

struct usb_host_endpoint{
    struct usb_endpoint_descriptor desc;//端点描述符
    struct list_head urb_list;//此端点的URB对列,由USB核心维护
    void *hcpriv;
    struct ep_device *ep_dev; /* For sysfs info */
    unsigned char*extra;/* Extra descriptors */
    int extralen;
    int enabled;
};

当调用USB设备驱动调用usb_submit_urb提交urb请求时,将调用int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb)把此urb增加到urb_list的尾巴上。(hcd: Host Controller Driver,对应数据结构struct usb_hcd )
 

8、USB设备参数查看方法

1)调用libusb库中的函数进行查询,后续博客会提及

2)Linux下查看方法

Linux系统下可查看USB设备信息的命令有:

lsusb

cat usb/devices

...

3)Windows下查看方法

Windows系统下可查看USB设备信息的方法有:

点击设备管理器,查看设备属性

USB查看器usbtreeviewV3.1.0软件 可查看USB设备详细信息

USB抓包工具--Bus Hound 可查看USB的通讯信息

...

 

参考链接

https://blog.csdn.net/sjin_1314/article/details/25366459

https://blog.csdn.net/zqixiao_09/article/details/50984074

https://www.cnblogs.com/tianchiyuyin/p/5139948.html

https://blog.csdn.net/qq_25544855/article/details/79313496

https://www.cnblogs.com/rusking/p/6107989.html

https://blog.csdn.net/encourage2011/article/details/53525297

https://blog.csdn.net/ericwuhk/article/details/79228117

https://blog.csdn.net/wy_stutdy/article/details/73326330

 

 

驱动开发向来是内核开发中工作量最多的一块,随着USB设备的普及,大量的USB设备的驱动开发也成为驱动开发者手头上做的最多的事情。本文主要介绍Linux平台下基于libusb的驱动开发,希望能够给从事Linux驱动开发的朋友带来些帮助,更希望能够给其他平台上的无驱设计带来些帮助。文章是我在工作中使用libusb的一些总结,难免有错误,如有不当的地方,还请指正。 [1]   Linux 平台上的usb驱动开发,主要有内核驱动的开发和基于libusb的无驱设计。   对于内核驱动的大部分设备,诸如带usb接口的hid设备linux本身已经自带了相关的驱动,我们只要操作设备文件便可以完成对设备大部分的操作,而另外一些设备,诸如自己设计的硬件产品,这些驱动就需要我们驱动工程师开发出相关的驱动了。内核驱动有它的优点,然而内核驱动在某些情况下会遇到如下的一些问题:   1 当使用我们产品的客户有2.4内核的平台,同时也有2.6内核的平台,我们要设计的驱动是要兼容两个平台的,就连makefile 我们都要写两个。   2 当我们要把linux移植到嵌入平台上,你会发现原先linux自 带的驱动移过去还挺大的,我的内核当然是越小越好拉,这样有必要么。这还不是最郁闷的地方,如果嵌入平台是客户的,客户要购买你的产品,你突然发现客户设 备里的系统和你的环境不一样,它没有你要的驱动了,你的程序运行不了,你会先想:“没关系,我写个内核驱动加载一下不就行了“。却发现客户连insmod加载模块的工具都没移植,那时你就看看老天,说声我怎么那么倒霉啊,客户可不想你动他花了n时间移植的内核哦   3 花了些功夫写了个新产品的驱动,挺有成就感啊,代码质量也是相当的有水准啊。正当你沉醉在你的代码中时,客服不断的邮件来了,“客户需要2.6.5内核的驱动,config文件我已经发你了” “客户需要双核的 2.6.18-smp 的驱动” “客户的平台是自己定制的是2.6.12-xxx “ 你恨不得把驱动的源代码给客户,这样省得编译了。你的一部分工作时间编译内核,定制驱动   有问题产生必然会有想办法解决问题的人, libusb的出现给我们带来了某些方便,即节约了我们的时间,也降低了公司的成本。 所以在一些情况下,就可以考虑使用libusb的无驱设计了。   下面我们就来详细讨论一下libusb, 并以写一个hid设备的驱动来讲解如何运用libusb,至于文章中涉及的usb协议的知识,限于篇幅,就不详细讲解了,相关的可自行查看usb相关协议。   一 libusb 介绍   libusb 设计了一系列的外部API 为应用程序所调用,通过这些API应用程序可以操作硬件,从libusb的源代码可以看出,这些API 调用了内核的底层接口,和kernel driver中所用到的函数所实现的功能差不多,只是libusb更加接近USB 规范。使得libusb的使用也比开发内核驱动相对容易的多。   Libusb 的编译安装请查看Readme,这里不做详解   二 libusb 的外部接口   2.1 初始化设备接口   这些接口也可以称为核心函数,它们主要用来初始化并寻找相关设备。   usb_init   函数定义: void usb_init(void);   从函数名称可以看出这个函数是用来初始化相关数据的,这个函数大家只要记住必须调用就行了,而且是一开始就要调用的.   usb_find_busses   函数定义: int usb_find_busses(void);   寻找系统上的usb总线,任何usb设备都通过usb总线和计算机总线通信。进而和其他设备通信。此函数返回总线数。   usb_find_devices   函数定义: int usb_find_devices(void);   寻找总线上的usb设备,这个函数必要在调用usb_find_busses()后使用。以上的三个函数都是一开始就要用到的,此函数返回设备数量。   usb_get_busses   函数定义: struct usb_bus *usb_get_busses(void);   这个函数返回总线的列表,在高一些的版本中已经用不到了,这在下面的实例中会有讲解   2.2 操作设备接口   usb_open   函数定义: usb_dev_handle *usb_open(struct *usb_device dev);   打开要使用的设备,在对硬件进行操作前必须要调用usb_open 来打开设备,这里大家看到有两个结构体 usb_dev_handle 和 usb_device 是我们在开发中经常碰到的,有必要把它们的结构看一看。在libusb 中的usb.h和usbi.h中有定义。   这里我们不妨理解为返回的 usb_dev_handle 指针是指向设备的句柄,而行参里输入就是需要打开的设备。   usb_close   函数定义: int usb_close(usb_dev_handle *dev);   与usb_open相对应,关闭设备,是必须调用的, 返回0成功,<0 失败。   usb_set_configuration   函数定义: int usb_set_configuration(usb_dev_handle *dev, int configuration);   设置当前设备使用的configuration,参数configuration 是你要使用的configurtation descriptoes中的bConfigurationValue, 返回0成功,<0失败( 一个设备可能包含多个configuration,比如同时支持高速和低速的设备就有对应的两个configuration,详细可查看usb标准)   usb_set_altinterface   函数定义: int usb_set_altinterface(usb_dev_handle *dev, int alternate);   和名字的意思一样,此函数设置当前设备配置的interface descriptor,参数alternate是指interface descriptor中的bAlternateSetting。返回0成功,<0失败   usb_resetep   函数定义: int usb_resetep(usb_dev_handle *dev, unsigned int ep);   复位指定的endpoint,参数ep 是指bEndpointAddress,。这个函数不经常用,被下面介绍的usb_clear_halt函数所替代。   usb_clear_halt   函数定义: int usb_clear_halt (usb_dev_handle *dev, unsigned int ep);   复位指定的endpoint,参数ep 是指bEndpointAddress。这个函数用来替代usb_resetep   usb_reset   函数定义: int usb_reset(usb_dev_handle *dev);   这个函数现在基本不怎么用,不过这里我也讲一下,和名字所起的意思一样,这个函数reset设备,因为重启设备后还是要重新打开设备,所以用usb_close就已经可以满足要求了。   usb_claim_interface   函数定义: int usb_claim_interface(usb_dev_handle *dev, int interface);   注册与操作系统通信的接口,这个函数必须被调用,因为只有注册接口,才能做相应的操作。   Interface 指 bInterfaceNumber. (下面介绍的usb_release_interface 与之相对应,也是必须调用的函数)   usb_release_interface   函数定义: int usb_release_interface(usb_dev_handle *dev, int interface);   注销被usb_claim_interface函数调用后的接口,释放资源,和usb_claim_interface对应使用。   2.3 控制传输接口   usb_control_msg   函数定义:int usb_control_msg(usb_dev_handle *dev, int requesttype, int request, int value, int index, char *bytes, int size, int timeout);   从默认的管道发送和接受控制数据   usb_get_string   函数定义: int usb_get_string(usb_dev_handle *dev, int index, int langid, char *buf, size_t buflen);   usb_get_string_simple   函数定义: int usb_get_string_simple(usb_dev_handle *dev, int index, char *buf, size_t buflen);   usb_get_descriptor   函数定义: int usb_get_descriptor(usb_dev_handle *dev, unsigned char type, unsigned char index, void *buf, int size);   usb_get_descriptor_by_endpoint   函数定义: int usb_get_descriptor_by_endpoint(usb_dev_handle *dev, int ep, unsigned char type, unsigned char index, void *buf, int size);   2.4 批传输接口   usb_bulk_write   函数定义: int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout);   usb_interrupt_read   函数定义: int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout);   2.5 中断传输接口   usb_bulk_write   函数定义: int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout);   usb_interrupt_read   函数定义: int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值