0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于DWC2的USB驱动开发-UVC的单元和终端类请求驱动代码优化

嵌入式USB开发 来源:嵌入式USB开发 作者:嵌入式USB开发 2023-07-14 09:44 次阅读

本文转自公众号,欢迎关注
基于DWC2的USB驱动开发-UVC的单元和终端类请求驱动代码优化 (qq.com)

前言

前面介绍了UVC的处理单元和相机终端,可以看到各单元和终端的请求形式都是一样的, 只是支持的CS和操作类型不一样,数据的大小不一样。目前的驱动是基于层层switch去解析的,这样代码冗余非常大,每一个接口的每一个单元和终端的每一个CS的每一个操作类型都要单独添加代码处理,而且这些处理应该位于类相关代码中,对用户不可见,用户只需要关心具体的值的获取与更新。而现在的处理方式做不到,现在用户必须去修改这部分代码才能获取和更新值,并且添加修改接口,终端和单元就需要修改代码。所以有必要对驱动进行优化,下面介绍优化细节。

https://mp.weixin.qq.com/s/4CTR1yjUmBsHqZPLEC7BhA

数据抽象

前面我们可以看到,单元和终端请求及其数据都是类似的,变化的是接口号,单元和终端号,CS,操作类型,值的大小,所以对这些进行抽象,抽象为数据结构,对应的变量即数据结构的成员变量。

首先对CS对应的值空间分配进行抽象

规格书P173中类相关的请求,对应如下,即对应不同的数据类型,其中SET只有一个,其他都是GET,暂时不考虑ALL的操作,对于ALL的操作预留回调接口到时单独实现,目前基本是不会用到ALL相关的操作的。

图片

那么我们需要如下的值空间,共7个值

CUR 当前值

MIN 最小值

MAX 最大值

RES 分辨率

LEN 数据长度

INFO 信息

DEF 默认值

最直接的分配空间的方法是以上7个类型各存一份,可连续放在一起。

但是不是所有的CS都支持这些值,所以为了减少空间占用,我们只分配支持的,不支持的不需要分配空间。并且我们还需要知道每个值的长度,

于是一个CS需要一片空间,这一片空间再分配给具体的某个类型,这样每个类型还需要一个长度信息,于是抽象出如下结构体

/**
 * struct usbd_uvc_utcs_t
 * UVC类的终端和单元对应CS的值,所有操作类型的值空间分配
 */
typedef struct 
{
    uint8_t  cs;                  /**< Control Selector Codes                            */
    uint8_t* buffer;              /**< 数据缓冲区                                         */
    uint8_t  len[7];              /**< [CUR MIN MAX RES LEN INFO DEF]的顺序,索引0对应CUR   */
} usbd_uvc_utcs_t;

Cs即为对应的CS

Buffer即分配给该CS下所有类型值的空间

Len[7]对应7个类型,每个类型的长度,长度为0表示该类型不支持。

那么怎么知道该类型在buffer中的偏移呢,可以通过前面所有len累计来计算,

比如MAX在CUR和MIN后面,其偏移就是len[0]+len[1],

并且还可以用GET_XXX的值是连续递增的特点来计算,比如GET_MAX=GET_CUR+2,

所以前面有2个值,所以计算偏移循环计算前面2个即可。

以上对小的颗粒CS的数据空间进行了抽象,再往上一层,一个单元和终端支持多个CS,但是不一定所有的都会支持,所以需要再将支持的CS进行组合

抽象出如下结构体

typedef void (*uvc_ut_set_pf)(void* data, uint32_t len); /**< SET回调函数 */
typedef void (*uvc_ut_get_pf)(void* data, uint32_t len); /**< GET回调函数 */

/**
 * struct usbd_uvc_utcr_t
 * UVC类的终端和单元控制请求结构体
 * Unit and Terminal Control Requests
 */
