URB概念

URB定义:

usb 请求块(usb request block, urb)是usb设备驱动中用来描述与usb设备通信所用的基本载体和核心数据结构,非常类似于网络设备驱动中的sk_buff结构体,是usb主机与设备通信的电波。

  1. struct urb {  
  2.     /* private: usb core and host controller only fields in the urb */  
  3.     struct kref kref;        /* URB引用计数 */  
  4.     void *hcpriv;            /* host控制器的私有数据 */  
  5.     atomic_t use_count;        /* 当前提交计数 */  
  6.     atomic_t reject;        /* 提交失败计数 */  
  7.     int unlinked;            /* 连接失败代码 */  
  8.     /* public: documented fields in the urb that can be used by drivers */  
  9.     struct list_head urb_list;    /* list head for use by the urb's 
  10.                      * current owner */  
  11.     struct list_head anchor_list;    /* the URB may be anchored */  
  12.     struct usb_anchor *anchor;  
  13.     struct usb_device *dev;     /* 指向这个 urb 要发送的目标 struct usb_device 的指针,这个变量必须在这个 urb 被发送到 USB 核心之前被 USB 驱动初始化.*/  
  14.     struct usb_host_endpoint *ep;    /* (internal) pointer to endpoint */  
  15.     unsigned int pipe;        
  16.     int status;           
  17.     unsigned int transfer_flags;    /* 传输设置*/  
  18.     void *transfer_buffer;        /* 指向用于发送数据到设备(OUT urb)或者从设备接收数据(IN urb)的缓冲区指针。为了主机控制器驱动正确访问这个缓冲, 它必须使用 kmalloc 调用来创建, 不是在堆栈或者静态内存中。 对控制端点, 这个缓冲区用于数据中转*/  
  19.     dma_addr_t transfer_dma;    /* 用于以 DMA 方式传送数据到 USB 设备的缓冲区*/  
  20.     int transfer_buffer_length;    /* transfer_buffer 或者 transfer_dma 变量指向的缓冲区大小。如果这是 0, 传送缓冲没有被 USB 核心所使用。对于一个 OUT 端点, 如果这个端点大小比这个变量指定的值小, 对这个 USB 设备的传输将被分成更小的块,以正确地传送数据。这种大的传送以连续的 USB 帧进行。在一个 urb 中提交一个大块数据, 并且使 USB 主机控制器去划分为更小的块, 比以连续地顺序发送小缓冲的速度快得多*/  
  21.     int actual_length;        /* 当这个 urb 完成后, 该变量被设置为这个 urb (对于 OUT urb)发送或(对于 IN urb)接受数据的真实长度.对于 IN urb, 必须是用此变量而非 transfer_buffer_length , 因为接收的数据可能比整个缓冲小*/  
  22.     unsigned char *setup_packet;    /* 指向控制urb的设置数据包指针.它在传送缓冲中的数据之前被传送(用于控制 urb)*/  
  23.     dma_addr_t setup_dma;        /* 控制 urb 用于设置数据包的 DMA 缓冲区地址,它在传送普通缓冲区中的数据之前被传送(用于控制 urb)*/  
  24.     int start_frame;        /* 设置或返回初始的帧数量(用于等时urb) */  
  25.     int number_of_packets;          
  26.     int interval;            
  27.     int error_count;        /* 等时urb的错误计数,由USB核心设置 */  
  28.     void *context;            /* 指向一个可以被USB驱动模块设置的数据块. 当 urb 被返回到驱动时,可在结束处理例程中使用. */  
  29.     usb_complete_t complete;    /* 结束处理例程函数指针, 当 urb 被完全传送或发生错误,它将被 USB 核心调用. 此函数检查这个 urb, 并决定释放它或重新提交给另一个传输中*/  
  30.     struct usb_iso_packet_descriptor iso_frame_desc[0];  
  31.     };     



URB 处理流程

1, usb设备驱动程序创建初始化一个访问特定usb设备特定端点的urb,并提交给usb core

2, usb core提交该urb到usb主控制器驱动程序

3, usb主控制器驱动程序根据urb描述的信息,来访问usb设

4,当设备访问结束后,usb主控制器驱动程序通知usb core(调用这个函数usb_complete_t complete;)然后其再通知usb设备驱动程序


创建URB

