ZYNQ学习之路9.USB总线学习(二)
Linux中USB驱动程序依然遵循标准的设备驱动模型——总线、设备、驱动。和I2C总线设备驱动一样,所有的USB驱动程序都必须创建的主要结构体是struct usb_driver,它们向USB核心代码描述了USB驱动程序,但这只是外壳,只实现了设备与总线的挂接,具体的USB设备是什么,如何实现,还需要编写相应的文件操作接口。本文详细介绍USB的驱动框架。
一. USB设备基础知识
1.1 USB设备的配置与传输类型
USB设备在接入系统时,以设备ID为0和主机通信,然后主机为其分配新的ID。在主机端,D+、D-都是下拉接地,设备端的D-接上拉时,表明此设备为全速设备(12Mbps),D+接上拉时为高速设备(480Mbps)。PC中的USB下拉电阻为15K,而设备中D+和D-则是1.5K上拉电阻。
USB是主从结构,并且是树形结构,在一个USB总线上的所有设备只有一个主机。所有的传输都是由主机发起的,即USB设备没有主动通知USB主机的能力。
USB有四种传输类型:
控制传输:最重要的也是最复杂的传输,控制传输由三个阶段构成(初始配置阶段、可选数据阶段、状态信息步骤),每一个阶段能够看成一个的传输,也就是说控制传输其实是由三个传输构成的,用来于USB设备初次加接到主机之后,主机通过控制传输来交换信息,设备地址和读取设备的描述符,使得主机识别设备,并安装相应的驱动程式,这是每一个USB研发者都要关心的问题。
批量传输:由OUT事务和IN事务构成,用于大容量数据传输,没有固定的传输速率,也不占用带宽,当总线忙时,USB会优先进行其他类型的数据传输,而暂时停止批量转输。例:U盘。
中断传输:由OUT事务和IN事务构成,用于键盘、鼠标等HID设备的数据传输中;
同步传输:由OUT事务和IN事务构成,有两个特别地方,第一,在同步传输的IN和OUT事务中是没有返回包阶段的;第二,在数据包阶段任何的数据包都为DATA0;
USB数据的传输都是在称之为“端点”的对象之间进行的,端点0用于控制传输,可双向传输,除了端点0其他每个端点都是单向传输数据。
OTG协议:OTG设备采用Mini-AB插座,相对于传统的USB数据线,Mini-AB接口多了一个数据线ID,ID线是否接入将Mini-AB接口区分为Mini-A和Mini-B接口两种类型。在OTG设备之间的数据连接过程中,通过OTG数据线Mini-A和Mini-B接口来确定OTG设备的主从:接入Mini-A接口的设备默认为A设备(主机接口);接Mini-B接口的设备默认为B设备(从设备)。
1.2 USB驱动程序框架
USB驱动程序的框架可描述如图1-1所示:
一个USB设备驱动程序由一些配置、接口和端点组成。一个USB设备可以包含一个或多个配置,每个配置可包含一个或多个接口,在每个接口中可含有若干各端点。这些单元之间的关系如图1-2所示:
二. USB驱动中的描述符
1.1 设备描述符
设备代表一个USB设备,由一个或多个配置组成。设备描述符用于说明设备的总体信息,并指明配置的个数,一个设备只能有一个设备描述符。
struct usb_device_descriptor {__u8 bLength; //设备描述符字节长度__u8 bDescriptorType; //设备描述符类型__le16 bcdUSB; //USB版本号__u8 bDeviceClass; //接口描述符的类__u8 bDeviceSubClass; //接口描述符的子类__u8 bDeviceProtocol; //接口描述符的协议__u8 bMaxPacketSize0; //端点0可以支持的最大包的长度__le16 idVendor; //Vendor ID__le16 idProduct; //Product ID__le16 bcdDevice; //设备版本__u8 iManufacturer; //字符串描述符生产商索引__u8 iProduct; //字符串描述符产品索引__u8 iSerialNumber; //字符串描述符设备串行号索引__u8 bNumConfigurations; //配置编号
} __attribute__ ((packed));
1.2 配置描述符
每个设备都有默认的配置描述符,支持至少一个接。配置描述符用于说明USB设备中各个配置的特性,如配置所包含的接口个数等。描述符结构定义如下:
struct usb_config_descriptor {__u8 bLength; //配置描述符长度__u8 bDescriptorType; //配置描述符类型__le16 wTotalLength; //配置返回数据的总长度__u8 bNumInterfaces; //配置支持接口数目__u8 bConfigurationValue; //设置配置请求值__u8 iConfiguration; //描述配置的字符串描述符的索引__u8 bmAttributes; //配置特性,供电选择__u8 bMaxPower; //从USB总线上获取的最大电流
} __attribute__ ((packed));
1.3 接口描述符
设备应该至少支持一个接口。接口时端点的集合,可以包含一个或多个可替换设置,用户能够在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));
1.4 端点描述符
端点是USB设备实际传输数据的对象,利用设备地址、端点号和传输方向可以指定一个端点,并与之进行通信。0号端点包含输入IN和输出OUT连个物理单元,且只支持控制传输。0号端点在设备上电后就能使用,而非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[1:0]的值为00-控制,01-同步,02-批量,03-中断__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));
1.5 字符串描述符
在USB设备中通常包含字符串描述符,如制造商名称、设备序列号等。字符串描述符结构如下:
struct usb_string_descriptor {__u8 bLength; //字符串描述符长度__u8 bDescriptorType; //描述符类型__le16 wData[1]; /* UTF-16LE encoded */
} __attribute__ ((packed));
三. 搭建驱动框架
3.1 注册USB驱动程序
USB设备的注册与卸载程序如下:
static int __init usb_skel_init(void)
{ int result; /* register this driver with the USB subsystem */ result = usb_register(&skel_driver); if (result) err("usb_register failed. Error number %d", result); return result;
}
static void __exit usb_skel_exit(void)
{ /* deregister this driver with the USB subsystem */ usb_deregister(&skel_driver);
} module_init (usb_skel_init);
module_exit (usb_skel_exit);
MODULE_LICENSE("GPL");
对比I2C的设备驱动,它们非常相似。skel_driver是struct 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 disable_hub_initiated_lpm:1;unsigned int soft_unbind:1;
};
初始化usb_driver至少需要实现五个字段:模块所有者、模块名字、probe函数、disconnect函数以及id_table。
id_table是struct usb_device_id类型,包括一列该驱动程序可以支持的所有不同类型的USB设备。如果没有设置该变量,USB驱动程序中的探测(probe)函数将不会被调用。
首先看看usb_device_id结构体的定义:
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;/* Used for vendor-specific interface matches */__u8 bInterfaceNumber;/* not matched against */kernel_ulong_t driver_info__attribute__((aligned(sizeof(kernel_ulong_t))));
};
USB系统通过vendor ID和Product ID的组合或者class和subclass的组合来识别设备,可以使用USB_DEVICE宏来定义这个ID:
#define USB_DEVICE(vend, prod) \.match_flags = USB_DEVICE_ID_MATCH_DEVICE, \.idVendor = (vend), \.idProduct = (prod)
例如:
static struct usb_device_id skel_table[]={{USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID)},{}
};
MODULE_DEVICE_TABLE(usb, skel_table);
MODULE_DEVICE_TABLE宏第一个参数是设备类型,第二个是设备表,这个设备表的最后一个元素是空的,用于标识结束。
当USB设备连接到USB控制器接口时,usb_core就检测该设备的一些信息,例如生成厂商ID和产品的ID,或者是设备所属的class,subclass根protocol,以便确定应该调用哪个驱动处理该设备。
3.2 几个重要的数据结构
usb_skel:用于与端点进行通信,定义如下:
struct usb_skel {struct usb_device *udev; /* the usb device for this device */struct usb_interface *interface; /* the interface for this device */struct semaphore limit_sem; /* limiting the number of writes in progress */struct usb_anchor submitted; /* in case we need to retract our submissions */struct urb *bulk_in_urb; /* the urb to read data with */unsigned char *bulk_in_buffer; /* the buffer to receive data */size_t bulk_in_size; /* the size of the receive buffer */size_t bulk_in_filled; /* number of bytes in the buffer */size_t bulk_in_copied; /* already copied to user space */__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */int errors; /* the last request tanked */bool ongoing_read; /* a read is going on */spinlock_t err_lock; /* lock for errors */struct kref kref;struct mutex io_mutex; /* synchronize I/O with disconnect */wait_queue_head_t bulk_in_wait; /* to wait for an ongoing read */
};
主要属性:
描述usb设备的结构体:udev
一个结构:interface
用于并发访问控制的信号量:limit_sem
用于接收数据的缓冲:bulk_in_buffer
用于接收数据的缓冲尺寸:bulk_in_size
批量数据端口地址:bulk_in_endpointAddr
批量输出端口地址:bulk_out_endpointAddr
usb_interface: usb接口数据结构:
struct usb_interface {struct usb_host_interface *altsetting;struct usb_host_interface *cur_altsetting; /* the currently * active alternate setting */unsigned num_altsetting; /* number of alternate settings */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 */
};
usb_hgost_endpoint: usb端点数据结构
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;int streams;
};
欢迎关注亦梦云烟的微信公众号: 亦梦智能计算
ZYNQ学习之路9.USB总线学习(二)相关推荐
- alin的学习之路(数据库篇:二)(select查询,where条件查询,order by排序,单行函数,多行函数,group by分组)
alin的学习之路(数据库篇:二)(select查询,where条件查询,order by排序,单行函数,多行函数,group by分组) 1. SQL语句 1.1 sql语言类型 sql是一门独立的 ...
- FPGA学习之路—应用程序—原码二位乘法器及Verilog代码分析
FPGA学习之路--原码二位乘法器及Verilog代码分析 原理 原码乘法可以分为原码一位乘和原码二位乘,两者在实现规则上大同小异.原码一位乘每次判断乘数的最低位,对被乘数和部分积进行相应操作.而原码 ...
- Spring Boot 学习之路之 Spring Security(二)加入mybatis
上一篇 Spring Security 基础配置: http://t.csdn.cn/m9oq5 在上文Spring Boot 学习之路之 Spring Security(一)中完成了 ...
- 交互设计师学习之路:第一天 色彩学习
第一次写博客.(该博文是总结摘录学习过程中的一些资料,以及自己的一些感悟) 阴差阳错的开始了自己的未来职业之路.身为计算机的学生,我也纠结了很久,实习被安排到做UI/UE,感觉从此与代码无缘.虽然平时 ...
- 大数据学习之路(七)——学习小结
个人目前学习的总结,如有问题,发现的时候会修正,用于个人回顾,有错误的地方欢迎留言指出 通过前几篇的学习 hadoop单节点伪分布式 hadoop完全分布式 hadoop完全分布式高可用(HA) zo ...
- 虚幻引擎学习之路:粒子系统篇(二)
在此,特别感谢Unreal中国团队对于本篇文章中Unreal引擎相关内容的审核,并在UWA团队学习其引擎的道路上提供的大力支持. 一.Module 功能 1.1 Required Required M ...
- 【尚观】Android游戏与应用开发最佳学习之路_转载来学习Android
Android游戏与应用开发最佳学习路线图 为了帮助大家更好的学习Android,并快速入门特此我们为大家制定了以下学习路线图,希望能够帮助大家. 一. 路线图概括: 开博不到一周,不予上传 ...
- orcale存储过程学习之路--创建空存储过程(二)
--创建表 create table TESTTABLE ( id1 VARCHAR2(12), name VARCHAR2(32) ) select t.id1,t.name from T ...
- JavaWeb学习之路——SSM框架之Mybatis(二)
1.简介: 框架: 是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种定义认为,框架是可被应用开发者定制的应用骨架.前者是从应用方面而后者是从目的方面给出的定义.它是一个半 ...
最新文章
- [IE9] 解决了傲游、搜狗浏览器在IE9下网页截图的问题
- 他,跳槽季用这样的方法复习进了阿里
- Android 插件技术实战总结
- 聊聊Batch Normalization在网络结构中的位置
- 请简述php循环控制语句,PHP 循环控制语句几种方法详解_PHP教程
- Java代理设计模式(Proxy)的四种具体实现:静态代理和动态代理
- POJ3320 Jessica's Reading Problem 尺取法
- 阿里测试环境运维及研发效率提升之道
- Mybatis mapper代理Dao开发
- 数据科学入门与实战:Matplotlib绘图DateFrame
- IE7下position:relative的问题
- python3中map的用法_python3内置函数map
- UE4官方文档UI学习:4.UMG 创建控件模板
- [FROM VIJOS]安装服务器
- python代码收费_莱斯大学学费 - 高速公路收费的python设计代码
- 编程的智慧 强烈推荐
- 人声和乐器的频谱范围
- GridControl之GridView(属性篇)(一)
- 【复试】2017北京理工大学上机(一)----身份证校验
- SecureCRT 设置黑底白字和编码
热门文章
- 统计学学习日记:L6-离散趋势分析之总体方差和标准差
- 论文阅读笔记 | 三维目标检测——3DSSD
- python换照片底色_Python 利用OpenCV给照片换底色的示例代码
- 【Unity 优化篇】 | 优化专栏《导航帖》,全面学习Unity优化技巧,让我们的Unity技术上升一个档次
- 元宇宙游戏项目:Decentraland(治理通证:MANA)
- ios:应用发布App Store流程
- 黑帽实战 | 给大家讲讲一个二类电商的大佬的故事!
- java毕设项目广东省梅州市宇恒节能科技有限公司(附源码)
- Python—计算方差、标准差
- c语言编写数据存储的游戏,c语言经典小程序和c语言编写的小游戏带注释(自动保存的).doc...