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所示:

图1-1: USB驱动架构

一个USB设备驱动程序由一些配置、接口和端点组成。一个USB设备可以包含一个或多个配置,每个配置可包含一个或多个接口,在每个接口中可含有若干各端点。这些单元之间的关系如图1-2所示:

图1-2: USB描述符组织结构

二. 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总线学习(二)相关推荐

  1. alin的学习之路(数据库篇:二)(select查询,where条件查询,order by排序,单行函数,多行函数,group by分组)

    alin的学习之路(数据库篇:二)(select查询,where条件查询,order by排序,单行函数,多行函数,group by分组) 1. SQL语句 1.1 sql语言类型 sql是一门独立的 ...

  2. FPGA学习之路—应用程序—原码二位乘法器及Verilog代码分析

    FPGA学习之路--原码二位乘法器及Verilog代码分析 原理 原码乘法可以分为原码一位乘和原码二位乘,两者在实现规则上大同小异.原码一位乘每次判断乘数的最低位,对被乘数和部分积进行相应操作.而原码 ...

  3. Spring Boot 学习之路之 Spring Security(二)加入mybatis

    上一篇 Spring Security 基础配置:  http://t.csdn.cn/m9oq5​​​​​​​ 在上文Spring Boot 学习之路之 Spring Security(一)中完成了 ...

  4. 交互设计师学习之路:第一天 色彩学习

    第一次写博客.(该博文是总结摘录学习过程中的一些资料,以及自己的一些感悟) 阴差阳错的开始了自己的未来职业之路.身为计算机的学生,我也纠结了很久,实习被安排到做UI/UE,感觉从此与代码无缘.虽然平时 ...

  5. 大数据学习之路(七)——学习小结

    个人目前学习的总结,如有问题,发现的时候会修正,用于个人回顾,有错误的地方欢迎留言指出 通过前几篇的学习 hadoop单节点伪分布式 hadoop完全分布式 hadoop完全分布式高可用(HA) zo ...

  6. 虚幻引擎学习之路:粒子系统篇(二)

    在此,特别感谢Unreal中国团队对于本篇文章中Unreal引擎相关内容的审核,并在UWA团队学习其引擎的道路上提供的大力支持. 一.Module 功能 1.1 Required Required M ...

  7. 【尚观】Android游戏与应用开发最佳学习之路_转载来学习Android

    Android游戏与应用开发最佳学习路线图 为了帮助大家更好的学习Android,并快速入门特此我们为大家制定了以下学习路线图,希望能够帮助大家. 一.      路线图概括: 开博不到一周,不予上传 ...

  8. orcale存储过程学习之路--创建空存储过程(二)

    --创建表 create table TESTTABLE (   id1  VARCHAR2(12),   name VARCHAR2(32) ) select t.id1,t.name from T ...

  9. JavaWeb学习之路——SSM框架之Mybatis(二)

    1.简介: 框架: 是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种定义认为,框架是可被应用开发者定制的应用骨架.前者是从应用方面而后者是从目的方面给出的定义.它是一个半 ...

最新文章

  1. [IE9] 解决了傲游、搜狗浏览器在IE9下网页截图的问题
  2. 他,跳槽季用这样的方法复习进了阿里
  3. Android 插件技术实战总结
  4. 聊聊Batch Normalization在网络结构中的位置
  5. 请简述php循环控制语句,PHP 循环控制语句几种方法详解_PHP教程
  6. Java代理设计模式(Proxy)的四种具体实现:静态代理和动态代理
  7. POJ3320 Jessica's Reading Problem 尺取法
  8. 阿里测试环境运维及研发效率提升之道
  9. Mybatis mapper代理Dao开发
  10. 数据科学入门与实战:Matplotlib绘图DateFrame
  11. IE7下position:relative的问题
  12. python3中map的用法_python3内置函数map
  13. UE4官方文档UI学习:4.UMG 创建控件模板
  14. [FROM VIJOS]安装服务器
  15. python代码收费_莱斯大学学费 - 高速公路收费的python设计代码
  16. 编程的智慧 强烈推荐
  17. 人声和乐器的频谱范围
  18. GridControl之GridView(属性篇)(一)
  19. 【复试】2017北京理工大学上机(一)----身份证校验
  20. SecureCRT 设置黑底白字和编码

热门文章

  1. 统计学学习日记:L6-离散趋势分析之总体方差和标准差
  2. 论文阅读笔记 | 三维目标检测——3DSSD
  3. python换照片底色_Python 利用OpenCV给照片换底色的示例代码
  4. 【Unity 优化篇】 | 优化专栏《导航帖》,全面学习Unity优化技巧,让我们的Unity技术上升一个档次
  5. 元宇宙游戏项目:Decentraland(治理通证:MANA)
  6. ios:应用发布App Store流程
  7. 黑帽实战 | 给大家讲讲一个二类电商的大佬的故事!
  8. java毕设项目广东省梅州市宇恒节能科技有限公司(附源码)
  9. Python—计算方差、标准差
  10. c语言编写数据存储的游戏,c语言经典小程序和c语言编写的小游戏带注释(自动保存的).doc...