struct urb *usb_alloc_urb(int iso_packets, int mem_flags) 参数:

iso_packets: urb 所包含的等时数据包的个数,若不是等时传输,则为0.

mem_flags: 内存分配标识(如GFP_KERNEL),参考kmalloc

不同的传递方式有不同的URB,如:controll, out, in;

初始化URB

对于中断urb,使用usb_fill_int_urb函数来初始化:

static inline void usb_fill_int_urb (struct urb *urb,要初始化的urb指针。
                     struct usb_device *dev,所要访问的设备
                     unsigned int      pipe,要访问的端点所对应的管道,使用usb_sndintpipe()或usb_rcvintpipe()创建
                     void              *transfer_buffer,要传输的数据缓冲区
                     int               buffer_length,缓冲区长度
                     usb_complete_t    complete_fn,当完成该urb所请求的操作时,要调用的回调函数
                     void              *context,complet_fn函数所需的上下文,通常取值为dev
                     int               interval)urb被调度的时间间隔

管道:驱动程序的数据缓冲区与一个端点的连接,它代表了一种在两者之间移动数据的能力

对于批量urb,使用usb_fill_bulk_urb函数来初始化。

对于控制urb,使用usb_fill_control_urb函数来初始化。

等时urb没有像中断,控制和批量urb那样的初始化函数,我们只能手动的初始化urb

提交URB

在完成urb的创建和初始化后,urb便可以通过usb_submit_urb函数来提交给usb核心:

int usb_submit_urb(struct urb *urb,gfp_t mem_flags)

参数:

urb:指向urb的指针;

mem_flags:内存分配标识,它用于告知usb核心如何分配内存缓冲区。

处理URB:

URB被提交到USB核心后,usb核心指定usb主控制器驱动程序来处理该urb,在3种情况下,urb会被认为处理完成:

1 urb被成功发送给设备,并且设备返回成功确认。如果urb->status为0,意味着对于一个输出urb,数据被成功发送;对于一个输入urb,请求的数据被成功收到。

2如果发送数据到设备或从设备接受数据时发生了错误,urb->status将记录错误值。

3 urb被“取消”,这发生在驱动通过usb_unlink_urb()或usb_kill_urb()函数取消urb,或urb虽已提交,而usb设备被拔出的情况下

当urb处理完成后,urb完成函数将被调用。