typedef struct usbd_uvc_utcr
{
    uint8_t itf;                /**< 位于的接口               */
    uint8_t id;                 /**< 终端和单元ID             */
    uint8_t csnum;              /**< 终端和单元的CS个数        */
    usbd_uvc_utcs_t* cs;        /**< 终端和单元CS对应的值空间  */
    uvc_ut_set_pf set_cb;       /**< SET回调                  */
    uvc_ut_get_pf get_cb;       /**< GET回调                  */ 
    struct usbd_uvc_utcr* next; /**< 指向下一个终端或单元       */
} usbd_uvc_utcr_t;

用户初始化

这样用户只需要分配上述空间,并注册回调函数,实现回调函数即可

usbd_uvc_utcr_t s_uvc_utcr2_t=
{
    0,                                                 /**< 接口0                     */
    2,                                                 /**< ID2                       */
    sizeof(s_uvc_utcs2_t)/sizeof(s_uvc_utcs2_t[0]),    /**< 总共19个属性               */
    .cs=s_uvc_utcs2_t,                                 /**< 终端和端元CS及其值          */
    .set_cb=0,                                         /**< set回调                    */
    .get_cb=0,                                         /**< get回调                    */
    .next=0,                                           /**< usbd_uvc_reg_utcr时自动链接 */
};

如下注册即可
usbd_uvc_reg_utcr(&s_uvc_utcr2_t);

对应s_uvc_utcs2_t一行对应一个CS,我们根据手册的描述来设置

以PU_BACKLIGHT_COMPENSATION_CONTROL为例

图片

该CS支持的类型有CUR,MIN,MAX,RES,INFO,DEF除了LEN都支持

对应长度分别时{2,2,2,2,0,1,2}总长为11,所以需要11字节缓冲区

uint8_t s_backlight_buffer_au8[11];

/** 处理单元
* len[CUR MIN MAX RES LEN INFO DEF]
*/
usbd_uvc_utcs_t s_uvc_utcs2_t[]=
{
{
.cs = PU_BACKLIGHT_COMPENSATION_CONTROL,
.buffer = s_backlight_buffer_au8,
{2,2,2,2,0,1,2},
},
}

以上就完成了初始化,缓冲区可以动态分配也可以静态分配。

注册过程

如下将每个终端和的单元的结构体通过单向链表链接起来,usb_uvc为类结构体全局变量,属于驱动部分这里不讲,后面会讲到。

/**

* @fn int usbd_uvc_reg_utcr(usbd_uvc_utcr_t* item)
* 注册类相关单元和终端控制请求
* @param[in] item ref usbd_uvc_utcr_t
* @retval 0:成功.
* @retval !=0:其他值失败.
  */
int usbd_uvc_reg_utcr(usbd_uvc_utcr_t* item)
  {
  if(item == (usbd_uvc_utcr_t*)0)
  {
  return -1;
  }
  if(usbd_uvc.utcr_list == (void*)0)
  {
  /* 当前链表为空,直接添加到头
  * 设置Next为空
    */
item- >next = (usbd_uvc_utcr_t*)0;
    usbd_uvc.utcr_list = item;
    }
    else
    {
    /* 当前链表不为空,插入到头
  * 设置Next为之前的头
    */
item- >next = (usbd_uvc_utcr_t*)(usbd_uvc.utcr_list);
    usbd_uvc.utcr_list = item;
    }
    return 0;
    }

处理过程

可以简单了很多,且支持任意的接口,终端单元,CS和类型的配置。

