前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux USB 设备驱动模型

Linux USB 设备驱动模型

作者头像
Jasonangel
发布2023-08-22 13:56:04
5620
发布2023-08-22 13:56:04
举报
文章被收录于专栏:嵌入式Linux系统开发
代码语言:javascript
复制
Linux 内核源码:include\linux\usb.h
Linux 内核源码:drivers\hid\usbhid\usbmouse.c

1. BUS/DEV/DRV 模型

"USB 接口"是逻辑上的 USB 设备,编写的 usb_driver 驱动程序,支持的是"USB 接口":

  • USB 控制器或 Hub 识别出 USB 设备后,会创建、注册 usb_device
  • usb_device 被"drivers\usb\core\generic.c" 驱动认领后,会选择、设置某个配置
  • 这个配置下面的接口,都会分配、设置、注册一个 usb_interface
  • 左边的 usb_driver 和右边的 usb_interface 如果匹配,则调用 usb_driver.probe

2. 接口函数

在 USB 设备驱动程序中,能使用的 USB 函数都在这个头文件里:include\linux\usb.h

2.1 pipe

使用这些接口函数的主要目的是传输数据,传输数据的对象是 USB 设备里的某个 endpoint,这被称为 pipe:

代码语言:javascript
复制
/* Create various pipes... */
#define usb_sndctrlpipe(dev, endpoint) \
 ((PIPE_CONTROL << 30) | __create_pipe(dev, endpoint))
#define usb_rcvctrlpipe(dev, endpoint) \
 ((PIPE_CONTROL << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
#define usb_sndisocpipe(dev, endpoint) \
 ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev, endpoint))
#define usb_rcvisocpipe(dev, endpoint) \
 ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
#define usb_sndbulkpipe(dev, endpoint) \
 ((PIPE_BULK << 30) | __create_pipe(dev, endpoint))
#define usb_rcvbulkpipe(dev, endpoint) \
 ((PIPE_BULK << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
#define usb_sndintpipe(dev, endpoint) \
 ((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint))
#define usb_rcvintpipe(dev, endpoint) \
 ((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)

2.2 同步传输函数

对于控制传输、批量传输、中断传输,有 3 个同步函数可以用来直接发起传输。这些函数内部会创建、填充、提交一个 URB("usb request block"),并等待它完成或超时。

函数原型如下:

代码语言:javascript
复制
int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
      __u8 requesttype, __u16 value, __u16 index, void *data,
      __u16 size, int timeout);

int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
   void *data, int len, int *actual_length, int timeout);

int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe,
        void *data, int len, int *actual_length, int timeout);

2.3 异步传输函数

使用 URB 进行传输时,它是异步方式:需要先分配、构造、提交一个 URB("usb request block"),当传输完成后,它的回调函数被调用。

关键就在于需要填充 URB:

  • dev:跟谁传输数据
  • pipe:跟哪个 pipe 传输数据
  • buffer:里面存有要发送的数据,或者用来接收要读取的数据
  • 数据长度
  • 回调函数
2.3.1 分配和释放 URB

函数原型如下:

代码语言:javascript
复制
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags);

void usb_free_urb(struct urb *urb);
2.3.2 分配/释放 DMA Buffer

发起 USB 传输时,数据保存在 buffer 里。这个 buffer 可以是一般的 buffer,也可以是 DMA Buffer。

对于一般的 buffer,在提交 URB 时会临时分配一个 DMA Buffer:

  • 发送数据时:函数内部会先从一般 buffer 中把数据复制到 DMA Buffer,在提交给 USB 控制器
  • 读取数据时:USB 控制器先把数据传到 DMA Buffer,函数内部在把 DMA Buffer 的数据复制到一般 buffer
  • 中间增加了一次数据的拷贝,效率低

我们可以直接使用 DMA Buffer,函数原型如下:

代码语言:javascript
复制
void *usb_alloc_coherent(struct usb_device *dev, size_t size, gfp_t mem_flags,dma_addr_t *dma);

void usb_free_coherent(struct usb_device *dev, size_t size, void *addr,dma_addr_t dma);
2.3.3 填充 URB

对于控制传输、批量传输、中断传输,分别有如下函数:

代码语言:javascript
复制
static inline 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_fn,
     void *context);

static inline 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_fn,
         void *context);

static inline void usb_fill_int_urb(struct urb *urb,
        struct usb_device *dev,
        unsigned int pipe,
        void *transfer_buffer,
        int buffer_length,
        usb_complete_t complete_fn,
        void *context,
        int interval);

如果 URB 使用 DMA Buffer,那么还需要设置一个 flag 表明这点:

代码语言:javascript
复制
urb->transfer_dma = DMA address of buffer; // usb_alloc_coherent的输出参数
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
2.3.4 提交 URB

构造好 URB 后,需要提交到 USB 系统里,才能启动传输。

代码语言:javascript
复制
int usb_submit_urb(struct urb *urb, gfp_t mem_flags);
2.3.5 取消 URB

已经提交的 URB,可以取消它,有 2 个函数:

  • usb_kill_urb:这是一个同步函数,它会等待 URB 结束
  • usb_unlink_urb:这是一个异步函数,它不会等待 URB 结束,USB 控制器驱动会调用它的回调函数
代码语言:javascript
复制
void usb_kill_urb(struct urb *urb);

int usb_unlink_urb(struct urb *urb);
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-06-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 嵌入式Linux系统开发 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. BUS/DEV/DRV 模型
  • 2. 接口函数
    • 2.1 pipe
      • 2.2 同步传输函数
        • 2.3 异步传输函数
          • 2.3.1 分配和释放 URB
          • 2.3.2 分配/释放 DMA Buffer
          • 2.3.3 填充 URB
          • 2.3.4 提交 URB
          • 2.3.5 取消 URB
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档