SylixOS USB Gadget层介绍

1. Gadget层介绍

因为SylixOS中没有Platform的概念,因此在笔者眼中Gadget层兼顾了Platform的功能,实现了UDC设备层和驱动层的链接。缓存

Gadget层结构以及函数调用过于复杂,没法对每一个函数作一一介绍,在此会抽调一些重要的函数以及结构体简单介绍。具体绑定传输流程还需读者花时间精力慢慢理顺。网络

1.1      上层函数操做集

如图 1‑1所示,在上层函数操做集中包含了4个重要的结构,这些结构体中包含大量的回调函数来获取底层信息,创建链接(此层的主要代码在composite.c文件中)。这些函数中如下几个尤为重要:数据结构

1.      composite_bind函数

Bind函数主要完成UDC驱动层Gadget设备和设备层composite设备关系的创建,分配控制端点0的请求数据结构,设置设备描述符,并调用功能层的bind函数分配功能层所须要的资源等工做。框架

2.      composite_unbind函数

Unbind函数完成bind函数中分配的资源,并遍历全部配置和各个配置下的功能,调用其对应的unbind函数来释放资源。函数

3.      usb_add_function函数

add_function在配置中增长一个功能,每一个配置初始化后都必须至少有一个功能。这个函数会在多个地方调用,使用需特别注意。spa

4.      composite_setup函数

setup函数完成了ep0所须要处理的而底层不能处理的功能,因为UDC层实现了这个函数,因此其余层就不须要关注于USB协议的这些事务性处理,而重点放在须要实现的功能上。orm

                       

图 1‑1 Gadget层需获取的函数集合接口

1.2      底层函数操做集

如图 1‑1所示,在底层操做函数操做集中大部分都是提供给上层的回调函数接口。其中有两个结构体,几乎贯穿整个USB虚拟网卡,详细介绍以下:队列

1.      usb_request结构体

struct usb_request {事务

void *buf;                                  //数据缓存区

unsigned length;                          //数据长度

dma_addr_t dma;                        //与buf关联的DMA地址,DMA传输时使用

unsigned no_interrupt:1;              //当为true时,表示没有完成函数,则经过中断通知传输完成,这个由DMA控制器直接控制

unsigned zero:1;                          //当输出的最后的数据包不够长度是是否填充0

unsigned short_not_ok:1;             //当接收的数据不够指定长度时,是否报错

void (*complete)(struct usb_ep *ep, struct usb_request *req);//请求完成函数

void *context;                             //被completion回调函数使用

struct list_head list;                      //被Gadget Driver使用,插入队列

int status;                                    //返回完成结果,0表示成功

unsigned actual;                          //实际传输的数据长度

};

 

在USB传输的过程当中全部的消息最终都是填充在一个buf中来进行传输的,USB为了更好的管理传输的消息定义了一个usb_request结构体来描述一帧消息的信息。

2.      usb_ep_ops

struct usb_ep_ops {

int (*enable) (struct usb_ep *ep,  const struct usb_endpoint_descriptor *desc);

int (*disable) (struct usb_ep *ep);

struct usb_request *(*alloc_request) (struct usb_ep *ep, gfp_t gfp_flags);

void (*free_request) (struct usb_ep *ep, struct usb_request *req);

int (*queue) (struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags);

int (*dequeue) (struct usb_ep *ep, struct usb_request *req);

int (*set_halt) (struct usb_ep *ep, int value);

int (*set_wedge) (struct usb_ep *ep);

int (*fifo_status) (struct usb_ep *ep);

void (*fifo_flush) (struct usb_ep *ep);

};

 

结构体中有两个重要的回调函数,一个是alloc_request函数,另外一个是queue函数。

在usb虚拟网卡中全部的消息都以队列的形式来进行传输的。调用alloc_request函数申请一个传输请求大小的空间,这个大小的值可根据实际传输时所需的最大空间进行配置。在每次消息传出以前都要提早调用alloc_request函数来申请内存。在消息接收完毕或者填充完毕以后都须要调用queue函数把须要传输的消息加入到消息队列中,进行传输。

1.3      注意事项

USB虚拟网卡移植的过程当中,主要参考的是Linux代码,SylixOS和Linux有许多区别,好比SylixOS中没有dev的概念,也没有platform的概念。须要改许多的代码框架。在SylixOS中使用的是LWIP网络协议栈,而Linux中使用的是TCP/IP协议栈。在数据传输的过程当中LWIP使用的是pbuf来封装数据,而Linux使用的是skbbuf,二者的使用机制彻底不同。相似的结构体不一样还有不少,在这不一一说明。

移植USB驱动时需特别注意虚拟地址和物理地址相互转换以及cache同步问题。USB传输的数据量会很大,所以会使用DMA。DMA使用的是物理地址,而CPU却只能识别虚拟地址。在物理地址和虚拟地址转换过程当中颇有可能会出现物理地址未映射成虚拟地址的状况。就算有作物理地址和虚拟地址的转换也须要注意cache刷新时刷新的字节大小问题。若是须要刷新内存的大小和cache刷新大小字节不对齐,会出现cache刷新失败问题。这个问题十分严重,会直接致使读出的数据是脏数据,系统直接跑飞,需特别注意。按照常理来讲使用cache的数据处理速度应该远大于未使用cache,可是在SylixOS的实测中,有无cache操做速度差距不大,甚至不使用cache速度会更快。目前的USB虚拟网卡使用的是无cache版本的操做。

实际使用过程当中,须要根据使用USB协议的版本号和实际速度,来配置初始化接收缓冲区个数。若是速度快而缓冲区个数过少,会出现严重的丢包现象,影响USB速率。

因为USB驱动中代码回调过于复杂,本文也只是简单提炼了一下框架和重点,若是想要彻底弄清还需耐下心花时间从头至尾循序渐进追一下数据传输流程的代码才行。因为做者水平有限可能会出现理解错误或者描述不到位的状况,请你们谅解,也请你们及时指正,谢谢。

相关文章
相关标签/搜索