实习报告

2,242 views

Published on

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,242
On SlideShare
0
From Embeds
0
Number of Embeds
964
Actions
Shares
0
Downloads
6
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide
  • 这个 ppt 的很多东西是个人观点 有许多我自己的看法 很多地方只是一个感性的认识具体的代码细节并没有深入跟踪进去 讲述的过程中如果我讲的不对 或大家哪里有疑问请随时打断我 协议主要是涉及 usb1.1 协议的部分比较难理解 我尽量把我理解的描述清楚 以方便大家理解从主机上层要进行数据传输到下层数据流在 usb 数据线上的流动最终存储到 u 盘是怎么一个过程 我觉得等我讲完了 大家也就晕了 很多地方是前后相互联系的 做 ppt 时 才看了下 2.0 协议
  • Usb 协议 1.0 1.1 2.0 3.0 ( usb 官方网站上有) otg 规范 Usb 设备的好处 动态插拔 关于挂起那部分 我本来想加上 可以一直没理解好 hub 那部分的代码,就没有加
  • TT 事物翻译 低速或这全速连接在高速 hub 上这个才起动,高速设备连接在高速 hub 上这个不会启动
  • PCI  USB 星形拓扑 分层 数据并串转换 反过来 串并 数据广播发送及设备接收。数据的流动
  • 大概讲讲 hub
  • 这里贴出来 给大家一个认识,以后会接触到
  • 内存,显存,网卡内存 端点缓冲区
  • 结构体是完全按照协议来组织的
  • 图片中有实际的物理通信流 也有逻辑通信流, usb 设备驱动层其实处理的是最上层,处理接口之间的通信。 图中的管道 pipe :有两种一种是消息管道,一种流管道。消息管道指的 usb 协议中规定好了格式,其中传输的数据必须是 usb 协议规定的格式才能被接收方所解析。 协议中说: Stream: Data moving through a pipe has no USB-defined structure Message: Data moving through a pipe has some USB-defined structure. 控制传输需要的
  • 数据传送方面
  • 事物的传送是不能被中断的,传输过程中的多个事物可以放在不同的帧中,但是传输中事物的相对顺序在帧中不能被打乱,这由主控那部分操作。 先讲完这一节再看各个内部细节
  • 协议中的图
  • 协议中的
  • 在电气规则上说就是 低速设备 信号边沿上升下降慢 而高速设备边沿陡峭 pre 就是告诉 hub 在给低速设备发包的时候加上一些 stuff 填充 ( 个人理解 ) 分析完包 再回头看事物分析就会很明朗了
  • U 盘用于批量存储,而批量存储就需要用到 bulk 端点, bulk 端点和 host 建立的连接是 stream pipe 。流管道,硬件上不会去解析其中的数据,需要软件来做。 需要软件来做的话就需要一定的规则,其实规则很多,不过 u 盘用的 bulk 协议,就如同计算机网络一样,在网线上流动的数据是比特流,然后需要用协议来 解析其中的数据。 Lsusb –v Scsi 这里没有涉及 scsi 协议,也不会涉及到。只是用 scsi 的命令字,但是好像 u 盘用的是 scsi 的一个分类 ufi 的命令字。
  • 上层看到的这些 在底层最终还是转化为一系列 in out 事物来完成的 Wrapper 是封装的意思
  • 讲解命令时稍微分析下各个字节填充什么 因为对硬盘不熟悉 有的命令还不知怎么用 逻辑地址给人感觉是一个递增的数字 而物理地址分磁道等等
  • Get max lun 不太理解, linux 那些事中说:比如一个读卡器有两个插槽, lun 就是 1 ,代表 0 1 两个插槽。 我做实验的 u 盘不支持 reset 命令,这里的 reset 和以后要说的枚举过程中 reset 不一样。 一个掉电 一个不掉电 ( 个人观点 ) 。
  • 其实不想讲协议了 可是不讲不行
  • 接下来讲 linux usb driver
  • 尽量少讲述代码 涉及到的代码主要是把流程说清
  • 想把内核注释贴出来 可是注释比代码都多 cscope ?注释
  • 注 libusb 使用的就是这个 ioctl 关于 usbfs 的代码实现在 usb core 中实现
  • 貌似很没含金量
  • 如果涉及挂起 和恢复 过程会很复杂 我没搞明白
  • 不知我这里说的清楚与否
  • 为什么说 linux 内核编写者很牛呢 整的看代码的人看不懂 这里只说些感性的描述 因为很多东西我还没弄明白
  • 这个函数我理解的不好 只能大概这样说了
  • 本来没打算说 这是后来加上的
  • 称为文件有点过 其实就是个存储块
  • 很简单的结构
  • 实习报告

    1. 1. 实习报告 樊朋
    2. 2. 主要从以下几个方面来讲述  我的作业,涉及到的知识和完成情况  Usb基本概念及其在linux中的体现  Linux usb系统中hub驱动怎样驱动usb 驱动,usb设备驱动及驱动与协议的结合  应用层的代码框架简述  自己的收获与不足及学习情况  演示
    3. 3. 0.0 作业简述及完成情况  简述:在 linux 下写一个 usb 驱动实现对 u 盘 的读写,通过 usb-skeleton 改写,然后写一个 demo 来演示。  相关知识: usb 协议(控制和批量传输) +linux usb 驱动代码  完成情况:基本功能已经实现,驱动层一次最 多传输 1024 字节, read 使用同步方式实现 , write 使用异步方式。自己定义了一个简单 的文件组织结构以方便演示
    4. 4. 第一部分 usb 基本概念及结构体
    5. 5. 1.0Usb 基本概念  Usb 架构中包括 3 个东西  HOST 主控制器  HUB 主控和设备的连接 作用很重要  Hub Repeater  Hub Controller  Transaction Translator(2.0 中才有的 ).  DEVICE usb 外设如 usb 鼠标,键盘。 设备架构在总线上的表现
    6. 6. 1.0.0Bus Topology Usb 总线是串行总线,数据是在两根差分线上流动的,数据是广播发送
    7. 7. 1.0.2 hub 的作用及内部结构图2.0spec 中的 1.1spec 中没有那个 TT usb 设备和 host 的交互是通过 hub 来进行的 hub 的一个任务就是检测设备的连接和断开
    8. 8. 1.0.2.0 hub 在 linux 中用什么来表示 结构体不全部贴出 只贴几个关键的 Struct usb_hub { struct device *intfdev; /* the "interface" device */ struct usb_device *hdev; struct kref kref; struct urb *urb; …….. union { struct usb_hub_status hub; struct usb_port_status port; } *status; ……. struct list_head event_list; unsigned long event_bits[1]; ……. } 其实这个结构体是用户自己定义的 因为 hub 也是 usb 设备 在 linux 代码中这种描述设 备的 结构体有很多,动不动就上百行 外加几百行的注释 看来写代码的真是按行计费的 @@
    9. 9. 1.0.3Device 、 Configuration 、 interface 、 endpoint 、 pipe  设备是配置的集合  配置是接口的集合,但一次只能激活一个配置  接口是端点的集合,接口可以有多个可选设置,一个 接口有一个驱动,而不是一个设备有一个驱动  端点是通信的最基本形式。端点在物理上其实是一个 缓冲区或者寄存器。  端点:有控制,同步,批量,中断端点 0 号控制端点是每个 usb 设备都有的 U 盘就一个配置 一个接口 3 个端点 PIPE :逻辑上的一个概念 管道 linux 上也有 pipe
    10. 10. 1.0.4 usb 设备在 linux 中如何表示 Struct usb_device { int devnum;// 设备地址 enum usb_device_state state;// 设备状态 上电 默认 挂起 配置等 enum usb_device_speed speed;// 高速 全速 低速 可变 未知 struct usb_device *parent; struct usb_bus *bus;// 设备挂在哪条总线上,这会涉及到 linux 设备模型 ,以后会讲 struct usb_host_endpoint ep0;// 从这里可以看出端点 0 的重要 struct device dev;// 设备模型 } 这个结构体在 /include/linux/usb.h 中定义
    11. 11. 1.0.5 关于描述符 对 1.0.2 的补充描述 概述: USB devices report their attributes using descriptors. A descriptor is a data structure with a defined format.Each descriptor begins with a byte-wide field that contains the total number of bytes in the descriptor followed by a byte-wide field that identifies the descriptor type. 设备描述符: A device descriptor describes general information about a USB device. It includes information that applies globally to the device and all of the device’s configurations. A USB device has only one device descriptor. 配置描述符: The configuration descriptor describes information about a specific device configuration. 接口描述符: The interface descriptor describes a specific interface within a configuration. A configuration provides one or more interfaces, each with zero or more endpoint descriptors describing a unique set of endpoints within the configuration. 端点描述符: This descriptor contains the information required by the host to determine the bandwidth requirements of each endpoint 还有一个字符串描述符 不重要 这些描述符存储在什么地方呢?如何报告给主机呢?这是一个什么过程呢? 简单描述下 u 盘的存储 eeprom flash 单片机
    12. 12. 1.0.5.0 各种描述符在 linux 中的表示 struct usb_device_descriptor { __u8 bLength; __u8 bDescriptorType; __le16 bcdUSB; __u8 bDeviceClass; __u8 bDeviceSubClass; __u8 bDeviceProtocol; __u8 bMaxPacketSize0; __le16 idVendor; __le16 idProduct; __le16 bcdDevice; __u8 iManufacturer; __u8 iProduct; __u8 iSerialNumber; __u8 bNumConfigurations; } __attribute__ ((packed)); struct usb_config_descriptor { __u8 bLength; __u8 bDescriptorType; __le16 wTotalLength; __u8 bNumInterfaces; __u8 bConfigurationValue; __u8 iConfiguration; __u8 bmAttributes; __u8 bMaxPower; } __attribute__ ((packed)); struct usb_interface_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bInterfaceNumber; __u8 bAlternateSetting; __u8 bNumEndpoints; __u8 bInterfaceClass; __u8 bInterfaceSubClass; __u8 bInterfaceProtocol; __u8 iInterface; } __attribute__ ((packed)); struct usb_endpoint_descriptor { __u8 bLength; __u8 bDescriptorType; __u8 bEndpointAddress; __u8 bmAttributes; __le16 wMaxPacketSize; __u8 bInterval; …….. } __attribute__ ((packed));
    13. 13. 1.1 关于 usb 设备的 request 命令 This bitmapped field identifies the characteristics of the specific request. This field specifies the particular request 这三个域根据不同的 命令而定 比如 get_address set_address 方向是不同的
    14. 14. 1.1.0 usb 标准 request 命令 对 1.1 的补充
    15. 15. 1.2 通过这个图说明 Usb 通讯流 给大家 一个感性认识 查看备注 Message pipe 控制传输用的 其它传输用 的。中断, 批量等用的
    16. 16. 1.2.0 逻辑通信和物理通信 对 1.2 的补充  Usb 协议中说:  A USB logical device appears to the USB system as a collection of endpoints. Endpoints are grouped into endpoint sets that implement an interface. Interfaces are views to the function. The USB System Software manages the device using the Default Control Pipe. Client software manages an interface using pipe bundles (associated with an endpoint set). Client software requests that data be moved across the USB between a buffer on the host and an endpoint on the USB device. The Host Controller (or USB device, depending on transfer direction) packetizes the data to move it over the USB. The Host Controller also coordinates when bus access is used to move the packet of data over the USB.
    17. 17. 1.2.1 管道 对 1.2 的补充 从上层来看通信 流 Pipe 逻辑上的概念 抽象一下就是主机和设备都有缓冲区,他们之间要传 输数据,就在两个缓冲区之间建立一个连接
    18. 18. 1.3Transaction (事 物)、 Frame (帧)、 Packet (包)继续 usb 协议分析 传输 transfer 帧 frame 包 ( 比特流 ) 包的组成
    19. 19. 1.3.1 对 1.3 事物,传输,帧,包的祥解  Usb 协议中规定传输方式:控制传输,批量传输,同步传输,中 断传输  传输可以由一个或多个事物构成。控制传输: setup 事物,可选 数据阶段,状态阶段。批量传输:一个或多个 in 事物或者 out 事 物。  事物是不能被中断的,否则会 conflict 。事物由令牌包,数据包 ,状态包组成。 In 事物, out 事物, setup 事物。事物的传输只 能由主机发起 (otg 设备除外 ) ,还有远程唤醒是设备能唯一主动 发起的,比如 usb 键盘可以在电脑休眠时唤醒它  The Host Controller produces SOF tokens at a period of 1ms. 协 议中说主控 1ms 发送一帧。但是现在高速主控每 1/8ms 发送一 帧。  包包括令牌包,数据包,状态包,帧起始包等。组成:同步域, pid 域, address 域, endpoint 域, crc 域,帧号域
    20. 20. 1.3.1.0 usb 事物 对 1.3.1 的补充  事物细节在传输那边讲述 控制传输和批量传输  协议中说  The packets that comprise a transaction varies depending on the endpoint type. There are four endpoint types: bulk, control, interrupt, and isochronous.
    21. 21. 1.3.1.1 us 4 种传输方式 对 1.3.1 的补充  控制传输  Control transfers are intended to support configuration/ command/ status type communication flows between client software and its function. A control transfer is composed of a Setup bus transaction moving request information from host to function,zero or more Data transactions sending data in the direction indicated by the Setup transaction, and a Status transaction returning status information from function to host.  批量传输  The bulk transfer type is designed to support devices that need to communicate relatively large amounts of data at highly variable times where the transfer can use any available bandwidth  同步传输  为实时传输准备的  中断传输  不是我们通常所说的中断 而是每隔一段时间主机询问设备有没有数据 如 usb 鼠标 通过 default pipe 来进行 主要讲述 这两个, u 盘传输只用 这两个
    22. 22. Control transfers setup stage Control tansfers data stage Control transfer status stage 数据阶段不是每个控制传输都有的,举例 Set address 命令, get descriptor 会有 dat 阶段 1.3.1.2 控制传输分析 (setup 事物 in 事物 out 事物 ) 对 1.3.1 补充 Usb 协议规定的命 令格式如: Get Status , Set address 等 事物讲述 传输是多个事物的集合
    23. 23. 1.3.1.3 批量传输分析 ( 本质是 in 事物和 out 事物 ) 对 1.3.1 补充 这个图只列出了具有 一个事物的批量传输
    24. 24. 1.3.1.4 关于包的分析 其实每个包的前面都应该有同步域 sync why ?异步传输和同步传输的不同是因为使用不同的 时钟,所以要同步。还有帧起始包 SOF 。 还有一个 PRE 包,这个包是告诉 hub ,接下来的包是发给低速设备的, pre 包是发给 hub 的 其它 usb 设备会忽略它。关于 pre ,协议中说 : All downstream packets transmitted to low-speed devices require a preamble. The preamble consists of a SYNC followed by a PRE PID, both sent at full-speed. 现在 2.0 的协议还有很多其它的包
    25. 25. 1.3.1.5 通过图来说明上面的传输过程 很经典的一个图,看完这个图 会觉得原来总线上的数据是 这样的啊 @@ 以控制传输为例 分析下这个图 在我没看到这句话之前 我很迷茫 usb 的传输过程 帧
    26. 26. 1.4 实现读写 u 盘还需要了解哪些协议  BULK协议 为什么要使用 bulk 协议 u 盘 interface class : usb mass storage 类 interface subclass : scsi interface protocal : bulk  SCSI命令 为什么还需要了解 scsi @@
    27. 27. 1.4.1BULK 协议 Bulk 协议的核心 cbw data csw 注:不要混淆这里的 bulk 协议 和前面的 bulk transfer (批量传输)
    28. 28. 1.4.1.0BULK CBW CSW Scsi 命令块 这两个 wrapper 都是 由主机发起的,和 前面说的那些包是 不同的 Data 阶段 数据长度
    29. 29. 1.4.1.1SCSI(UFI) 命令字 在我的代码中实现的 UFI 命令: READ WRITE READ CAPACITY 对 u 盘来说是扇区个数 读写起始逻辑地址 Logic block address 其实是扇区地址
    30. 30. 1.4.1.2BULK 命令字  Bulk 还支持两个命令字和 usb 协议中的命令字一样, 前提是外设可以识别这两个命令:  Bulk Reset :软件层面的复位,协议中说: bulk reset 使外设恢复到能准确接收下一个 CBW 的状 态  Get Max Lun :如果给 u 盘发这个命令返回值为 0. 如果 u 盘固件识别这个命令。 For Reset Recovery the host shall issue in the following order: : (a) a Bulk-Only Mass Storage Reset (b) a Clear Feature HALT to the Bulk-In endpoint (c) a Clear Feature HALT to the Bulk-Out endpoint
    31. 31. 1.5 u 盘 主机是怎么知道设备的 是如何通信的  把 u 盘插上的那一刻,到 u 盘开始工作 是怎样一个过程呢,主机是如何知道 usb 设备已经插到电脑上了呢,又是如 何知道 usb 设备已经不存在了呢,主机 又是如何开始与 usb 设备进行通讯的呢 ,带着这些疑问开始这节的讲述
    32. 32. 1.5.0 枚举概述 对 1.5 的详细描述  1. 当查上 usb 设备后,主机 hub 会检测到设备的连接之后主机也 会知道有设备连接,这时设备处在 attached 状态, hub 开始为设 备供电,这时设备处在 powered 状态  2.hub 是如何检测这个设备的呢? hub 端口的 D+ D- 线都有一个 15k 的下拉电阻,而设备在 D+ D- 有上拉电阻,通过电平的变化 ,检测到新设备然后主机通过中断传输得知 hub 哪个端口发生变 化(仅仅是端口发生状态变化,可能是新设备到来,也可能是设 备离开)之后设备通过 GET_PORT_STATUS 控制传输获得端口 状态的具体信息,如果真的是新设备,那好继续向下走  3.host 通过 Set_Port_Feature 控制传输来重置端口 ,port_reset  4.hub 结束对端口的重启之后,设备就处于默认状态。这时 host 可以和 hub 进行控制传输通过 0 号控制端点。此时设备可以从总 线上抽取不超过 100ma 的电流  5.host 通过发送一个控制请求 get_descriptor 给设备地址 0 ,端 点 0 来得到端点 0 的通信能力  6. 这时 host 会分配一个唯一的地址给 usb 设备 (hub 也是 usb 设 备 也有地址 ) 设备进入地址态  7.host 开始通过分配好的地址来读取设备描述符,配置描述符等  8.host 通过得到的配置信息给设备进行一个配置,设备进入配置 态,这时设备可以正常使用了,进入配置态之前会加载驱动。
    33. 33. 1.5.0.0 usb 设备的几个状态 对 1.5.0 的图 解
    34. 34. 1.5.1 主机和 u 盘的通信  经过 1.5.0 节讲述的枚举,驱动加载之后 ,设备就可以正常工作了  无论是 windows 还是 linux 对 u 盘操作 都是需要文件系统的,所以在枚举完之 后,操作系统内部驱动会去读取文件系 统部分,根据得到的文件系统信息,进 而加载相应的文件系统驱动,这是就可 以进行文件的读取,写入操作了。
    35. 35. 1.5.1.0 对 1.5.1 的补充说明  到这里 u 盘的一个通讯流程基本结束了 ,之后开始讲述 linux 下 usb 设备的工作  以写入文件为例重新理顺 u 盘的工作过 程,环境是 xp 。 复制 bulk 传输
    36. 36. 1.5.1.1 图示 u 盘复制(写入)文件的底层机制 补充 1.5.1.0 第二部 分  Bulk 传输 因为使用的是 stream pipe ,所以要使用 bulk 协议对数 据流进行解释 sync Out pid address out endp crc Out token sync data0 pid Data payloads crc sync data1 pid Data payloads crc Data0 data1 交替传输 协议规定的 Data x packet sync In pid address out endp crc In token sync Ack pid handshake CBW DATA OUT 这期间的事物过程可以多可以少看情况而 定吧,我实验用的 u 盘传输 512 字节的 Data payloads CSW 一个文件可能需要多个 bulk 协议规定的过程 一个这样的过程需要多个批量传输过程 代表一个事物 注:上面假设每个事物都正确,上面只代表一个 bulk 协议规定的 3 个阶段 这 3 个阶段都是 bu 传输
    37. 37. 第一部分结束 参考: usb1.1 usb2.0 usb 大全 usb 项目技术报告及对协议的深层次理解 Bulk-only transport Ufi Usb2.0 规范初探
    38. 38. 第二部分 LINUX USB DRIVER HOWTO 这部分会主要讲些驱动代码
    39. 39. 2.0 编写 usb 设备驱动 ( 称接口驱动更合适 些 )  要考虑的问题:  驱动支持哪些设备  驱动中结构体的分析  注册 usb 驱动程序  探测与断开的细节  不使用 urb 的 usb 传输  使用 urb 的 usb 传输 参考 LDD3 ch13
    40. 40. 2.0.0 驱动支持的设备 #define USB_SKEL_VENDOR_ID 0x0204 #define USB_SKEL_PRODUCT_ID 0x6025/* 上面这两个是清华紫光 u 盘的 */ static struct usb_device_id skel_table [] = { { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, skel_table); Usb 核心使用上面的列表来判断对于一个设备应该使用哪一个驱动,热插拔 脚本使用它来决定当一个特定的设备插入时该自动装载哪一个驱动 对于 pc 驱动程序 MODULE_DEVICE_TABLE 这个宏是必需的,以允许用 户空间的程序判断这个驱动可以控制什么设备,所以这里第一个参数是 usb 字符串 USB_DEVICE 这个宏是给结构体中的字段赋值 编写简单的驱动按照这个 模式来确定 驱动支持什么设备就可以了贴出其它的几个宏:和 USB_DEVICE 宏的作用 一样 USB_DEVICE_VER(vendor,product,lo,hi) USB_DEVICE_INFO(class,subclass,protocal)
    41. 41. 2.0.1 注册 usb 设备驱动 所有的 usb 设备驱动都必须有 一个 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 (*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; };
    42. 42. 2.0.1.0 对 2.1.0 部分成员描述 来自内核代码  @name: The driver name should be unique among USB drivers,  * and should normally be the same as the module name.  * @probe: Called to see if the driver is willing to manage a particular  * interface on a device. If it is, probe returns zero and uses  * usb_set_intfdata() to associate driver-specific data with the  * interface. It may also use usb_set_interface() to specify the  * appropriate altsetting. If unwilling to manage the interface,  * return -ENODEV, if genuine IO errors occured, an appropriate  * negative errno value.  * @disconnect: Called when the interface is no longer accessible, usually  * because its device has been (or is being) disconnected or the  * driver module is being unloaded.  * @ioctl: Used for drivers that want to talk to userspace through  * the "usbfs" filesystem. This lets devices provide ways to  * expose information to user space regardless of where they  * do (or don't) show up otherwise in the filesystem.  * @suspend: Called when the device is going to be suspended by the system.  * @resume: Called when the device is being resumed by the system.  * @supports_autosuspend: if set to 0, the USB core will not allow autosuspend for interfaces bound to this driver.
    43. 43. 2.0.1.1 我们的 u 盘驱动的结构体  static struct usb_driver skel_driver = {  .name = “udisk_driver",  .probe = skel_probe,  .disconnect = skel_disconnect,  //.suspend = skel_suspend,  //.resume = skel_resume,  .id_table = skel_table,  //.supports_autosuspend = 1,  };
    44. 44. 2.0.2 驱动中结构体的分析 ( 一 )  struct usb_skel {  struct usb_device *udev;  struct usb_interface *interface; unsigned char *bulk_buffer; /* the buffer to receive send data */  size_t bulk_size; /* the size of the receive send buffer */  __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 */  int open_count; /* count the number of openers */  spinlock_t err_lock; /* lock for errors */  struct kref kref;  struct mutex io_mutex; /* synchronize I/O with disconnect */  atomic_t skel_available; /* 只允许应用打开设备一次 */  }; 在 linux 代码骨架代码中解释是 Structure to hold all of our device specific stuff 这个结构体是驱动程序的编写者自己定义的 ,但是有一个成员是必须有的 struct usb_device *udev ; 因为这是 usb 设备驱动 ,没它,这个驱动还有什么意义。
    45. 45. 2.0.3 驱动中结构体的分析 ( 二 )  static const struct file_operations skel_fops = {  .owner = THIS_MODULE,  .read = skel_read,  .write = skel_write,  .open = skel_open,  .ioctl = skel_ioctl,  .release = skel_release,  .flush = skel_flush,  };// 这个结构体定义对设备进行操作的函数 一切都是文件 (linux 中 )  static struct usb_class_driver skel_class = {  .name = “udisk%d",  .fops = &skel_fops,  .minor_base = USB_SKEL_MINOR_BASE,  };//identifies a USB driver that wants to use the USB major number (我觉的这个是给 udev 用的 猜测)
    46. 46. 2.0.3.0 补充 2.0.3 结构体分析  identifies a USB driver that wants to use the USB major number 内核中这样说的。那么从另一个层面我们就会知道有的 usb 驱动 不会是用 usb 主设备号,那这就会给人带来疑问? Linux 把一切 都当作文件来操作,设备在 linux 中表示为设备文件每个设备文 件都由主设备号和次设备号来唯一确定一个设备,没有主设备 号??这是因为有的 usb 设备驱动会挂接在其它的驱动下。拿我 们的 u 盘为例:如果你的 linux 内核中编译了 usb mass storage 驱动和 scsi 驱动,那么 u 盘在 /dev 目录下会显示 sda* 也就说 u 盘被模拟为一个 scsi 设备,这当然就不用 usb 主设备号了,看看 usb mass storage 驱动你肯定找不到用 struct usb_class_driver 定义的结构体。还有的 usb 设备驱动会挂接在 input 驱动上,也 不会使用 usb 主设备号。 如果你的驱动只是一个单纯的 usb 驱动,不挂接其他的驱动,用这 个结构体定义一个变量,并且向 usb core 进行注册是必须 DI 。
    47. 47. 2.0.3 探测与断开的细节 在 usb_driver 中驱动程序指定两个 usb core 可以在适 当的 时候调用的函数。当一个设备被安装时,探测函数被调 用,探测函数会检查传递给它的设备信息,确认该驱动 是否真的符合这个设备。当因某种原因驱动不再控制设 备时,断开函数被调用,来做一些清理工作。 下面两个函数: int Skel_probe(struct usb_interface *interface, const struct usb_device_id *id) Void Skel_disconnect(struct usb_interface *interface)
    48. 48. 2.0.3.0 关于探测函数 Skel_probe(struct usb_interface *interface, const struct usb_device_id *id)  在 usb 核心觉得这个驱动能处理一个新设备时,这 个函数将被调用,在这个函数中,它应该初始化任何 可能用于控制 usb 设备的局部结构,还应该把所需的 相关信息保存到局部结构体中,因为在此时做这个工 作比较容易。  在我们的驱动中 主要是申请一个 usb_skel 结构体, 然后对它进行初始化,同时把我们的设备向系统进行 注册 ,int usb_register(struct usb_interface *intf, struct usb_class_driver *class_driver); 这之后就可以使用 skel_fops 中定义的函数对设备进行操作了  代码不贴了 太多了 cscope
    49. 49. 2.0.3.1 关于断开函数 Skel_disconnect(struct usb_interface *interface)  当一个设备被断开时,和该设备相关联 的所有资源尽量都尽可能被清理掉。如 果在 skel_probe 中注册了次设备号的话 ,用 usb_deregister_dev 来把次设备号 交还给 usb core ,并且把一些私有指针 给清掉。  cscope
    50. 50. 2.0.4 不使用 urb 的 usb 传输 在 linux 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, __index, void *data, __u16 size, int timeout); 这里说的不使用并不是真的不使用 只不过不需要驱动编 写者来操作 urb Cscope
    51. 51. 2.0.4.0 usb_bulk_msg 解析  注释中说: Builds a bulk urb, sends it off and waits for completion  This function sends a simple bulk message to a specified endpoint and waits for the message to complete, or timeout. This function sends a simple bulk message to a specified endpoint and waits for the message to complete, or timeout.  我们要实现对 u 盘的读取或写入如果以同步方式来实 现,就需要执行 3 次 usb_bulk_msg() ,分别完成 CBW, DATA in or out , CSW 的传输。  注:以这种方式实现可能会对系统性能产生影响,并 且会容易宕机,但是我在用异步实现写的时候也没少 宕机过。主要还是看代码的编写问题。还有涉及 dma 的问题 。一点个人看法。
    52. 52. 2.0.4.0.0usb_bulk_msg 图示 只分析批量传输  Usb_bulk_msg- | |->usb_alloc_urb | |->usb_fill_bulk_urb | |->usb_start_wait_urb | |->usb_submit_urb | |->wait_for_completion_timeout | |->usb_free_urb 这是个大概的流程,内部还有很多细节 在这里等待这 次传输的结束 。 阻塞等待,直 到超时
    53. 53. 2.0.4.1usb_control_msg  usb_control_msg - Builds a control urb, sends it off and waits for completion. This function sends a simple control message to a specified endpoint and waits for the message to complete, or timeout. 和 usb_bulk_msg 中的注解基本相同,但是这 个函数在给 u 盘发送控制请求时比驱动开发人 员自己分配 urb 然后初始化方便的多,也不容 易出错
    54. 54. 2.0.5 使用 urb 的传输  创建 urb usb_alloc_urb 释放 usb_free_urb  填充 urb usb_fill_bulk_urb  提交 urb usb_submit_urb  完成回调函数来做些清理工作,在这个函数中不能有 导致睡眠的函数,因为完成回调函数是在中断上下文 执行的。  取消 urb usb_kill_urb usb_unlink_urb  现在内核还添加了 usb_anchor_urb 为了人们能对 urb 进行跟踪
    55. 55. 2.0 节结语  知道了 2.0 节所描述的 linux usb 驱动中涉及的 api ,第一部分的 几个 usb 结构和与设备进行通讯的协议流程,再就是阅读 linux 内核中提供的 usb-skeleton.c 代码,就可以写一个基本的 usb 驱 动了。但让还要考虑代码中一些锁的问题,还有一些代码细节问 题,这主要看编码能力。在我的这个驱动中,对一个设备,只允 许被打开一次,所以很多问题就被简化了。  抛开设备驱动代码的细节,原来驱动就这么回事?如果仅仅满足 写完一个驱动到这里就可以结束了。再往内部看,这个驱动怎么 就被执行了?怎么 probe 函数就被执行了?探测函数的参数是谁 传递给它的呢?枚举过程在 linux 内核中是怎么体现的? U 盘不 是通过 hub 连接主机的么, hub 驱动都做了什么?这部分主要是 讲述 hub 驱动的工作。很是让人好奇。 Fu_abc 说过:读代码就 像读故事。 参考 linux 那些事之我是 hub /drivers/usb/core/hub.c
    56. 56. 2.1 hub 驱动  Hub 驱动也是个 usb 驱动,不过它是驱动 hub 而已。  Hub 驱动会在电脑开机的时候的自动加载,除 非你在内核编译过程中把 usb support 给 pass 掉  Hub 驱动代码的实现在内核中存在 于 /drivers/usb/core 下。  后面的讲述都假设 hub 没有挂起,一直处于活 动状态
    57. 57. 2.1.0hub 驱动初始化过程  Usb_hub_init() hub 的初始化函数在 usb core 初始化 时会调用它,可以看出 hub 的地位,在 usb core 中实 现。  Usb_hub_init 中执行这句, khubd_task = kthread_run(hub_thread, NULL, “khubd”); 这个函数的 意思,就是在系统中建立一个内核线程,名字是 khubd ,通过 ps 可以看到,创建的内核线程的执行体 是 hub_thread() 函数。而 hub_thread() 函数会调用另 外的函数 hub_events(), 这是 hub 驱动的核心,重量级 函数,所有的 usb 外设驱动需要的结构体大部分都在 这里完成,然后再把使用结构体的权利下放给设备驱 动。前面说的探测函数的参数就是在这部分完成的。 如果 hub 端口没有检测到变化 hub_events 会睡眠
    58. 58. 2.1.0.0 先补充一点 hub 的知识  Hub 设备中有两个端点,又讲到协议了,一个控制端 点,一个中断端点。控制端点在前面第一部分说过是 发送控制命令请求,查询状态等用的,而中断端点呢 ,主机会每隔一定的时间来给设备发一个中断传输, 然后通过传回的数据来确定哪个 hub 端口有变化,确 定哪个端口有变化了,然后把睡眠中的 hub_events 唤 醒,开始进一步的工作  根 hub 和其他的 hub 貌似不同,不过感性上为了理解 可以认为都是通过中断传输来获得端口状态变化的
    59. 59. 2.1.0.1hub_events 函数  Hub_events 被唤醒后,会判断哪个端口 有变化,然后进一步去获取端口的状态 ,是什么原因导致端口的变化,如果是 新设备到来,那好就开始枚举过程。  在枚举过程中有一个重要的函数是 static void Hub_port_connect_change ( struct usb_hub *hub, int port1,u16 portstatus, u16 portchange );
    60. 60. 2.1.0.2hub_port_connect_change  这个函数都做了什么呢?  Usb_alloc_dev(); attached 态  usb_set_device_state(udev,USB_STATE_POWERED );powered 态  choose_address(udev); 选择一个地址  hub_port_init(); 复位并设置地址 进入地址态  usb_new_device(); 进入配置态,这个函数还有内容  看看这几个函数是不是很前面的枚举图表类似呢?代 码和协议的结合
    61. 61. 2.1.0.3usb_new_device  这个函数会遍历总线上的驱动,来寻找 合适的能给这个设备服务的驱动,找到 了就会执行驱动的 probe 探测函数,同 时把 probe 函数需要的结构体指针传过 去,比如 skel_probe ( struct usb_interface *intf, struct usb_device_id *id ) ; 这两个参数就是在这里给赋值的。 遍历的过程貌似关系到设备模型了 后面 再说
    62. 62. 2.1.1 设备模型  系统如何根据设备找到驱动 驱动如何找 到设备 看看指针是多么强大 看看结构体 的运用 fudan_abc 说把代码当做故事读。 在这里真的会把故事当做代码来读  这里先说一个框框 然后再说一下 usb 驱 动和设备在设备模型中是怎样表示的。
    63. 63. 2.1.1.0 设备模型的图示  Struct bus_type 总线  |->struct bus_type_private *p  struct klist klist_devices; 总线上挂着设备链表  struct klist klist_drivers; 总线上挂着驱动链表  struct device_driver 设备驱动  |->struct bus_type *p  |->Struct driver_private *p  Struct klist klist_devices  struct device 设备  |->struct bus_type *bus  |->Struct device_driver *driver 在你看代码的时候 一是指针 一是链表 把东西穿起来
    64. 64. 2.1.1.1usb 的设备和驱动在模型中的表示  Struct bus_bype usb_bus_type 不要误以为 usb_bus 是总线  Struct usb_device  Struct usb_driver  这两个结构体内部都嵌套了 struct device 和 struct device_driver 结构体所以就相互联系起来了  声明一点:我们写的所谓 usb 设备驱动并不挂在总线上 其实是接 口驱动 那不挂在总线上那设备模型对 usb 驱动有什么意义? Usb_device 是挂在总线上的 可你的 usb_driver 不挂在总线上。 很迷惑很迷茫?在 driver.c 中有相关函数 那我们的探测函数怎么 被调用呢 中间有个一函数是 usb_probe_interface 前面的 usb_new_device 就是调用这个函数 然后通过这个函数再调用我 们自己的 skel_probe 函数 因为 usb_new_device 没有跟进去 所 以有的地方理解的不好
    65. 65. 2.2hub 驱动结语  Hub 驱动的大概流程就是这样,很多细 节我理解的不好,一个感性认识,方便 理解上层设备驱动的工作。  这部分主要参考: linux 那些 事, /drivers/usb/core , usb_skeleton.c, ldd3 下面开讲应用 demo ,比较简单
    66. 66. 第三部分  应用框架概述  主要对应用代码大概结构及简单的文件组织结构进行描述
    67. 67. 3.0 一点 linux 文件操作的知识  在 linux 中设备是被当做文件来操作的, 和普通文件操作一样调用的接口是  Open read write close ioctl 这几个函数 在驱动层对应 struct file_operations 结 构中的函数  其实这些调用是通过 vfs 虚拟文件系统 来做桥梁的
    68. 68. 3.1main() 函数  open(“/dev/udisk0”, O_RDWR);//udisk1  buffer_analy();  do_cmnd();  Close(fd);  应用层的 main 主函数主要就是这四个函 数。
    69. 69. 3.2buffer_analy()  这个函数不只是用在 main 函数中,在其它需 要输入的地方都用了它  它主要是对输入的字符进行处理,同时返回值 给相应的函数。  在这个函数中通过 execl 系统调用能支持在这 个应用中使用 shell 命令 但是不支持 $bash –c bash 如果在这个应用中这样执行后果是应用 stopped 你可以通过 jobs 命令来查看作业号, 然后用 fg * 来重新运行应用。
    70. 70. 3.3do_cmnd()  这个函数的一个参数用到了 3.2 buffer_analy 的返回值 用这个返回值做 命令号,来决定 do_cmnd 执行什么命令 。  do_cmnd() 主要是 switch 语句,这个函 数里面定义了 10 个用户层的命令,主要 使用了 ioctl read write 三个函数,因为 有的 u 盘不支持复位 所以去掉了
    71. 71. 3.4 简单的文件组织结构  这个应用只用到了 u 盘的前 2M ,支持 128 个文件,每个文件最大 16k 。使用 u 盘的第一个扇区来存储文件组织结构的信息。一个扇区为 512 字节,分三部分使用。用数字代表文件名,每个文件的存放位置固 定。 512 字 节 128 字节的每个字节指示某个位置有没有文件 128 字节的每个字节指示每个文件的长度 256 字节每两个字节指示一个文件 最后一个扇区没有使用的空间的长度
    72. 72. 3.4.0 文件组织结构 文件 1 的存放位置,无论文件 1 存在与否,其它文件都不会占用这个空间 长度 32 个扇区 16k ,其它文件依次后排 固定的位置存放固定的文件 起始扇区 其逻辑地址为 0
    73. 73. 3.5 应用结语 功能很简单  难点在于对输入字符的处理  只能对设备打开一次 , 防止竞争操作,这是在 驱动层做的工作  可以打开多个应用 对不同的 usb 设备操作 也 就是对不同的设备进行文件操作 应用会提示你 输入设备名  下面说下自己的体会 大家如果觉得我哪里讲述 的不清楚或不对 请随时打断我
    74. 74. 第四部分 一点体会 收获与不足  在完成的过程中协议是很重要的 usb 协议 bulk 协议 scsi 然后 根据协议构建命令包 在这里我出过 n 多问题  驱动代码已经有框架了,关于这个代码框架,我觉得是 linux usb 开发者一是方便大家入门,知道怎么去写 usb 驱动,再就是展示 新特性 , 因为很多内核 usb 代码中没有 anchor 没有 suspend , 而在这里面都有体现。  修改代码的主要工作就是根据协议来修改,就是一些代码的细节 问题,其实还是协议的一些规定  协议不看不行,看还看不懂,这是刚开始,现在也有很多不懂的 地方,和代码一样很多细节停留在感性的认识上。根据代码再回 头分析协议,这对理解协议帮助很大。等理解的差不多了再重新 理一下思路会明朗许多。要有耐心。  在调试程序的时候如果发现结果出错了,那一定是程序错了,应 用层进程状态成为 D 不可中断进程,意味着你杀不掉它,不要怀 疑是 pc 和 u 盘固件的问题,这几乎不可能。出错之后的结果将 不得不让你 restart ,不用怀疑。驱动出错,意味着内核出错。假 设你在编译内核的时候允许 forcing module load 和 forcing module unload ,我感觉也没用,我试过(个人水平有限),照 样宕机。
    75. 75.  调程序的时候犯过很多低级错误,比如应该是 == 却写成 =  还有在 bulkmsg 的时候应该是 snd 确写成 rcv 这是拷贝导致的结果啊  容易把;忘掉 还有 {} 有时代码多了 看不清楚 删错一个 错误一大片。  还有是命令组包的时候出错 前期在这里出了很 多错误。  再就是一些代码细节问题 内存的申请 释放 好 几次忘记释放掉。  进行读写的时候不能拔掉设备 会宕机
    76. 76. 关于挂起 异步 的一点小感概  可惜水平有限没有实现自动挂起,在框架代码中已经有了支持自 动挂起的代码,我在这里用了一个星期的时间,暂时宣布放弃。  说一下挂起,不得不再次谈到协议,挂起分两种:全局性的挂起 ,和局部挂起 (selective). 全局挂起是针对 usb 子系统的,是由 pm 子系统来管理的,当年设备模型就是由 pm 发展来的,这时 sof 包会被中止等等。而选择性挂起是针对 hub 端口的。在代码 中的体现就是 interface 先挂起,然后 device 挂起。 Interface 挂 起是软件层面上的,而 device 是针对端口的挂起,也就是这个设 备以后不会收到任何包 (3ms) ,除非 resume 。  关于异步,在刚开始写驱动时总是不成功,后来转用同步。现在 只在写那部分用异步实现,因为 5 月 1 号的时候才把异步弄好, 因为如果把读的同步操作也改的话,很多其它的命令也需要改动 ,所以只是小范围把写改成异步了。 异步和同步的速度差别挺大 ,在执行 dd 命令时 可以看到 现在对 dd 命令的偏移量还不支持 dd 命令以字节为便宜 而我的驱动是以扇区为偏移 不使用 seek 和 skip 参数的话 dd 命令还是可以运行的 如 dd if=/dev/udisk0 of=a bs=1024 count=1
    77. 77.  难点在于不会调试驱动代码,只是通过输出消息来看 问题出在哪里,在弄挂起那部分时, dev_vdbg 消息一 直不输出,内核我重现编译过好几次,没有效果,所 以关于挂起内部执行的一些调试消息就没有看到。  Linux kernel usb 那部分代码的参考文档很少,主要 是国外的一些网站,还有 /Documentations 文档。  主要还是对内核不熟悉,看内核代码的心情是路漫漫 其修远兮,吾将上下而求索,做梦都希望代码中就一 句 printk 。其实协议也是一块骨头。  代码要不止一遍的读,协议要不止一遍的看。知识在 于点滴的积累,希望以后能把代码当故事。
    78. 78.  谢谢  结束了 开始演示

    ×