基于DWC2的USB驱动开发-抽丝剥茧再论SETUP完成标志DOEPINTn.SetUp

描述

本文转自公众号系列文章,欢迎关注
基于DWC2的USB驱动开发-USB包详解 (qq.com)

前言

https://www.elecfans.com/outside?redirect=https://mp.weixin.qq.com/s/gm5OAutfnYv6H_5Ce8GEqA一文中我们介绍了控制传输,中断相关寄存器,尤其是DOEPINTn.XferCompl,DOEPINTn.SetUp,DOEPINTn.StsPhseRcvd几个状态的组合非常重要,因为由他们可以确定当前控制传输处于什么阶段。前文我们对此进行了介绍,但是个人觉得讲的还不够透彻,原理还没有讲清楚,知其然知其所以然是我们一开始就强调的,所以针对这个几个标志位我们再次更详细的介绍,先来介绍最关键的DOEPINTn.SetUp。

DOEPINTn.SetUp

Setup是DOEPINTn寄存器中的一个标志,用于表示Setup阶段是否完成。

我们这里可能要问DIEPINTn为什么没有SetUp标志,因为Setup总是HOST->DEV的,所以总是OUT传输,所以总是对应OUT端点的,IN端点的寄存器不可能有。这里强调一下我们一定要多问为什么,先思考猜测,然后再从手册规格书中查找答案,只有这样才能加深理解,USB开发中这是很重要的,当然任何技术性的工作这一点都重要。

我们从寄存器的描述中也可以看到,该位表示SetUP阶段完成,并且只有控制OUT端点有。

还有个信息是本次控制传输没有连续的SETUP,意味着软件可以开始解析SETUP的8字节的内容以决定下一步要干嘛了。如果有连续的SETUP则是最后一个SETUP完成才能进行。

驱动开发

那么究竟SETUP完成对应的一个什么状态呢? 硬件是怎么知道SETUP阶段完成的呢? 主机和设备判断SETUP完成有什么区别吗? 我们会抛出一连串问题,如果能抛出这些问题说明你很适合做底层和驱动开发!

后面我们会抽丝剥茧,从原理到实践,从猜测到手册中求证,一层层剖析。

这里我们要回顾下控制传输,

我们从规格书中的拓扑图可以看到,控制传输的SETUP阶段过程如下

驱动开发

或者换个表达方式如下

驱动开发

或者从USB分析仪抓包数据来看,更形象。

SETUP阶段对应3个包:SETUP令牌包;DATA0的数据包,始终是HOST->DEV;设备的响应ACK。

驱动开发

这里顺便提一下,SETUP的数据始终使用DATA0;而设备要么正常接收并接受ACK,要么接收错误或者没接收到不响应,只有这两种情况,不能接收了不接受而NAK或者STALL等。

这里还顺便提一下接收和接受的概念区别。接收通常指的物理层数据正确接收到了,而接受则指的软件能够处理,或者说愿意处理。 接收了也可能不接受。

这里协议层面我们可以看到SETUP阶段完成的标志是,设备ACK响应了。

如下所示

驱动开发

仔细思考下,这里只是协议上的定义,也就是对应的总线上的数据,至于SETUP完不完成是要由参与者HOST和DEV去确定的,换句话说总线上的状态并不意味参与者就是这个状态了,因为参与者还要正确接收到总线上的数据才算。

对于主机接收到了设备发送的ACK那么主机认为SETUP阶段完成,如果设备发了ACK但是主机没收到呢?那么主机认为SETUP没完成,会认为本次失败,从头发SETUP包重来。

同样的对于设备来说将ACK发送到总线上去后,设备并不知道HOST接收没接收到,设备并不知道主机的状态。所以主机和设备对SETUP完成的状态确认存在不同步。

那怎么办呢? 设备并不知道主机认为SETUP完成了没有,因为设备并不知道主机收没收到ACK。就好比子非鱼安知鱼之乐,有点绕了。

其实也没什么办法,设备确实现在不知道主机是不是认为SETUP完成了,但是设备可以根据主机下一步的行动来确定。因为主机如果接收到了ACK,认为SETUP完成了那么主机下一步就会发IN或者OUT令牌进入数据阶段(或者状态阶段),否则则会重发SETUP重新进入SETUP阶段。

设备可以据此来进行判断,如果收到了主机的IN或者OUT令牌,则确认主机认为SETUP已经完成了,于是设备也知道SETUP完成了,这里设备和主机判断SETUP完成的时间点是不一样的,这就是协议定义和实际判定存在的区别。所以对于协议设计一定要考虑这种区别,协议设计了要考虑如何实现,也就一直强调的理论要结合实践,对于驱动编写更需要考虑从这些细节。

以上恭喜你从头开始推导出了设备判断SETUP完成的原理,那么回到上面的标志位DOEPINTn.SetUp即表示SETUP阶段完成,DWC2控制也是这么判断的吗?所谓英雄所见略同,DWC2控制器还真是这么判断的。

上面寄存器的描述中只提了该标志的含义,并没提其具体置位的原理和时机,实际编程指导手册中是有说明的。

如下位置,手册就进行了说明当,SETUP令牌之后,接收到了IN或OUT令牌则置位DOEPINTn.SetU,和我们推导的完全一致。

驱动开发

为什么是IN或OUT呢,因为控制传输可能是控制写,控制读,还可能无数据阶段的控制传输,所以后面要不就是IN数据或者OUT数据或者IN状态,如下所示

控制写

驱动开发

控制读

驱动开发

无数据阶段的控制传输

驱动开发

驱动编写注意事项

从上面可以看到设备是延迟才能确认SETUP完成的,也就是在SETUP后设备收到了主机发的IN和OUT之后才确认,中断也是在此时产生。

对于控制写,上面设备确认SETUP完成产生中断,中断服务函数开始去解析8字节的SETUP内容,来确定数据阶段要干嘛,后面数据阶段是IN还是OUT或者还是没有数据阶段直接状态阶段,其实是数据阶段(状态阶段)已经启动,设备延迟了。

此时中断服务函数还未执行,软件还没根据解析准备IN或者OUT描述符,所以此时对于这个IN或者OUT,硬件只能自动NAK,如下所描述:

驱动开发

所以这种情况下,中断服务函数中进行了解析,并知道要准备OUT描述进行接收时,设置EPEna=1时还要同时设置CNAK=1,清除硬件的自动NAK状态,让硬件根据OUT描述符去接收数据。

驱动开发

对于控制读后面是IN数据阶段,或者无数据阶段的控制传输后面是IN状态,也是类似的。

总结

以上对DOEPINTn.SetUp进行了详细的解析,因为它太重要了,SETUP阶段是控制传输的核心,因为只有SETUP完成才能去解析8字节的内容,才能决定后续干嘛,对于驱动编写接收到8字节的SETUP数据是第一个关键步。以上也可以看出一定要从原理上去理解,这也是驱动编写的重要原则,必须知其然知其所以然,精确的知道各个时间点的各个时间,以及其条件等,注意精确两个字。

审核编辑:汤梓红

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

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分