转载:http://blog.youkuaiyun.com/lidaqiang99/article/details/6591024  
<think>首先,用户的问题是:“基于Linux4.19 usb存储驱动实现URB缓冲机制”。我需要用中文回答这个问题。 关键点是:基于Linux内核版本4.19的USB存储驱动,实现URB缓冲机制。URB是USB Request Block,用于USB通信中的数据传输。 我的回答应该包括: - 解释URB缓冲机制的基本概念。 - 描述在Linux USB存储驱动中如何实现URB缓冲。 - 提供代码示例或伪代码来展示实现。 - 最后,必须输出“§§相关问题§§”,然后列出3-5个相关问题。 回答结构: 1. 先回答问题本身。 2. 在末尾添加“§§相关问题§§”,然后列出问题。 确保回答完整,不换行分散。 现在,回忆Linux USB驱动相关知识: - 在Linux USB子系统中,URB用于管理USB请求。 - 对于USB存储设备,驱动如usb-storage模块处理块设备操作。 - URB缓冲机制可能涉及批量传输中的缓冲管理,以提高性能或处理数据流。 实现URB缓冲机制的关键步骤: - 分配URB:使用usb_alloc_urb()函数分配URB。 - 填充URB:设置URB的端点、缓冲区、完成回调等。 - 提交URB:使用usb_submit_urb()提交到USB核心。 - 缓冲管理:使用内存池(如kmalloc或dma_alloc_coherent)分配缓冲区。 - 完成处理:在回调函数中处理完成事件,释放或重用缓冲区。 在usb-storage驱动中,具体实现可能涉及scsi命令的传输。 伪代码示例: ```c // 示例代码,展示URB缓冲机制 static void urb_completion(struct urb *urb) { // 处理URB完成事件 if (urb->status == 0) { // 数据传输成功 // 处理数据 } else { // 错误处理 } // 释放或重用URB和缓冲区 usb_free_urb(urb); kfree(urb->transfer_buffer); } static int submit_urb(struct usb_device *udev, struct usb_endpoint_descriptor *ep, void *data, size_t len) { struct urb *urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) return -ENOMEM; void *buffer = kmalloc(len, GFP_KERNEL); if (!buffer) { usb_free_urb(urb); return -ENOMEM; } memcpy(buffer, data, len); // 如果需要发送数据 usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, ep->bEndpointAddress), buffer, len, urb_completion, NULL); int ret = usb_submit_urb(urb, GFP_KERNEL); if (ret) { kfree(buffer); usb_free_urb(urb); } return ret; } ``` 在回答中,我需要用中文解释。 最后,提出3-5个相关问题: 1. URB缓冲机制在USB驱动中的作用是什么? 2. Linux内核中如何管理URB的内存分配? 3. 在USB存储驱动中,如何处理URB提交失败的情况? 4. URB完成回调函数中需要注意哪些同步问题? 5. 如何优化URB缓冲机制以提高USB存储设备的性能? 确保输出格式正确: - 回答结束后,直接输出“§§相关问题§§” - 然后列出问题,用1. 2. 3. 等编号。 完整回答结构: - 开头解释问题。 - 提供代码示例。 - 输出“§§相关问题§§” - 列出问题。</think>在Linux 4.19内核中,USB存储驱动(如`drivers/usb/storage`模块)实现URB(USB Request Block)缓冲机制,主要用于优化批量数据传输的性能。URB是USB通信的核心数据结构,用于封装USB请求(如读/写操作)。缓冲机制通过预分配和复用URB及关联的数据缓冲区,减少内存分配开销和延迟,提高吞吐量。以下是关键实现步骤: 1. **URB分配与初始化**:使用`usb_alloc_urb()`动态分配URB,并通过`usb_fill_bulk_urb()`填充URB参数(如端点地址、传输方向、缓冲区指针和完成回调函数)。 2. **缓冲区管理**:使用`kmalloc()`或`dma_alloc_coherent()`分配数据缓冲区(对齐到DMA边界),并关联到URB的`transfer_buffer`字段。驱动通常维护一个URB池(如链表或数组)以复用URB。 3. **URB提交与完成处理**:通过`usb_submit_urb()`提交URB到USB核心。在完成回调函数中,检查`urb->status`处理结果(如成功、错误或取消),并释放或回收URB和缓冲区。 4. **错误处理与重试**:包括超时机制(使用定时器)和urb状态检查(如`-EPIPE`错误时重置端点)。 示例代码片段(基于Linux 4.19的简化伪代码): ```c #include <linux/usb.h> #include <linux/slab.h> // URB完成回调函数 static void urb_completion(struct urb *urb) { struct us_data *us = urb->context; // 假设us是驱动私有数据结构 if (urb->status) { // 错误处理:记录日志或重试 printk(KERN_ERR "URB error: %d\n", urb->status); } else { // 成功:处理接收到的数据(如复制到SCSI层) memcpy(us->sg_buffer, urb->transfer_buffer, urb->actual_length); } // 回收URB到缓冲池(例如添加到空闲链表) list_add(&urb->urb_list, &us->free_urb_list); } // 提交URB函数 int submit_bulk_urb(struct us_data *us, void *data, int len, int dir) { struct urb *urb; void *buf; // 从缓冲池获取空闲URB if (list_empty(&us->free_urb_list)) { urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) return -ENOMEM; } else { urb = list_first_entry(&us->free_urb_list, struct urb, urb_list); list_del(&urb->urb_list); } // 分配或复用缓冲区 buf = kmalloc(len, GFP_KERNEL); if (!buf) { usb_free_urb(urb); return -ENOMEM; } if (dir == USB_DIR_IN) // 读取操作 usb_fill_bulk_urb(urb, us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in), buf, len, urb_completion, us); else // 写入操作 usb_fill_bulk_urb(urb, us->pusb_dev, usb_sndbulkpipe(us->pusb_dev, us->ep_out), buf, len, urb_completion, us); // 提交URB int ret = usb_submit_urb(urb, GFP_KERNEL); if (ret) { kfree(buf); usb_free_urb(urb); // 错误时释放资源 } return ret; } ``` 在该实现中,缓冲池通过链表管理,减少频繁分配开销。注意:实际代码需处理同步(如自旋锁)和DMA映射(`usb_alloc_coherent()`)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值