usb_bulk_msg 说明

本文详细介绍USB设备驱动程序的编写方法,包括usb_bulk_msg和usb_control_msg接口函数的使用,urb的初始化过程,以及如何编译配置和使用驱动模块。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

usb_bulk_msg接口函数的定义如下:

  int usb_bulk_msg(struct usb_device *usb_dev,unsigned int pipe,

  void *data,int len,int *actual_length,int timeout);

  其参数为:

  struct usb_device *usb_dev:指向批量消息所发送的目标USB设备指针。

  unsigned int pipe:批量消息所发送目标USB设备的特定端点,此值是调用usb_sndbulkpipe或者usb_rcvbulkpipe来创建的。

  void *data:如果是一个OUT端点,它是指向即将发送到设备的数据的指针。如果是IN端点,它是指向从设备读取的数据应该存放的位置的指针。

  int len:data参数所指缓冲区的大小。

  int *actual_length:指向保存实际传输字节数的位置的指针,至于是传输到设备还是从设备接收取决于端点的方向。

  int timeout:以Jiffies为单位的等待的超时时间,如果该值为0,该函数一直等待消息的结束。

  如果该接口函数调用成功,返回值为0,否则返回一个负的错误值。

  usb_control_msg接口函数定义如下:

  int usb_control_msg(struct usb_device *dev,unsigned int pipe,__u8    request,__u8requesttype,__u16 value,__u16 index,void *data,__u16 size,int timeout)

  除了允许驱动程序发送和接收USB控制消息之外,usb_control_msg函数的运作和usb_bulk_msg函数类似,其参数和usb_bulk_msg的参数有几个重要区别:

  struct usb_device *dev:指向控制消息所发送的目标USB设备的指针。

  unsigned int pipe:控制消息所发送的目标USB设备的特定端点,该值是调用usb_sndctrlpipe或usb_rcvctrlpipe来创建的。

  __u8 request:控制消息的USB请求值。

  __u8 requesttype:控制消息的USB请求类型值。

  __u16 value:控制消息的USB消息值。

  __u16 index:控制消息的USB消息索引值。

  void *data:如果是一个OUT端点,它是指身即将发送到设备的数据的指针。如果是一个IN端点,它是指向从设备读取的数据应该存放的位置的指针。

  __u16 size:data参数所指缓冲区的大小。

  int timeout:以Jiffies为单位的应该等待的超时时间,如果为0,该函数将一直等待消息结束。

  如果该接口函数调用成功,返回传输到设备或者从设备读取的字节数;如果不成功它返回一个负的错误值。

  这两个接口函数都不能在一个中断上下文中或者持有自旋锁的情况下调用,同样,该函数也不能被任何其它函数取消,使用时要谨慎。

  我们要给未知的USB设备写驱动程序,只需要把这个框架程序稍做修改就可以用了,前面我们已经说过要修改制造商和产品的ID号,把0xfff0这两个值改为未知USB的ID号。

  #define USB_SKEL_VENDOR_ID      0xfff0

  #define USB_SKEL_PRODUCT_ID     0xfff0

  还有就是在探测函数中把需要探测的接口端点类型写好,在这个框架程序中只探测了批量(USB_ENDPOINT_XFER_BULK)IN和OUT端点,可以在此处使用掩码(USB_ENDPOINT_XFERTYPE_MASK)让其探测其它的端点类型,驱动程序会对USB设备的每一个接口进行一次探测,当探测成功后,驱动程序就被绑定到这个接口上。再有就是urb的初始化问题,如果你只写简单的USB驱动,这块不用多加考虑,框架程序里的东西已经够用了,这里我们简单介绍三个初始化urb的辅助函数:

  usb_fill_int_urb :它的函数原型是这样的:

  void usb_fill_int_urb(struct urb *urb,struct usb_device *dev,

  unsigned int pipe,void *transfer_buff,

  int buffer_length,usb_complete_t complete,

  void *context,int interval);

  这个函数用来正确的初始化即将被发送到USB设备的中断端点的urb。

  usb_fill_bulk_urb :它的函数原型是这样的:

  void usb_fill_bulk_urb(struct urb *urb,struct usb_device *dev,

  unsigned int pipe,void *transfer_buffer,

  int buffer_length,usb_complete_t complete)

  这个函数是用来正确的初始化批量urb端点的。

  usb_fill_control_urb :它的函数原型是这样的:

  void usb_fill_control_urb(struct urb *urb,struct usb_device *dev,unsigned int pipe,unsigned char *setup_packet,void *transfer_buffer,int buffer_length,usb_complete_t complete,void *context);

  这个函数是用来正确初始化控制urb端点的。

  还有一个初始化等时urb的,它现在还没有初始化函数,所以它们在被提交到USB核心前,必须在驱动程序中手工地进行初始化,可以参考内核源代码树下的/usr/src/~/drivers/usb/media下的konicawc.c文件。

  驱动模块的编译、配置和使用

  现在我们的驱动程序已经大体写好了,然后在linux下把它编译成模块就可以把驱动模块插入到内核中运行了,编译的Makefile文件可以这样来写:

  ifneq ($(KERNELRELEASE),)

         obj-m := xxx.o

  else

         KERNELDIR ?= /lib/modules/$(shell uname -r)/build

         PWD := $(shell pwd)

  default:

         $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

  endif

  clean:

         rm -rf *.mod.* *.o *.ko .*.ko.* .tmp* .*.mod.o.* .*.o.*

  其中xxx是源文件的文件名,在linux下直接执行make就可以生成驱动模块(xxx.ko)了。生成驱动模块后使用insmod xxx.ko就可以插入到内核中运行了,用lsmod可以看到你插入到内核中的模块,也可以从系统中用命令rmmod xxx把模块卸载掉;如果把编译出来的驱动模块拷贝到/lib/modules/~/kernel/drivers/usb/下,然后depmod一下,那么你在插入USB设备的时候,系统就会自动为你加载驱动模块的;当然这个得有hotplug的支持;加载驱动模块成功后就会在/dev/下生成设备文件了,如果用命令cat/proc/bus/usb/devices,我们可以看到驱动程序已经绑定到接口上了:

  T:  Bus="03" Lev="01" Prnt="01" Port="01" Cnt="01" Dev#=  2 Spd="12"  MxCh= 0

  D:  Ver= 1.10 Cls="02"(comm.) Sub="00" Prot="00" MxPS= 8 #Cfgs=  1

  P:  Vendor="1234" ProdID="2345" Rev= 1.10

  C:* #Ifs= 1 Cfg#= 1 Atr="c0" MxPwr=  0mA

  I:  If#= 1 Alt= 0 #EPs= 2 Cls="0a"(data ) Sub="00" Prot="00" Driver="test"_usb_driver /*我们的驱动*/

  E:  Ad="01"(O) Atr="02"(Bulk) MxPS=  64 Ivl="0ms"

  E:  A