static void uvc_class_ut_req(dwc_handle *dwc, ureq_t setup)
{
/* 搜寻终端和处理单元号 */
usbd_uvc_utcr_t* p = 0;
usb_class_def* c_p;
int itf;
int id;
int cs;
int req;
int off = 0;
int len = 0;
int getsetlen = 0;
itf = setup- >wIndex & 0xFF;

/* 根据接口号查找接口类 */
c_p = dwc- >pclass_cb;
int getitf=0;
while(c_p != 0)
{
for(int i=0; i< 8; i++)
{
if(c_p- >itfs[i] == 0xFF)
{
break;
}
if(c_p- >itfs[i] == itf)
{
getitf = 1;
break;
}
}
if(getitf != 0)
{
p = c_p- >utcr_list;
break;
}
c_p= c_p- >next;
}
if(p == 0)
{
/* 没有找到接口对应的类 */
USBD_UVC_WARN(("unknow itf:%drn",itf));
return;
}

id =  (setup- >wIndex > > 8) & 0xFF;
cs = (setup- >wValue > > 8) & 0xFF;
req = setup- >bRequest;

#if 0
if((req & 0xF0) == 0x90)
{
/* GET_XXX_ALL*/
usb_ep0_set_stall(dwc); /* 暂时不支持 后面再实现 */
return;
}
else if((req & 0xF0) == 0x10)
{
/* SET_CUR_ALL */
usb_ep0_set_stall(dwc); /* 暂时不支持 后面再实现 */
return;
}
else
{
/* 其他的支持 */
}
#endif
while(p != (usbd_uvc_utcr_t*)0)
{
if((p- >itf == itf) && (p- >id == id))
{
for(int i=0; i< p- >csnum; i++)
{
if(cs == p- >cs[i].cs)
{
/* 如果长度为0说明不支持,直接STALL返回 */
len = p- >cs[i].len[(req&0x0F) - 1];
if(len == 0)
{
usb_ep0_set_stall(dwc);
USBD_UVC_WARN(("NOT SUPPORT ITF:%d ID:%d CS:%d REQ%drn",itf,id,cs,req));
return;
}
getsetlen = setup- >wLength > len ? len  : setup- >wLength;
/* 计算偏移值 */
off = 0;
for(int j=0; j< ((req&0x0F) - 1); j++)
{
off += p- >cs[i].len[j];
}
/* 找到CS
* INFO CUR MIN MAX RES DEF LEN
*/
switch(req)
{
case GET_INFO:
case GET_CUR:
case GET_MIN:
case GET_MAX:
case GET_RES:
case GET_DEF:
case GET_LEN:
if(p- >get_cb != 0)
{
p- >get_cb(0,0);
}
usb_ep0_write(dwc, p- >cs[i].buffer + off, getsetlen);
USBD_UVC_LOG(("ITF:%d ID:%d GET CS:%d REQ:%x OF:%d LEN:%drn",itf,id,cs,req,off,getsetlen));
break;
case SET_CUR:
if(p- >set_cb != 0)
{
p- >set_cb(0,0);
}
usb_ep0_read(dwc, p- >cs[i].buffer + off, getsetlen, 0);
USBD_UVC_LOG(("%d %d SET %d %d %d %drn",itf,id,cs,req,off,getsetlen));
break;
default:
usb_ep0_set_stall(dwc);
break;
}
return;
}
}
}
p = p- >next;
}
/* 未找到匹配项则STALL */
usb_ep0_set_stall(dwc);
}

对比之前的设计,

图片

总结

以上实现了终端和单元请求相关驱动的代码,实现可分层设计,可扩展具备可移植性。后面再考虑实现ALL相关的操作。
审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • usb
    usb
    +关注

    关注

    60

    文章

    7887

    浏览量

    263837
  • 程序
    +关注

    关注

    116

    文章

    3769

    浏览量

    80805
  • 代码
    +关注

    关注

    30

    文章

    4733

    浏览量

    68291
  • USB驱动
    +关注

    关注

    1

    文章

    136

    浏览量

    20163
  • 驱动开发
    +关注

    关注

    0

    文章

    130

    浏览量

    12059
  • uvc
    uvc
    +关注

    关注

    1

    文章

    126

    浏览量

    14497
  • DWC2
    +关注

    关注

    0

    文章

    35

    浏览量

    118
