标准仪器设备接口是上位机和仪器通信使用的接口,如示波器、电源、信号发生器等。常用的标准仪器接口及对应的通信协议如下。
通信接口 | 通信协议 |
---|---|
LAN | VXI-11 |
USB | USB-TMC |
GPIB | IEEE488 |
LAN 的通信协议和 TCP/IP 的 TCP Client/Server
、RAW Socket
没什么本质区别,只要支持其中之一都可以正常识别。
GPIB 的通信协议比较复杂,而且物理层是并口传输,几乎只能使用专用的收发器芯片进行通信,专用收发器的控制方式类似 SRAM ,也并没有什么很难解决的问题。
USB 的通信方式就必须品鉴一下了。
而我司作为一个测试设备和仪器公司,居然在这方面没有任何技术积累,那就让我来细品一下什么是 USB-TMC 枚举吧。
首先非常重要的是翻阅标准文档,有以下两份我自己机翻的双语对照版,需要双页视图对照查看。
USBTMC_1_00_EnCh.pdf | yono的文件
USBTMC_usb488_subclass_1_00_EnCh.pdf | yono的文件
其次非常不重要的是找一个友商的正规仪器抓数据包看看是啥情况。
ps:实际上我并没有看过第二份文档哪怕一眼,完全依靠抓数据和第一个文档互相映照就完成了驱动开发。
值得关注的是 USB-TMC 类枚举实际上只有 USB488 这一个子类,而且仍有许多的预留数据位置和通信过程,所以我认为 TMC 这个枚举仍然可以使用很多年。
众所周知,驱动程序在 USB 设备的枚举阶段需要询问描述符,这个描述符的编写是怎么枚举成 TMC 设备的关键。
经过抓包,一个示例的设备描述符就出来了,和他一模一样就好了,有如下。
其中最为关键的是 Interface descriptor
部分中的 bInterfaceClass
和 bInterfaceSubClass
这两个部分直接决定了我们的枚举类型。当然整个描述符中的其他标有 (static)
的部分都是必须与示例一模一样的,是标准的规定。有些是 TMC 标准,有些是 USB 标准,反正标有 (static)
的部分都一模一样就好了。
最为特殊的是 ,USB-TMC 的协议没有高速全速之分,无论工作在什么 USB 线路上,都需要设备限定符,这是和其他现代化的 USB 协议栈所不同的地方。
次为特殊的是,USB-TMC 设备不能使用组合枚举,会破坏 NI-VISA 对 USB-TMC 的识别和资源调用。例如我尝试组合枚举成 TMC + 串口 设备,设备管理器中都显示出来了(枚举成功),但是使用 VISA 调用时只能调用串口,TMC 设备不能调用了。
通信中,会通过此前描述符中的 Bulk Out
端点向仪器发送报文。所以设备应该反复开启这个 Bulk Out
端点以接收任何可能的报文。如果设备没有开启 Bulk Out
端点,将会使得设备产生大量的 NAK 包
,触发向设备发送重启端点的 setup 指令
。
例如一个经典的报文如下
0x01, 0x02, 0xFD, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2A, 0x49, 0x44, 0x4E, 0x3F, 0x0A, 0x00, 0x00
这个报文可以依据 USBTMC_1_00 标准文档中的描述进行解析,分为如下几个段。
报文段 | 含义 |
---|---|
0x01 | 标识这个报文是 OUT 请求,也就是仪器需要解析这个报文的内容进行动作。 |
0x02, 0xFD | 报文 tag 及其反码,这个 tag 每个报文都会不同。 |
0x00 | 固定 0。 |
0x06, 0x00, 0x00, 0x00 | 指内容长度是 6 ,这个长度以小端 32 位传输。 |
0x01, 0x00, 0x00, 0x00 | 仅首字节的第一个 bit 有意义,是 1 表示内容语句已经完成传输,是 0 表示还有后续的内容报文。 |
0x2A, 0x49, 0x44, 0x4E, 0x3F, 0x0A | 6 个字节的正式内容,是 *IDN?\r ,\r 是回车的转义字符。 |
0x00, 0x00 | 保证整个报文长度是 4 字节对齐的补 0。 |
简单来说也就是解析 Bulk Out 端点接收到的报文数据的首个字节,如果是 0x01 那么进行后续解析,提取到 "*IDN?" 的正式 SCPI 指令并进行处理。
通信中,仪器并不能随意通过 USB 向上位机发送回复,这是和串口通信不同的。当上位机期望接收回复数据时,会在 Bulk Out 端点上发送一个报文,表示仪器现在可以发送回复了,一个经典开启回复的报文示例如下。
0x02, 0x03, 0xFC, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
这个报文可以依据 USBTMC_1_00 标准文档中的描述进行解析,分为如下几个段。
报文段 | 含义 |
---|---|
0x02 | 标识这个报文是 IN 请求,也就是仪器应当发送任何可能的回复了。 |
0x03, 0xFC | 报文 tag 及其反码,这个 tag 每个报文都会不同。 |
0x00 | 固定 0。 |
0x00, 0x04, 0x00, 0x00 | 表示上位机开通了 0x0400 共 1024 个字节的接收 buffer,这个值完全不用理会,上位机资源那么丰富,如果通信失败让他自己开大点就好了。 |
0x00 | 其 D1 位也就是 &0x2 的那一位有意义,如果是 1 意味着下面一个字节是上位机支持且设备必须支持的终止符,如果是 0 则不用理会。其他位全是预留。 |
0x00 | 如果上一个字节的 D1 位是 1,那么这个字节表示支持的终止符,例如 \r。 |
0x00, 0x00 | 填充的保留字符,实际作用也是保证整个报文长度是 4 字节对齐的补 0。 |
简单来说也就是解析 Bulk Out 端点接收到的报文数据的首个字节,如果是 0x02 那么开启设备的发送通道,例如上位机此前传输了 "*IDN?" 的正式 SCPI 指令,那么设备此时需要传输原本产生的回复。
而所谓指定终止符功能,当然不会支持,在后面必须的 setup 包介绍中,会介绍如何让上位机知道我们不支持这个指定。
一个经典的回复传输报文如下。
0x02, 0x03, 0xFC, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2A, 0x49, 0x44, 0x4E, 0x3F, 0x0A, 0x00, 0x00,
报文段 | 含义 |
---|---|
0x02 | 标识这个报文是 IN 请求的回复。 |
0x03, 0xFC | 报文 tag 及其反码,这个 tag 应该复制请求报文的。 |
0x00 | 固定 0。 |
0x06, 0x00, 0x00, 0x00 | 表示回复的有效数据共 6 个字节,这个长度以小端 32 位传输。 |
0x01, 0x00, 0x00, 0x00 | 仅首字节的第一个 bit 有意义,是 1 表示内容语句已经完成传输,是 0 表示还有后续的内容报文。 |
0x2A, 0x49, 0x44, 0x4E, 0x3F, 0x0A | *IDN?\r,这是在做回环测试,我的仪器会复制最后一条接收到的指令响应任何回复。 |
0x00, 0x00 | 保证整个报文长度是 4 字节对齐的补 0。 |
使用位运算非常方便高效地进行 4 字节对齐
0x80 0x06
开头的这些标准 setup 包,有如下类别,恕我也爱莫能助,我也是完全依赖 usbx 协议栈进行自动处理。
而 0xA1
or 0xA2
开头的 COMMAND_REQUEST 包,有如下内容是我们必须处理的。
以 0xA1 0x07
开头,表示在询问设备的特性,此时有一个示例的回复包如下。其中的内容比较多,可以自行翻阅文档,推荐是直接按这个回复就好了。此时会告诉上位机,我们不支持设定结尾符。
0x01, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
以 0xA2 0x01
开头,表示清除 bulk-out 传输,我们啥也不管,回复成功就好了,回复包如下。
0x01,tag(复制setup包以0计的第2个字节)
以 0xA2 0x02
开头,表示问询此前清除 bulk-out 传输的请求的状态,我们啥也不管,回复成功就好了,回复包如下。
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00
至于其他的 COMMAND_REQUEST 包,不常用不用管。
Note
首先你应该稍微熟悉 usbx 的实践,起码使用过其中已有的例子例如 CDC_ACM,否则以下内容毫无意义。以下均是在 usbx 这个 usb 协议栈框架下,如何将 TMC 枚举增加进去的示例。
关注到 _ux_device_stack_initialize 函数,其中有这样的部分,也就是只会以 Interface descriptor
以及 Configuration descriptor
部分在描述符整体中的位置进行寄存。Configuration descriptor
部分之前的统统作为设备描述符, Interface descriptor
部分以后的作为端点描述符。
那么需要稍微修改原本的 ux_device_descriptors.c
文件中 USBD_Device_Framework_Builder 函数的内容,原本函数中应该有类似如下的内容。其中指定只有高速 USB 才需要设备限定符,但是我们的 USB-TMC 现在通常都是工作在全速 USB 上的,又因为 USB-TMC 的协议没有高速全速之分,所以这里需要删掉这个 if 判断,无论高速还是全速,都把这个设备限定符编进描述符中。
其他部分不需要大的调整,只要按照示例描述符一段一段修改参数就可以了,在原文件基础上修改好的 ux_device_descriptors.c 和 ux_device_descriptors.h 文件如下。
管你这那的,看代码。开了一共 3 个 buffer,这是必须的,因为 USB 传输线路会占用一个 buffer,接收到的完整语句的寄存需要一个,发送寄存需要一个。值得注意的是,_ux_device_class_dpump_tmc_read 函数哪怕经过我的改造,也依然是一个阻塞的函数,所以 TMC_Engine() 的 TMC 收发支持需要独立占用一个线程。
TMC_Dpump 指针将会在 TMC_Device 的 activate 和 deactivate 中进行绑定和解绑。
我们知道,usbx 中想要开启设备类,必须调用 ux_device_stack_class_register() API,这个适合需要传入一个 _entry
函数作为类的驱动,依据 usbx 的官方示例和文档,自定义的类枚举最好以 dpump 类为基础,所以这里以 _ux_device_class_dpump_entry
为基础改造一个 TMC 类的驱动。
其中主要需要改写的是 UX_SLAVE_CLASS_COMMAND_QUERY 和 UX_SLAVE_CLASS_COMMAND_REQUEST 分支,UX_SLAVE_CLASS_COMMAND_QUERY 分支需要改写成对于 0xFE InterfaceClass: TMC 的支持,而 UX_SLAVE_CLASS_COMMAND_REQUEST 分支需要增加此前提到的 0xA1 0xA2 TMC 特殊命令的支持。
UCHAR device_framework_full_speed_demo[] = {
/* Device descriptor 设备描述符 */
0x12, // len(static)
0x01, // device(static)
0x00, 0x02, // USB2.0(static)
0x00, // DeviceClass(static)
0x00, // DeviceSubClass(static)
0x00, // bDeviceProtocol(static)
0x40, // bMaxPacketSize0
0x69, 0x0A, // idVendor (VID): 0x0A69
0x8A, 0x08, // idProduct (PID): 0x088A
0x00, 0x01, // bcdDevice: Device release number 0x0100
0x01, // iManufacturer 厂商字符串描述符的索引(static)
0x02, // iProduct 产品字符串描述符的索引(static)
0x03, // iSerialNumber 序列号字符串描述符的索引(static)
0x01, // bNumConfigurations(static)
/* Device qualifier descriptor 设备限定符描述符 */
0x0a, // len(static)
0x06, // bDescriptorType: Device Qualifier Descriptor(static)
0x00, 0x02, // USB2.0(static)
0x00, // DeviceClass(static)
0x00, // DeviceSubClass(static)
0x00, // bDeviceProtocol(static)
0x40, // bMaxPacketSize0
0x01, // bNumConfigurations(static)
0x00, // reserve(static)
/* Configuration descriptor 配置描述符 */
0x09, // len(static)
0x02, // bDescriptorType: Configuration Descriptor(static)
0x20, 0x00, // wTotalLength: Total length of data for this configuration (32 bytes)
0x01, // bNumInterfaces(static)
0x01, // bConfigurationValue(static)
0x00, // iConfiguration: Index of configuration string descriptor
0x80, // bmAttributes: Self-powered 自供电
0x64, // bMaxPower: 200 mA (0x64 * 2 mA)
/* Interface descriptor 接口描述符 */
0x09, // len(static)
0x04, // bDescriptorType: Interface Descriptor(static)
0x00, // bInterfaceNumber: Interface 0
0x00, // bAlternateSetting
0x02, // bNumEndpoints: 2 endpoints(static)
0xFE, // bInterfaceClass: TMC(static)
0x03, // bInterfaceSubClass: USB488(static)
0x01, // bInterfaceProtocol(static)
0x00, // iInterface: Index of interface string descriptor(static)
/* Endpoint descriptor 端点描述符(Bulk Out) */
0x07, // len(static)
0x05, // bDescriptorType: Endpoint Descriptor(static)
0x81, // bEndpointAddress: 端点地址(IN 方向,端点号 1)
0x02, // bmAttributes: 传输类型为批量(Bulk)
0x40, 0x00, // wMaxPacketSize: 最大包大小 64 字节
0x00, // bInterval: 查询间隔(用于批量传输时为 0)
/* Endpoint descriptor 端点描述符(Bulk In) */
0x07, // len(static)
0x05, // bDescriptorType: Endpoint Descriptor(static)
0x01, // bEndpointAddress: 端点地址(OUT 方向,端点号 1)
0x02, // bmAttributes: 传输类型为批量(Bulk)
0x40, 0x00, // wMaxPacketSize: 最大包大小 64 字节
0x00, // bInterval: 查询间隔(用于批量传输时为 0)
};
uint8_t *data; // 原有报文buffer
uint32_t len = OriginalLen; // 原有长度
for(uint32_t i = len; i < ((len + 0x3) & ~0x3U); i++)
{
data[i] = 0x00;
}
_write(data, ((len + 0x3) & ~0x3U));
#define UX_GET_STATUS 0u
#define UX_CLEAR_FEATURE 1u
#define UX_SET_FEATURE 3u
#define UX_SET_ADDRESS 5u
#define UX_GET_DESCRIPTOR 6u
#define UX_SET_DESCRIPTOR 7u
#define UX_GET_CONFIGURATION 8u
#define UX_SET_CONFIGURATION 9u
#define UX_GET_INTERFACE 10u
#define UX_SET_INTERFACE 11u
#define UX_SYNCH_FRAME 12u
switch(descriptor_type)
{
case UX_INTERFACE_DESCRIPTOR_ITEM:
......
break;
case UX_CONFIGURATION_DESCRIPTOR_ITEM:
......
break;
default:
break;
}
/* Check if USBx is in high speed mode to add qualifier descriptor */
if(Speed == USBD_HIGH_SPEED)
{
pDevQualDesc = (USBD_DevQualiDescTypedef *)(pDevFrameWorkDesc + pdev->CurrDevDescSz);
pDevQualDesc->bLength = (uint8_t)sizeof(USBD_DevQualiDescTypedef);
pDevQualDesc->bDescriptorType = UX_DEVICE_QUALIFIER_DESCRIPTOR_ITEM;
pDevQualDesc->bcdDevice = 0x0200;
pDevQualDesc->Class = 0x00;
pDevQualDesc->SubClass = 0x00;
pDevQualDesc->Protocol = 0x00;
pDevQualDesc->bMaxPacketSize = 0x40;
pDevQualDesc->bNumConfigurations = 0x01;
pDevQualDesc->bReserved = 0x00;
pdev->CurrDevDescSz += (uint32_t)sizeof(USBD_DevQualiDescTypedef);
}
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file ux_device_descriptors.c
* @author MCD Application Team
* @brief USBX Device descriptor header file
******************************************************************************
* @attention
*
* Copyright (c) 2020-2021 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "ux_device_descriptors.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
USBD_DevClassHandleTypeDef USBD_Device_FS, USBD_Device_HS;
uint8_t UserClassInstance[USBD_MAX_CLASS_INTERFACES] = {
CLASS_TYPE_TMC,
};
uint8_t UserTMCInterface[] = {
INTERFACE_TMC_USB488,
};
/* The generic device descriptor buffer that will be filled by builder
Size of the buffer is the maximum possible device FS descriptor size. */
#if defined(__ICCARM__) /* IAR Compiler */
#pragma data_alignment = 4
#endif /* defined ( __ICCARM__ ) */
__ALIGN_BEGIN static uint8_t DevFrameWorkDesc_FS[USBD_FRAMEWORK_MAX_DESC_SZ] __ALIGN_END = {0};
/* The generic device descriptor buffer that will be filled by builder
Size of the buffer is the maximum possible device HS descriptor size. */
#if defined(__ICCARM__) /* IAR Compiler */
#pragma data_alignment = 4
#endif /* defined ( __ICCARM__ ) */
__ALIGN_BEGIN static uint8_t DevFrameWorkDesc_HS[USBD_FRAMEWORK_MAX_DESC_SZ] __ALIGN_END = {0};
static uint8_t *pDevFrameWorkDesc_FS = DevFrameWorkDesc_FS;
static uint8_t *pDevFrameWorkDesc_HS = DevFrameWorkDesc_HS;
/* USER CODE BEGIN PV0 */
/* USER CODE END PV0 */
/* String Device Framework :
Byte 0 and 1 : Word containing the language ID : 0x0904 for US
Byte 2 : Byte containing the index of the descriptor
Byte 3 : Byte containing the length of the descriptor string
*/
#if defined(__ICCARM__) /* IAR Compiler */
#pragma data_alignment = 4
#endif /* defined ( __ICCARM__ ) */
__ALIGN_BEGIN UCHAR USBD_string_framework[USBD_STRING_FRAMEWORK_MAX_LENGTH] __ALIGN_END = {0};
/* Multiple languages are supported on the device, to add
a language besides English, the Unicode language code must
be appended to the language_id_framework array and the length
adjusted accordingly. */
#if defined(__ICCARM__) /* IAR Compiler */
#pragma data_alignment = 4
#endif /* defined ( __ICCARM__ ) */
__ALIGN_BEGIN UCHAR USBD_language_id_framework[LANGUAGE_ID_MAX_LENGTH] __ALIGN_END = {0};
/* USER CODE BEGIN PV1 */
/* USER CODE END PV1 */
/* Private function prototypes -----------------------------------------------*/
static void USBD_Desc_GetString(uint8_t *desc, uint8_t *Buffer, uint16_t *len);
static uint8_t USBD_Desc_GetLen(uint8_t *buf);
static uint8_t *USBD_Device_Framework_Builder(USBD_DevClassHandleTypeDef *pdev, uint8_t *pDevFrameWorkDesc, uint8_t *UserClassInstance, uint8_t Speed);
static uint8_t USBD_FrameWork_AddToConfDesc(USBD_DevClassHandleTypeDef *pdev, uint8_t Speed, uint8_t *pCmpstConfDesc);
static uint8_t USBD_FrameWork_AddClass(USBD_DevClassHandleTypeDef *pdev, USBD_CompositeClassTypeDef class, uint8_t cfgidx, uint8_t Speed, uint8_t *pCmpstConfDesc);
static uint8_t USBD_FrameWork_FindFreeIFNbr(USBD_DevClassHandleTypeDef *pdev);
static void USBD_FrameWork_AddConfDesc(uint32_t Conf, uint32_t *pSze);
static void USBD_FrameWork_AssignEp(USBD_DevClassHandleTypeDef *pdev, uint8_t Add, uint8_t Type, uint32_t Sze);
static void USBD_FrameWork_TMC_Desc(USBD_DevClassHandleTypeDef *pdev, uint32_t pConf, uint32_t *Sze);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief USBD_Get_Device_Framework_Speed
* Return the device speed descriptor
* @param Speed : HIGH or FULL SPEED flag
* @param length : length of HIGH or FULL SPEED array
* @retval Pointer to descriptor buffer
*/
uint8_t *USBD_Get_Device_Framework_Speed(uint8_t Speed, ULONG *Length)
{
uint8_t *pFrameWork = NULL;
/* USER CODE BEGIN Device_Framework0 */
/* USER CODE END Device_Framework0 */
if(USBD_FULL_SPEED == Speed)
{
USBD_Device_Framework_Builder(&USBD_Device_FS, pDevFrameWorkDesc_FS, UserClassInstance, Speed);
/* Get the length of USBD_device_framework_full_speed */
*Length = (ULONG)(USBD_Device_FS.CurrDevDescSz + USBD_Device_FS.CurrConfDescSz);
pFrameWork = pDevFrameWorkDesc_FS;
}
else
{
USBD_Device_Framework_Builder(&USBD_Device_HS, pDevFrameWorkDesc_HS, UserClassInstance, Speed);
/* Get the length of USBD_device_framework_high_speed */
*Length = (ULONG)(USBD_Device_HS.CurrDevDescSz + USBD_Device_HS.CurrConfDescSz);
pFrameWork = pDevFrameWorkDesc_HS;
}
/* USER CODE BEGIN Device_Framework1 */
/* USER CODE END Device_Framework1 */
return pFrameWork;
}
/**
* @brief USBD_Get_String_Framework
* Return the language_id_framework
* @param Length : Length of String_Framework
* @retval Pointer to language_id_framework buffer
*/
uint8_t *USBD_Get_String_Framework(ULONG *Length)
{
uint16_t len = 0U;
uint8_t count = 0U;
/* USER CODE BEGIN String_Framework0 */
/* USER CODE END String_Framework0 */
/* Set the Manufacturer language Id and index in USBD_string_framework */
USBD_string_framework[count++] = USBD_LANGID_STRING & 0xFF;
USBD_string_framework[count++] = USBD_LANGID_STRING >> 8;
USBD_string_framework[count++] = USBD_IDX_MFC_STR;
/* Set the Manufacturer string in string_framework */
USBD_Desc_GetString((uint8_t *)USBD_MANUFACTURER_STRING, USBD_string_framework + count, &len);
/* Set the Product language Id and index in USBD_string_framework */
count += len + 1;
USBD_string_framework[count++] = USBD_LANGID_STRING & 0xFF;
USBD_string_framework[count++] = USBD_LANGID_STRING >> 8;
USBD_string_framework[count++] = USBD_IDX_PRODUCT_STR;
/* Set the Product string in USBD_string_framework */
USBD_Desc_GetString((uint8_t *)USBD_PRODUCT_STRING, USBD_string_framework + count, &len);
/* Set Serial language Id and index in string_framework */
count += len + 1;
USBD_string_framework[count++] = USBD_LANGID_STRING & 0xFF;
USBD_string_framework[count++] = USBD_LANGID_STRING >> 8;
USBD_string_framework[count++] = USBD_IDX_SERIAL_STR;
/* Set the Serial number in USBD_string_framework */
USBD_Desc_GetString((uint8_t *)USBD_SERIAL_NUMBER, USBD_string_framework + count, &len);
/* USER CODE BEGIN String_Framework1 */
/* USER CODE END String_Framework1 */
/* Get the length of USBD_string_framework */
*Length = strlen((const char *)USBD_string_framework);
return USBD_string_framework;
}
/**
* @brief USBD_Get_Language_Id_Framework
* Return the language_id_framework
* @param Length : Length of Language_Id_Framework
* @retval Pointer to language_id_framework buffer
*/
uint8_t *USBD_Get_Language_Id_Framework(ULONG *Length)
{
uint8_t count = 0U;
/* Set the language Id in USBD_language_id_framework */
USBD_language_id_framework[count++] = USBD_LANGID_STRING & 0xFF;
USBD_language_id_framework[count++] = USBD_LANGID_STRING >> 8;
/* Get the length of USBD_language_id_framework */
*Length = strlen((const char *)USBD_language_id_framework);
return USBD_language_id_framework;
}
/**
* @brief USBD_Get_Interface_Number
* Return interface number
* @param class_type : Device class type
* @param interface_type : Device interface type
* @retval interface number
*/
uint16_t USBD_Get_Interface_Number(uint8_t class_type, uint8_t interface_type)
{
uint8_t itf_num = 0U;
uint8_t idx = 0U;
/* USER CODE BEGIN USBD_Get_Interface_Number0 */
/* USER CODE END USBD_Get_Interface_Number0 */
for(idx = 0; idx < USBD_MAX_SUPPORTED_CLASS; idx++)
{
if((USBD_Device_FS.tclasslist[idx].ClassType == class_type) && (USBD_Device_FS.tclasslist[idx].InterfaceType == interface_type))
{
itf_num = USBD_Device_FS.tclasslist[idx].Ifs[0];
}
}
/* USER CODE BEGIN USBD_Get_Interface_Number1 */
/* USER CODE END USBD_Get_Interface_Number1 */
return itf_num;
}
/**
* @brief USBD_Get_Configuration_Number
* Return configuration number
* @param class_type : Device class type
* @param interface_type : Device interface type
* @retval configuration number
*/
uint16_t USBD_Get_Configuration_Number(uint8_t class_type, uint8_t interface_type)
{
uint8_t cfg_num = 1U;
/* USER CODE BEGIN USBD_Get_CONFIGURATION_Number0 */
/* USER CODE END USBD_Get_CONFIGURATION_Number0 */
/* USER CODE BEGIN USBD_Get_CONFIGURATION_Number1 */
/* USER CODE END USBD_Get_CONFIGURATION_Number1 */
return cfg_num;
}
/**
* @brief USBD_Desc_GetString
* Convert ASCII string into Unicode one
* @param desc : descriptor buffer
* @param Unicode : Formatted string buffer (Unicode)
* @param len : descriptor length
* @retval None
*/
static void USBD_Desc_GetString(uint8_t *desc, uint8_t *unicode, uint16_t *len)
{
uint8_t idx = 0U;
uint8_t *pdesc;
if(desc == NULL)
{
return;
}
pdesc = desc;
*len = (uint16_t)USBD_Desc_GetLen(pdesc);
unicode[idx++] = *(uint8_t *)len;
while(*pdesc != (uint8_t)'\0')
{
unicode[idx++] = *pdesc;
pdesc++;
}
}
/**
* @brief USBD_Desc_GetLen
* return the string length
* @param buf : pointer to the ASCII string buffer
* @retval string length
*/
static uint8_t USBD_Desc_GetLen(uint8_t *buf)
{
uint8_t len = 0U;
uint8_t *pbuff = buf;
while(*pbuff != (uint8_t)'\0')
{
len++;
pbuff++;
}
return len;
}
/**
* @brief USBD_Device_Framework_Builder
* Device Framework builder
* @param pdev: device instance
* @param pDevFrameWorkDesc: Pointer to the device framework descriptor
* @param UserClassInstance: type of the class to be added
* @param Speed: Speed parameter HS or FS
* @retval status
*/
static uint8_t *USBD_Device_Framework_Builder(USBD_DevClassHandleTypeDef *pdev, uint8_t *pDevFrameWorkDesc, uint8_t *UserClassInstance, uint8_t Speed)
{
static USBD_DeviceDescTypedef *pDevDesc;
static USBD_DevQualiDescTypedef *pDevQualDesc;
uint8_t Idx_Instance = 0U;
/* Set Dev and conf descriptors size to 0 */
pdev->CurrConfDescSz = 0U;
pdev->CurrDevDescSz = 0U;
/* Set the pointer to the device descriptor area*/
pDevDesc = (USBD_DeviceDescTypedef *)pDevFrameWorkDesc;
/* Start building the generic device descriptor common part */
pDevDesc->bLength = (uint8_t)sizeof(USBD_DeviceDescTypedef);
pDevDesc->bDescriptorType = UX_DEVICE_DESCRIPTOR_ITEM;
pDevDesc->bcdUSB = USB_BCDUSB;
pDevDesc->bDeviceClass = 0x00;
pDevDesc->bDeviceSubClass = 0x00;
pDevDesc->bDeviceProtocol = 0x00;
pDevDesc->bMaxPacketSize = USBD_MAX_EP0_SIZE;
pDevDesc->idVendor = USBD_VID;
pDevDesc->idProduct = USBD_PID;
pDevDesc->bcdDevice = 0x0100;
pDevDesc->iManufacturer = USBD_IDX_MFC_STR;
pDevDesc->iProduct = USBD_IDX_PRODUCT_STR;
pDevDesc->iSerialNumber = USBD_IDX_SERIAL_STR;
pDevDesc->bNumConfigurations = USBD_MAX_NUM_CONFIGURATION;
pdev->CurrDevDescSz += (uint32_t)sizeof(USBD_DeviceDescTypedef);
/* Check if USBx is in high speed mode to add qualifier descriptor */
pDevQualDesc = (USBD_DevQualiDescTypedef *)(pDevFrameWorkDesc + pdev->CurrDevDescSz);
pDevQualDesc->bLength = (uint8_t)sizeof(USBD_DevQualiDescTypedef);
pDevQualDesc->bDescriptorType = UX_DEVICE_QUALIFIER_DESCRIPTOR_ITEM;
pDevQualDesc->bcdDevice = 0x0200;
pDevQualDesc->Class = 0x00;
pDevQualDesc->SubClass = 0x00;
pDevQualDesc->Protocol = 0x00;
pDevQualDesc->bMaxPacketSize = 0x40;
pDevQualDesc->bNumConfigurations = 0x01;
pDevQualDesc->bReserved = 0x00;
pdev->CurrDevDescSz += (uint32_t)sizeof(USBD_DevQualiDescTypedef);
/* Build the device framework */
while(Idx_Instance < USBD_MAX_SUPPORTED_CLASS)
{
if((pdev->classId < USBD_MAX_SUPPORTED_CLASS) && (pdev->NumClasses < USBD_MAX_SUPPORTED_CLASS) && (UserClassInstance[Idx_Instance] != CLASS_TYPE_NONE))
{
/* Call the composite class builder */
(void)USBD_FrameWork_AddClass(pdev, (USBD_CompositeClassTypeDef)UserClassInstance[Idx_Instance], 0, Speed, (pDevFrameWorkDesc + pdev->CurrDevDescSz));
/* Increment the ClassId for the next occurrence */
pdev->classId++;
pdev->NumClasses++;
}
Idx_Instance++;
}
/* Check if there is a composite class and update device class */
if(pdev->NumClasses > 1)
{
pDevDesc->bDeviceClass = 0xEF;
pDevDesc->bDeviceSubClass = 0x02;
pDevDesc->bDeviceProtocol = 0x01;
}
else
{
/* Check if the CDC ACM class is set and update device class */
if(UserClassInstance[0] == CLASS_TYPE_CDC_ACM)
{
pDevDesc->bDeviceClass = 0x02;
pDevDesc->bDeviceSubClass = 0x02;
pDevDesc->bDeviceProtocol = 0x00;
}
}
return pDevFrameWorkDesc;
}
/**
* @brief USBD_FrameWork_AddToConfDesc
* Add a new class to the configuration descriptor
* @param pdev: device instance
* @param Speed: device speed
* @param pCmpstConfDesc: to composite device configuration descriptor
* @retval status
*/
uint8_t USBD_FrameWork_AddToConfDesc(USBD_DevClassHandleTypeDef *pdev, uint8_t Speed, uint8_t *pCmpstConfDesc)
{
uint8_t interface = 0U;
pdev->Speed = Speed;
if(pdev->classId == 0U)
{
/* Add configuration and IAD descriptors */
USBD_FrameWork_AddConfDesc((uint32_t)pCmpstConfDesc, &pdev->CurrConfDescSz);
}
switch(pdev->tclasslist[pdev->classId].ClassType)
{
case CLASS_TYPE_TMC:
switch(pdev->tclasslist[pdev->classId].InterfaceType)
{
case INTERFACE_TMC_USB488:
/* Find the first available interface slot and Assign number of interfaces */
interface = USBD_FrameWork_FindFreeIFNbr(pdev);
pdev->tclasslist[pdev->classId].NumIf = 1U;
pdev->tclasslist[pdev->classId].Ifs[0] = interface;
/* Assign endpoint numbers */
pdev->tclasslist[pdev->classId].NumEps = 2U; /* EP_IN, EP_OUT */
/* Check the current speed to assign endpoints */
if(pdev->Speed == USBD_HIGH_SPEED)
{
/* Assign IN Endpoint */
USBD_FrameWork_AssignEp(pdev, USBD_TMC_NONE_EPIN_ADDR, USBD_EP_TYPE_BULK, USBD_TMC_NONE_EPIN_HS_MPS);
/* Assign OUT Endpoint */
USBD_FrameWork_AssignEp(pdev, USBD_TMC_NONE_EPOUT_ADDR, USBD_EP_TYPE_BULK, USBD_TMC_NONE_EPOUT_HS_MPS);
}
else
{
USBD_FrameWork_AssignEp(pdev, USBD_TMC_NONE_EPIN_ADDR, USBD_EP_TYPE_BULK, USBD_TMC_NONE_EPIN_FS_MPS);
USBD_FrameWork_AssignEp(pdev, USBD_TMC_NONE_EPOUT_ADDR, USBD_EP_TYPE_BULK, USBD_TMC_NONE_EPOUT_FS_MPS);
}
USBD_FrameWork_TMC_Desc(pdev, (uint32_t)pCmpstConfDesc, &pdev->CurrConfDescSz);
break;
default:
break;
}
break;
default:
break;
}
return UX_SUCCESS;
}
/**
* @brief USBD_FrameWork_AddClass
* Register a class in the class builder
* @param pdev: device instance
* @param class: type of the class to be added (from USBD_CompositeClassTypeDef)
* @param cfgidx: configuration index
* @param speed: device speed
* @param pCmpstConfDesc: to composite device configuration descriptor
* @retval status
*/
uint8_t USBD_FrameWork_AddClass(USBD_DevClassHandleTypeDef *pdev, USBD_CompositeClassTypeDef class, uint8_t cfgidx, uint8_t Speed, uint8_t *pCmpstConfDesc)
{
static uint8_t interface_idx = 0U;
if((pdev->classId < USBD_MAX_SUPPORTED_CLASS) && (pdev->tclasslist[pdev->classId].Active == 0U))
{
/* Store the class parameters in the global tab */
pdev->tclasslist[pdev->classId].ClassId = pdev->classId;
pdev->tclasslist[pdev->classId].Active = 1U;
pdev->tclasslist[pdev->classId].ClassType = class;
if(class == CLASS_TYPE_TMC)
{
pdev->tclasslist[pdev->classId].InterfaceType = UserTMCInterface[interface_idx];
interface_idx++;
if(interface_idx == sizeof(UserTMCInterface))
{
interface_idx = 0U;
}
}
/* Call configuration descriptor builder and endpoint configuration builder */
if(USBD_FrameWork_AddToConfDesc(pdev, Speed, pCmpstConfDesc) != UX_SUCCESS)
{
return UX_ERROR;
}
}
UNUSED(cfgidx);
return UX_SUCCESS;
}
/**
* @brief USBD_FrameWork_FindFreeIFNbr
* Find the first interface available slot
* @param pdev: device instance
* @retval The interface number to be used
*/
static uint8_t USBD_FrameWork_FindFreeIFNbr(USBD_DevClassHandleTypeDef *pdev)
{
uint32_t idx = 0U;
/* Unroll all already activated classes */
for(uint32_t i = 0U; i < pdev->NumClasses; i++)
{
/* Unroll each class interfaces */
for(uint32_t j = 0U; j < pdev->tclasslist[i].NumIf; j++)
{
/* Increment the interface counter index */
idx++;
}
}
/* Return the first available interface slot */
return (uint8_t)idx;
}
/**
* @brief USBD_FrameWork_AddConfDesc
* Add a new class to the configuration descriptor
* @param Conf: configuration descriptor
* @param pSze: pointer to the configuration descriptor size
* @retval none
*/
static void USBD_FrameWork_AddConfDesc(uint32_t Conf, uint32_t *pSze)
{
/* Intermediate variable to comply with MISRA-C Rule 11.3 */
USBD_ConfigDescTypedef *ptr = (USBD_ConfigDescTypedef *)Conf;
ptr->bLength = (uint8_t)sizeof(USBD_ConfigDescTypedef);
ptr->bDescriptorType = UX_CONFIGURATION_DESCRIPTOR_ITEM;
ptr->wDescriptorLength = 0x20;
ptr->bNumInterfaces = 1U;
ptr->bConfigurationValue = 1U;
ptr->iConfiguration = USBD_CONFIG_STR_DESC_IDX;
ptr->bmAttributes = USBD_CONFIG_BMATTRIBUTES;
ptr->bMaxPower = USBD_CONFIG_MAXPOWER;
*pSze += sizeof(USBD_ConfigDescTypedef);
}
/**
* @brief USBD_FrameWork_AssignEp
* Assign and endpoint
* @param pdev: device instance
* @param Add: Endpoint address
* @param Type: Endpoint type
* @param Sze: Endpoint max packet size
* @retval none
*/
static void USBD_FrameWork_AssignEp(USBD_DevClassHandleTypeDef *pdev, uint8_t Add, uint8_t Type, uint32_t Sze)
{
uint32_t idx = 0U;
/* Find the first available endpoint slot */
while(((idx < (pdev->tclasslist[pdev->classId]).NumEps) && ((pdev->tclasslist[pdev->classId].Eps[idx].is_used) != 0U)))
{
/* Increment the index */
idx++;
}
/* Configure the endpoint */
pdev->tclasslist[pdev->classId].Eps[idx].add = Add;
pdev->tclasslist[pdev->classId].Eps[idx].type = Type;
pdev->tclasslist[pdev->classId].Eps[idx].size = (uint16_t)Sze;
pdev->tclasslist[pdev->classId].Eps[idx].is_used = 1U;
}
/**
* @brief USBD_FrameWork_TMC_Desc
* Configure and Append the TMC Descriptor
* @param pdev: device instance
* @param pConf: Configuration descriptor pointer
* @param Sze: pointer to the current configuration descriptor size
* @retval None
*/
static void USBD_FrameWork_TMC_Desc(USBD_DevClassHandleTypeDef *pdev, uint32_t pConf, uint32_t *Sze)
{
static USBD_IfDescTypedef *pIfDesc;
static USBD_EpDescTypedef *pEpDesc;
switch(pdev->tclasslist[pdev->classId].InterfaceType)
{
case INTERFACE_TMC_USB488:
/* Append Interface descriptor to Configuration descriptor */
__USBD_FRAMEWORK_SET_IF(pdev->tclasslist[pdev->classId].Ifs[0], 0U, (uint8_t)(pdev->tclasslist[pdev->classId].NumEps), UX_DEVICE_CLASS_TMC_CLASS, UX_DEVICE_SUB_CLASS_USB488_CLASS, INTERFACE_TMC_USB488, 0U);
if(pdev->Speed == USBD_HIGH_SPEED)
{
/* Append Endpoint descriptor to Configuration descriptor */
__USBD_FRAMEWORK_SET_EP(pdev->tclasslist[pdev->classId].Eps[0].add, USBD_EP_TYPE_BULK, (uint16_t)pdev->tclasslist[pdev->classId].Eps[0].size, USBD_TMC_NONE_EPIN_FS_BINTERVAL, USBD_TMC_NONE_EPIN_HS_BINTERVAL);
__USBD_FRAMEWORK_SET_EP(pdev->tclasslist[pdev->classId].Eps[1].add, USBD_EP_TYPE_BULK, (uint16_t)pdev->tclasslist[pdev->classId].Eps[1].size, USBD_TMC_NONE_EPOUT_HS_BINTERVAL, USBD_TMC_NONE_EPOUT_FS_BINTERVAL);
}
else
{
/* Append Endpoint descriptor to Configuration descriptor */
__USBD_FRAMEWORK_SET_EP(pdev->tclasslist[pdev->classId].Eps[0].add, USBD_EP_TYPE_BULK, (uint16_t)pdev->tclasslist[pdev->classId].Eps[0].size, USBD_TMC_NONE_EPIN_FS_BINTERVAL, USBD_TMC_NONE_EPIN_HS_BINTERVAL);
__USBD_FRAMEWORK_SET_EP(pdev->tclasslist[pdev->classId].Eps[1].add, USBD_EP_TYPE_BULK, (uint16_t)pdev->tclasslist[pdev->classId].Eps[1].size, USBD_TMC_NONE_EPOUT_HS_BINTERVAL, USBD_TMC_NONE_EPOUT_FS_BINTERVAL);
}
break;
default:
break;
}
}
/********************************************************************************
**** Copyright (C), 2024, Yuanlong Xu <Yono233@outlook.com> ****
**** All rights reserved ****
********************************************************************************
* File Name : ux_device_tmc.c
* Author : yono
* Date : 2025-01-07
* Version : 1.0
********************************************************************************/
/**************************************************************************/
/*
tmc用户解析
*/
/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include "ux_api.h"
#include "ux_device_class_dpump.h"
/* Private types -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
#define UX_HOST_CLASS_DPUMP_PACKET_SIZE 255
extern UX_SLAVE_CLASS_DPUMP *TMC_Dpump;
/* 信道buffer */
uint8_t device_buffer[UX_HOST_CLASS_DPUMP_PACKET_SIZE];
/* 帧信息 */
uint8_t TransferEMO = 0; // 传输结束标志 0x00继续传输 0x01传输结束
uint8_t TransferTag = 0; // 传输令牌,回复时复制
/* 信息buffer */
uint8_t SCPI_Read[255];
uint32_t SCPI_Read_Length = 0;
uint8_t SCPI_Write[255];
uint32_t SCPI_Write_Length = 0;
/* Private Constants ---------------------------------------------------------*/
/* Private macros ------------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
uint32_t TMC_Write(uint8_t *data, uint32_t len);
extern UINT _ux_device_class_dpump_tmc_read(UX_SLAVE_CLASS_DPUMP *dpump, UCHAR *buffer, ULONG requested_length, ULONG *actual_length);
/* Private functions ---------------------------------------------------------*/
void TMC_Engine(void)
{
UINT status;
ULONG actual_length;
if(TMC_Dpump != UX_NULL)
{
/* 收取部分 */
status = _ux_device_class_dpump_tmc_read(TMC_Dpump, device_buffer, UX_HOST_CLASS_DPUMP_PACKET_SIZE, &actual_length);
if(status == UX_SUCCESS)
{
if((0xff ^ device_buffer[1] ^ device_buffer[2]) || device_buffer[3] != 0)
return; // 不良报文
switch(device_buffer[0]) // 命令
{
case 0x01:
// 接收分支
ux_utility_memory_copy(&SCPI_Read[0 + SCPI_Read_Length], // 兼容分包传输,以前一次传输的长度为起始
&device_buffer[12], // 真实携带数据的起始位置
*(uint32_t *)(&device_buffer[4])); // 4~7字节以小端组成数据长度
SCPI_Read_Length += *(uint32_t *)(&device_buffer[4]);
TransferEMO = device_buffer[8] & 0x01;
if(TransferEMO)
{
/* 回环测试 */
TMC_Write(SCPI_Read, SCPI_Read_Length);
/* 结尾兼容 */
// 传输结束 触发解析
SCPI_Read_Length = 0;
}
else
{
// 传输未结束 不处理
}
break;
case 0x02:
TransferTag = device_buffer[1]; // 寄存令牌
/* 0~3 是请求及令牌,无需更改*/
*(uint32_t *)(&device_buffer[4]) = SCPI_Write_Length; // 4~7字节以小端组成数据长度
/* 成功固定数据 */
device_buffer[8] = 0x1; // 成功回复
device_buffer[9] = 0x00;
device_buffer[10] = 0x00;
device_buffer[11] = 0x00;
/* 填充发送数据 */
ux_utility_memory_copy(&device_buffer[12], SCPI_Write, SCPI_Write_Length);
/* 填充0x00 此处如果原本就是4字节对齐,将多补4个0 */
for(uint32_t i = 12 + SCPI_Write_Length; i < ((12 + SCPI_Write_Length + 0x3) & ~0x3U); i++)
{
device_buffer[i] = 0x00;
}
/* 请求发送 */
status = _ux_device_class_dpump_write(TMC_Dpump, device_buffer, ((12 + SCPI_Write_Length + 0x3) & ~0x3U), &actual_length);
/* 清理发送请求 */
SCPI_Write_Length = 0;
break;
default:
break;
}
}
}
}
uint32_t TMC_Write(uint8_t *data, uint32_t len)
{
if(sizeof(SCPI_Write) < (SCPI_Write_Length + len))
return 1;
ux_utility_memory_copy(&SCPI_Write[0 + SCPI_Write_Length], // 兼容分包填充
data,
len);
SCPI_Write_Length += len;
return 0;
}
/**************************************************************************/
/* */
/* Copyright (c) Microsoft Corporation. All rights reserved. */
/* */
/* This software is licensed under the Microsoft Software License */
/* Terms for Microsoft Azure RTOS. Full text of the license can be */
/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
/* and in the root directory of this software. */
/* */
/**************************************************************************/
/**************************************************************************/
/**************************************************************************/
/** */
/** USBX Component */
/** */
/** Device Data Pump Class */
/** */
/**************************************************************************/
/**************************************************************************/
#define UX_SOURCE_CODE
/* Include necessary system files. */
#include "ux_api.h"
#include "ux_device_class_dpump.h"
#define UX_SLAVE_CLASS_DPUMP_TMC_CLASS 0xFE
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _ux_device_class_dpump_tmc_entry PORTABLE C */
/* 6.1 */
/* AUTHOR */
/* */
/* yono, yono233.cn */
/* */
/* DESCRIPTION */
/* */
/* This function is the entry point of the device dpump class. It */
/* will be called by the device stack enumeration module when the */
/* host has sent a SET_CONFIGURATION command and the dpump interface */
/* needs to be mounted. */
/* */
/* INPUT */
/* */
/* command Pointer to class command */
/* */
/* OUTPUT */
/* */
/* Completion Status */
/* */
/* CALLS */
/* */
/* _ux_device_class_dpump_initialize Initialize dpump class */
/* _ux_device_class_dpump_activate Activate dpump class */
/* _ux_device_class_dpump_deactivate Deactivate dpump class */
/* _ux_device_class_dpump_change Alternate setting change */
/* */
/* CALLED BY */
/* */
/* Device Data Pump Class */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
/* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
/* resulting in version 6.1 */
/* 01-07-2025 yono Initial */
/* */
/**************************************************************************/
UINT _ux_device_class_dpump_tmc_entry(UX_SLAVE_CLASS_COMMAND *command)
{
UINT status;
/* The command request will tell us we need to do here, either a enumeration
query, an activation or a deactivation. */
switch(command->ux_slave_class_command_request)
{
case UX_SLAVE_CLASS_COMMAND_INITIALIZE:
/* Call the init function of the DPUMP class. */
status = _ux_device_class_dpump_initialize(command);
/* Return the completion status. */
return (status);
case UX_SLAVE_CLASS_COMMAND_QUERY:
/* Check the CLASS definition in the interface descriptor. */
if(command->ux_slave_class_command_class == UX_SLAVE_CLASS_DPUMP_TMC_CLASS)
return (UX_SUCCESS);
else
return (UX_NO_CLASS_MATCH);
case UX_SLAVE_CLASS_COMMAND_ACTIVATE:
/* The activate command is used when the host has sent a SET_CONFIGURATION command
and this interface has to be mounted. Both Bulk endpoints have to be mounted
and the dpump thread needs to be activated. */
status = _ux_device_class_dpump_activate(command);
/* Return the completion status. */
return (status);
case UX_SLAVE_CLASS_COMMAND_CHANGE:
/* The change command is used when the host has sent a SET_INTERFACE command
to go from Alternate Setting 0 to 1 or revert to the default mode. */
status = _ux_device_class_dpump_change(command);
/* Return the completion status. */
return (status);
case UX_SLAVE_CLASS_COMMAND_DEACTIVATE:
/* The deactivate command is used when the device has been extracted.
The device endpoints have to be dismounted and the dpump thread canceled. */
status = _ux_device_class_dpump_deactivate(command);
/* Return the completion status. */
return (status);
case UX_SLAVE_CLASS_COMMAND_REQUEST:
{ // my request begin
status = UX_SUCCESS;
UX_SLAVE_DEVICE *device;
UX_SLAVE_TRANSFER *transfer_request;
UX_SLAVE_ENDPOINT *endpoint;
ULONG length;
/* Get the pointer to the device. */
device = &_ux_system_slave->ux_system_slave_device;
/* Get the control endpoint associated with the device. */
endpoint = &device->ux_slave_device_control_endpoint;
/* Get the pointer to the transfer request associated with the endpoint. */
transfer_request = &endpoint->ux_slave_endpoint_transfer_request;
switch(transfer_request->ux_slave_transfer_request_setup[0])
{
case 0xA1: /* Use case of memcpy is verified. */
length = transfer_request->ux_slave_transfer_request_setup[6] + (transfer_request->ux_slave_transfer_request_setup[7] << 8);
/* Set the direction to OUT. */
transfer_request->ux_slave_transfer_request_phase = UX_TRANSFER_PHASE_DATA_OUT;
switch(transfer_request->ux_slave_transfer_request_setup[1])
{
case 0x07:
/* Copy the device descriptor into the transfer request memory. */
_ux_utility_memory_copy(transfer_request->ux_slave_transfer_request_data_pointer, //
"\x01\x00\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", //
0x18);
break;
default:
break;
}
_ux_device_stack_transfer_request(transfer_request, length, length);
break;
case 0xA2:
length = transfer_request->ux_slave_transfer_request_setup[6] + (transfer_request->ux_slave_transfer_request_setup[7] << 8);
/* Set the direction to IN. */
transfer_request->ux_slave_transfer_request_phase = UX_TRANSFER_PHASE_DATA_OUT;
switch(transfer_request->ux_slave_transfer_request_setup[1])
{
case 0x01:
transfer_request->ux_slave_transfer_request_data_pointer[0] = 0x01; // USBTMC_status values
transfer_request->ux_slave_transfer_request_data_pointer[1] = transfer_request->ux_slave_transfer_request_setup[2]; // 复制令牌
length = 2;
break;
case 0x02:
transfer_request->ux_slave_transfer_request_data_pointer[0] = 0x01; // USBTMC_status values
transfer_request->ux_slave_transfer_request_data_pointer[1] = 0x00; // fixed segment
transfer_request->ux_slave_transfer_request_data_pointer[2] = 0x00;
transfer_request->ux_slave_transfer_request_data_pointer[3] = 0x00;
transfer_request->ux_slave_transfer_request_data_pointer[4] = 0x00; // 剩余未处理的字节数
transfer_request->ux_slave_transfer_request_data_pointer[5] = 0x00;
transfer_request->ux_slave_transfer_request_data_pointer[6] = 0x00;
transfer_request->ux_slave_transfer_request_data_pointer[7] = 0x00;
break;
default:
break;
}
/* Receive the data from the host. */
_ux_device_stack_transfer_request(transfer_request, length, length);
break;
default:
break;
}
} // my request end
return (UX_SUCCESS);
default:
/* Error trap. */
_ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED);
/* If trace is enabled, insert this event into the trace buffer. */
UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0)
/* Return an error. */
return (UX_FUNCTION_NOT_SUPPORTED);
}
}