d="82"(I) Atr="02"(Bulk) MxPS=  64 Ivl="0ms"

  此框架程序生成的是skel0(可以自由修改)的设备文件,现在就可以对这个设备文件进行打开、读写、关闭等的操作了。

  面对层出不穷的新的USB设备,必须有人不断编写新的驱动程序以便让这些设备能够在linux下正常的工作,从这个意义上讲,驱动程序的编写本身就是一件非常有意义的工作,本文只是起到一个抛砖引玉的作用,帮助那些有志于写驱动程序的开发人员进一步了解USB驱动程序的设计思路,从而吸引更多的人加入到这个队伍中来。linux不仅为我们提供了一个顶级质量的操作系统,而且也为我们提供了参与到其未来开发过程的机会,我们完全可以从中得到无尽的快乐!

<think>嗯,用户想了解USB Bulk传输的工作流程和实现方式。这个问题涉及到USB协议中比较专业的部分,从引用内容看用户可能是个嵌入式开发者或驱动工程师。 用户提供的引用材料很有价值,特别是引用[3]详细说明Bulk传输的应用场景和驱动问题。不过用户明确要求的是工作流程和实现原理,我需要重点解释传输机制而不是外围问题。 USB Bulk传输的核心特点其实很清晰:它是一种可靠但无实时性保证的传输方式,适合大块数据。主机控制器会通过令牌包发起传输,设备根据端点地址响应。这里需要强调令牌包-数据包-握手包的三段式流程,以及NAK/STALL等流控机制。 实现层面要分硬件和软件两块说。硬件上USB控制器有专门的Bulk端点缓冲区,软件则需要处理描述符配置和传输请求。特别要注意高速/超高速模式下数据包大小从512字节提升到1024字节这个关键差异。 用户可能还会关心实际开发中的问题,比如如何避免缓冲区溢出。这个问题在引用[2]里提到过注意事项,可以结合着说说轮询间隔和最大包长的设置技巧。 最后考虑设计几个延伸问题:关于性能优化和应用场景的问题应该能帮助用户深化理解,特别是那个批量传输与等时传输的对比问题,对实际选型很有参考价值。</think>USB Bulk传输是USB协议中用于可靠大数据量传输的机制,其工作流程和实现方式如下: --- ### **一、Bulk传输核心特性** 1. **可靠性优先** - 通过CRC校验和自动重传机制保证数据完整性。 - 无固定带宽和延迟保证(与等时传输相反)。 2. **带宽动态分配** 当总线空闲时抢占剩余带宽,优先级低于控制/中断/等时传输[^1]。 3. **端点单向性** 每个端点仅支持单一方向(`Bulk IN`或`Bulk OUT`),需成对端点实现双向通信[^3]。 --- ### **二、工作流程详解** #### **1. 传输初始化(主机主导)** - 主机控制器发送**令牌包(Token Packet)**: 包含目标设备地址、端点号及传输方向(`IN`/`OUT`)。 - 示例:主机发起读取请求(`Bulk IN`) $$TokenPacket = [DeviceAddr|Endpoint|IN]$$ #### **2. 数据传输阶段** - **OUT传输(主机→设备)**: 主机发送**数据包(Data Packet)**,设备返回**握手包(ACK/NAK/STALL)**。 ```mermaid sequenceDiagram 主机->>设备: DATA Packet (含负载) 设备-->>主机: ACK (成功) / NAK (重试) / STALL (错误) ``` - **IN传输(设备→主机)**: 设备返回**数据包**,主机回复**握手包**。 ```mermaid sequenceDiagram 主机->>设备: IN Token 设备-->>主机: DATA Packet 主机-->>设备: ACK/NAK/STALL ``` #### **3. 错误处理机制** | 握手信号 | 含义 | 处理方式 | |----------|-----------------------|------------------------| | `ACK` | 传输成功 | 继续下一包 | | `NAK` | 设备暂无法响应 | 主机延迟后重试 | | `STALL` | 端点永久错误(如配置失败) | 需主机重置端点[^2] | --- ### **三、实现方式** #### **1. 硬件层** - **端点缓冲区(Endpoint Buffer)** 设备需为每个Bulk端点预留RAM缓冲区(大小由描述符定义)。 - **包大小限制** - 全速模式:最大包长 **64字节** - 高速模式:最大包长 **512字节** - 超高速模式:最大包长 **1024字节**[^3] #### **2. 软件层** - **设备固件** ```c // USB描述符配置示例(Bulk端点) const EndpointDescriptor bulk_ep_in = { .bLength = sizeof(EndpointDescriptor), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x81, // EP1 IN (bit7=IN方向) .bmAttributes = USB_TRANSFER_TYPE_BULK, .wMaxPacketSize = 512, // 高速模式包大小 .bInterval = 0 // Bulk无需轮询间隔 }; ``` - **主机驱动** - Windows:使用`WinUSB.sys`或`libusb`发起传输请求。 - Linux:通过`usb_bulk_msg()`或`libusb_bulk_transfer()` API操作。 #### **3. 传输优化** - **队列深度(Queue Depth)** 主机可发起多个未完成请求(高速模式建议队列深度≥4)。 - **流控策略** 设备在缓冲区满时返回`NAK`,主机通过指数退避算法降低重试频率。 --- ### **四、典型应用场景** 1. **大文件传输** USB Mass Storage(U盘)、网络适配器。 2. **打印设备** 打印机接收打印任务数据流。 3. **工业采集** 高速传感器数据上传(如摄像头原始帧)[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值