收藏 人收藏

    评论

    相关推荐

    基于DWC2USB驱动开发-0x01开篇介绍与新思DWC2 USB2.0控制器简介

    本文转自公众号,欢迎关注 基于DWC2USB驱动开发-0x01开篇介绍与新思DWC2 USB2
    的头像 发表于 05-08 18:10 4479次阅读
    基于<b class='flag-5'>DWC2</b>的<b class='flag-5'>USB</b><b class='flag-5'>驱动</b><b class='flag-5'>开发</b>-0x01开篇介绍与新思<b class='flag-5'>DWC2</b> <b class='flag-5'>USB</b>2.0控制器简介

    基于DWC2USB驱动开发-0x02 DWC2 USB2.0 IP功能特征介绍

    DWC2即新思(Synopsys )的DesignWare® Cores USB 2.0 HiSpeed On-The-Go (OTG)控制器IP,被大量使用。从linux的内核源码驱动中就带
    的头像 发表于 05-09 10:09 9000次阅读
    基于<b class='flag-5'>DWC2</b>的<b class='flag-5'>USB</b><b class='flag-5'>驱动</b><b class='flag-5'>开发</b>-0x02 <b class='flag-5'>DWC2</b> <b class='flag-5'>USB</b>2.0 IP功能特征介绍

    基于DWC2USB驱动开发-IAD描述符详解

    本文转自公众号,欢迎关注 基于DWC2USB驱动开发-IAD描述符详解 (qq.com) 一.  前言 IAD描述符用于一个设备功能关联多个接口,可以用于实现组合设备。 二.参考文档
    的头像 发表于 06-27 08:45 5.2w次阅读
    基于<b class='flag-5'>DWC2</b>的<b class='flag-5'>USB</b><b class='flag-5'>驱动</b><b class='flag-5'>开发</b>-IAD描述符详解

    基于DWC2USB驱动开发-USB复位详解

    本文转自公众号欢迎关注 基于DWC2USB驱动开发-USB复位详解 (qq.com) 一.前言          上一篇我们详细介绍了
    的头像 发表于 07-07 11:18 5.6w次阅读
    基于<b class='flag-5'>DWC2</b>的<b class='flag-5'>USB</b><b class='flag-5'>驱动</b><b class='flag-5'>开发</b>-<b class='flag-5'>USB</b>复位详解

    基于DWC2USB驱动开发-USB连接详解

    本文转自公众号,欢迎关注 基于DWC2USB驱动开发-USB连接详解 (qq.com) 一.前言   之前一直在阅读手册,规格书,练习招式
    的头像 发表于 07-07 08:46 3544次阅读
    基于<b class='flag-5'>DWC2</b>的<b class='flag-5'>USB</b><b class='flag-5'>驱动</b><b class='flag-5'>开发</b>-<b class='flag-5'>USB</b>连接详解

    基于DWC2USB驱动开发-高速设备枚举为全速设备问题案例分析

    本文转自公众号,欢迎关注 基于DWC2USB驱动开发-高速设备枚举为全速设备问题案例分析 (qq.com) 一.前言   本文分享一个高速设备被枚举为全速的问题。     高速设备速
    的头像 发表于 07-10 17:12 1300次阅读
    基于<b class='flag-5'>DWC2</b>的<b class='flag-5'>USB</b><b class='flag-5'>驱动</b><b class='flag-5'>开发</b>-高速设备枚举为全速设备问题案例分析

    基于DWC2USB驱动开发-UVC的处理单元详解

    本篇来详细分析下UVC的处理单元相关的内容,同样的我们理论结合实践来进行。
    的头像 发表于 07-13 09:42 2043次阅读
    基于<b class='flag-5'>DWC2</b>的<b class='flag-5'>USB</b><b class='flag-5'>驱动</b><b class='flag-5'>开发</b>-<b class='flag-5'>UVC</b>的处理<b class='flag-5'>单元</b>详解

    基于DWC2USB驱动开发-UVC的相机终端详解

    本篇来详细分析下UVC的相机终端相关的内容,同样的我们理论结合实践来进行。
    的头像 发表于 07-13 09:46 1968次阅读
    基于<b class='flag-5'>DWC2</b>的<b class='flag-5'>USB</b><b class='flag-5'>驱动</b><b class='flag-5'>开发</b>-<b class='flag-5'>UVC</b>的相机<b class='flag-5'>终端</b>详解

    基于DWC2USB驱动开发-设备驱动框架

    本文转自公众号,欢迎关注 基于DWC2USB驱动开发-设备驱动框架 (qq.com) 一.前
    的头像 发表于 07-16 15:56 1269次阅读
    基于<b class='flag-5'>DWC2</b>的<b class='flag-5'>USB</b><b class='flag-5'>驱动</b><b class='flag-5'>开发</b>-设备<b class='flag-5'>类</b><b class='flag-5'>驱动</b>框架

    基于DWC2USB驱动开发-发送相关的寄存器DMA寄存器详解

    本文转自公众号,欢迎关注 基于DWC2USB驱动开发-发送相关的寄存器DMA寄存器详解 (qq.com) 前言 如下寄存器DIEPxxx,对应IN端点,和发送数据相关,这一篇先介绍和
    的头像 发表于 07-16 16:42 1566次阅读
    基于<b class='flag-5'>DWC2</b>的<b class='flag-5'>USB</b><b class='flag-5'>驱动</b><b class='flag-5'>开发</b>-发送相关的寄存器DMA寄存器详解

    基于DWC2USB驱动开发-USB包详解

    不管什么通讯协议,比如UART,SPI,USB等等,不管是并口还是串口,不管是同步还是异步,我们从抽象的角度去看,其本质都是一样的。都是先定义物理信号,物理信号可能是差分,单端,电流驱动电压驱动等等
    的头像 发表于 07-23 17:11 2439次阅读
    基于<b class='flag-5'>DWC2</b>的<b class='flag-5'>USB</b><b class='flag-5'>驱动</b><b class='flag-5'>开发</b>-<b class='flag-5'>USB</b>包详解

    基于DWC2USB驱动开发-控制传输中断相关寄存器

    本篇讲解Scatter/Gather DMA模式下控制传输相关的寄存器。控制传输是USB驱动的核心部分,控制传输调通了驱动就完成了一大半,而驱动的核心又是中断的处理。
    的头像 发表于 07-24 00:07 2325次阅读
    基于<b class='flag-5'>DWC2</b>的<b class='flag-5'>USB</b><b class='flag-5'>驱动</b><b class='flag-5'>开发</b>-控制传输中断相关寄存器

    基于DWC2USB驱动开发-数据不能发送问题分析案例

    本文转自公众号欢迎关注 基于DWC2USB驱动开发-数据不能发送问题分析案例 (qq.com)   一.前言        对于驱动
    的头像 发表于 08-08 09:43 2097次阅读
    基于<b class='flag-5'>DWC2</b>的<b class='flag-5'>USB</b><b class='flag-5'>驱动</b><b class='flag-5'>开发</b>-数据不能发送问题分析案例

    如何对基于hal库的DWC2 USB IP进行调试呢

    背景之前适配 DWC2 USB IP 的时候,主要是基于 st 的 hal 库来走的,当时我就对他们的 hal 库代码不满,只是无奈,迫于时间就没重构,果不其然,usb bug 一堆,
    发表于 06-14 15:23

    基于DWC2USB驱动开发-抽丝剥茧再论切换到状态阶段标志DOEPINTn.StsPhseRcvd

    本文转自公众号系列文章,欢迎关注 基于DWC2USB驱动开发-USB包详解 (qq.com) 一.前言 前面我们对SETUP完成标志DOE
    的头像 发表于 07-24 18:04 1429次阅读
    基于<b class='flag-5'>DWC2</b>的<b class='flag-5'>USB</b><b class='flag-5'>驱动</b><b class='flag-5'>开发</b>-抽丝剥茧再论切换到状态阶段标志DOEPINTn.StsPhseRcvd