第 5 章 - USBX 设备类注意事项
设备类注册
每个设备类都遵循相同的注册原则。 包含特定类参数的结构将传递给类初始化函数。
/* Set the parameters for callback when insertion/extraction of a HID device. */
hid_parameter.ux_slave_class_hid_instance_activate = tx_demo_hid_instance_activate;
hid_parameter.ux_slave_class_hid_instance_deactivate = tx_demo_hid_instance_deactivate;
/* Initialize the hid class parameters for the device. */
hid_parameter.ux_device_class_hid_parameter_report_address = hid_device_report;
hid_parameter.ux_device_class_hid_parameter_report_length = HID_DEVICE_REPORT_LENGTH;
hid_parameter.ux_device_class_hid_parameter_report_id = UX_TRUE;
hid_parameter.ux_device_class_hid_parameter_callback = demo_thread_hid_callback;
/* Initialize the device hid class. The class is connected with interface 0 */
status = ux_device_stack_class_register(_ux_system_slave_class_hid_name,
ux_device_class_hid_entry,1,0, (VOID *)&hid_parameter);
当激活类的实例时,每个类都可以注册一个回调函数。 然后,设备堆栈调用该回调,以通知应用程序已创建实例。
应用程序的主体中将有 2 个用于激活和停用的函数,如下面的示例中所示。
VOID tx_demo_hid_instance_activate(VOID *hid_instance)
{
/* Save the HID instance. */
hid_slave = (UX_SLAVE_CLASS_HID *) hid_instance;
}
VOID tx_demo_hid_instance_deactivate(VOID *hid_instance)
{
/* Reset the HID instance. */
hid_slave = UX_NULL;
}
不建议在这些函数中执行任何操作,但要记住类的实例,并与应用程序的其余部分同步。
批量传输的一般注意事项
根据 USB 2.0 规范,终结点传输数据有效负载时的数据字段必须始终小于或等于终结点报告的 wMaxPacketSize 值。 数据包的大小限制为 bMaxPacketSize。 以下情况下可完成传输
- 终结点已恰好传输了预期的数据量
- 设备或主机终结点收到的数据包大小小于最大数据包大小 (wMaxPacketSize)。 这一短数据包指示没有剩余的数据包且传输已完成,或者当要传输的所有数据包大小等于 wMaxPacketSize 时,则无法确定传输是否结束。 为了使传输完成,必须发送零长度数据包 (ZLP)。短数据包和零长度数据包用于表示批量数据传输结束。 上述注意事项适用于原始批量数据传输 API,如 ux_device_class_cdc_acm_read()。
USB 设备存储类
USB 设备存储类允许在系统中嵌入的存储设备对 USB 主机可见。
USB 设备存储类本身不提供存储解决方案。 它仅接受并解释来自主机的 SCSI 请求。 当其中一个请求为读取或写入命令时,它将调用一个预定义的回调到实际存储设备处理程序,例如 ATA 设备驱动程序或闪存设备驱动程序。
初始化设备存储类时,会向包含所有必需信息的类提供指针结构。 下面给出了一个示例。
/* Initialize the storage class parameters to customize vendor strings. */
storage_parameter.ux_slave_class_storage_parameter_vendor_id
= demo_ux_system_slave_class_storage_vendor_id;
storage_parameter.ux_slave_class_storage_parameter_product_id
= demo_ux_system_slave_class_storage_product_id;
storage_parameter.ux_slave_class_storage_parameter_product_rev
= demo_ux_system_slave_class_storage_product_rev;
storage_parameter.ux_slave_class_storage_parameter_product_serial
= demo_ux_system_slave_class_storage_product_serial;
/* Store the number of LUN in this device storage instance: single LUN. */
storage_parameter.ux_slave_class_storage_parameter_number_lun = 1;
/* Initialize the storage class parameters for reading/writing to the Flash Disk. */
storage_parameter.ux_slave_class_storage_parameter_lun[0].ux_slave_class_storage_media_last_lba = 0x1e6bfe;
storage_parameter.ux_slave_class_storage_parameter_lun[0].ux_slave_class_storage_media_block_length = 512;
storage_parameter.ux_slave_class_storage_parameter_lun[0].ux_slave_class_storage_media_type = 0;
storage_parameter.ux_slave_class_storage_parameter_lun[0].ux_slave_class_storage_media_removable_flag = 0x80;
storage_parameter.ux_slave_class_storage_parameter_lun[0].ux_slave_class_storage_media_read_only_flag
= UX_FALSE;
storage_parameter.ux_slave_class_storage_parameter_lun[0].ux_slave_class_storage_media_read
= tx_demo_thread_flash_media_read;
storage_parameter.ux_slave_class_storage_parameter_lun[0].ux_slave_class_storage_media_write
= tx_demo_thread_flash_media_write;
storage_parameter.ux_slave_class_storage_parameter_lun[0].ux_slave_class_storage_media_status
= tx_demo_thread_flash_media_status;
/* A simple write caching support.
If this callback is assigned, host gets notification from caching page
that write caching is supported, and can send SYNCHRONIZE_CACHE to
flush cache. But caching options are not allowed to be changed. */
storage_parameter.ux_slave_class_storage_parameter_lun[0].
ux_slave_class_storage_media_flush =
tx_demo_thread_flash_media_flush;
/* Initialize the device storage class. The class is connected with interface 0 */
status = ux_device_stack_class_register(_ux_system_slave_class_storage_name,
ux_device_class_storage_entry, ux_device_class_storage_thread, 0, (VOID *)&storage_parameter);
在此示例中,通过将字符串指针分配给相应的参数来自定义驱动程序存储字符串。 如果将任何一个字符串指针留给 UX_NULL,则使用默认的 Azure RTOS 字符串。
在此示例中,将提供驱动器的最后一个块地址或 LBA 以及逻辑扇区大小。 LBA 是介质 (1) 中可用的扇区数。 常规存储介质中的块长度设置为 512。 对于光驱,可以将其设置为 2048。
应用程序需要传递三个回调函数指针,以允许存储类读取、写入和获取介质状态。
下面的示例展示了 read 和 write 函数的原型。
UINT media_read(
VOID *storage,
ULONG lun,
UCHAR *data_pointer,
ULONG number_blocks,
ULONG lba,
ULONG *media_status);
UINT media_write(
VOID *storage,
ULONG lun,
UCHAR *data_pointer,
ULONG number_blocks,
ULONG lba,
ULONG *media_status);
其中:
- storage 是存储类的实例。
- lun 是命令定向到的 LUN。
- data_pointer 是要用于读取或写入的缓冲区的地址。
- number_blocks 是要读取/写入的扇区数。
- lba 是要读取的扇区地址。
- media_status 应完全按照介质状态回调的返回值来填写。
返回值可以是 UX_SUCCESS 或 UX_ERROR 的值,表示操作成功或不成功。 这些操作不需要返回任何其他错误代码。 如果任何操作中出现错误,则存储类将调用状态回调函数。
此函数具有以下原型。
ULONG media_status(
VOID *storage,
ULONG lun,
ULONG media_id,
ULONG *media_status);
当前未使用调用参数 media_id,它应始终为 0。 将来,可以使用它来区分多个存储设备或具有多个 SCSI LUN 的存储设备。 此版本的存储类不支持多个具有多个 SCSI LUN 的存储类或存储设备实例。
返回值是一个可以采用以下格式的 SCSI 错误代码。
- Bits 0-7 Sense_key
- Bits 8-15 其他感知代码
- Bits 16-23 其他感知代码限定符
下表提供了可能的感知/ASC/ASCQ 组合。
感知密钥 | ASC | ASCQ | 说明 |
---|---|---|---|
00 | 00 | 00 | 无感知 |
01 | 17 | 01 | 重试后恢复的数据 |
01 | 18 | 00 | 使用 ECC 恢复数据 |
02 | 04 | 01 | 逻辑驱动器未就绪 - 准备就绪 |
02 | 04 | 02 | 逻辑驱动器未就绪 - 需要初始化 |
02 | 04 | 04 | 逻辑单元未就绪 - 正在进行格式设置 |
02 | 04 | FF | 逻辑驱动器未就绪 - 设备繁忙 |
02 | 06 | 00 | 找不到引用位置 |
02 | 08 | 00 | 逻辑单元通信失败 |
02 | 08 | 01 | 逻辑单元通信超时 |
02 | 08 | 80 | 逻辑单元通信超限 |
02 | 3A | 00 | 不存在介质 |
02 | 54 | 00 | USB 到主机系统接口故障 |
02 | 80 | 00 | 资源不足 |
02 | FF | FF | 未知错误 |
03 | 02 | 00 | 无查找完成 |
03 | 03 | 00 | 写入错误 |
03 | 10 | 00 | ID CRC 错误 |
03 | 11 | 00 | 未恢复的读取错误 |
03 | 12 | 00 | 找不到 ID 字段的地址标记 |
03 | 13 | 00 | 找不到数据字段的地址标记 |
03 | 14 | 00 | 找不到记录的实体 |
03 | 30 | 01 | 无法读取介质 - 未知格式 |
03 | 31 | 01 | 格式命令失败 |
04 | 40 | NN | 组件 NN(80H-FFH)的诊断故障 |
05 | 1A | 00 | 参数列表长度错误 |
05 | 20 | 00 | 无效的命令操作代码 |
05 | 21 | 00 | 逻辑块地址超出范围 |
05 | 24 | 00 | 命令包中的无效字段 |
05 | 25 | 00 | 逻辑单元不受支持 |
05 | 26 | 00 | 参数列表中的无效字段 |
05 | 26 | 01 | 参数不受支持 |
05 | 26 | 02 | 参数值无效 |
05 | 39 | 00 | 保存参数不受支持 |
06 | 28 | 00 | 未准备好进行转换 - 介质已更改 |
06 | 29 | 00 | 发生开机重置或总线设备重置 |
06 | 2F | 00 | 其他发起程序清除的命令 |
07 | 27 | 00 | 写入受保护的介质 |
0B | 4E | 00 | 尝试重叠的命令 |
应用程序可以实现另外两种可选的回调;一种用于响应 GET_STATUS_NOTIFICATION 命令,另一种用于响应 SYNCHRONIZE_CACHE 命令 。
如果应用程序要处理主机中的 GET_STATUS_NOTIFICATION 命令,则应使用以下原型实现回调。
UINT ux_slave_class_storage_media_notification(
VOID *storage,
ULONG lun,
ULONG media_id,
ULONG notification_class,
UCHAR **media_notification,
ULONG *media_notification_length);
其中:
- storage 是存储类的实例。
- 当前未使用 media_id。 notification_class 指定通知的类。
- media_notification 应由应用程序设置为包含通知响应的缓冲区。
- media_notification_length 应由应用程序设置为包含响应缓冲区的长度。
返回值指示命令是否成功 - 应为 UX_SUCCESS 或 UX_ERROR 。
如果应用程序未实现此回调,则在接收到 GET_STATUS_NOTIFICATION 命令后,USBX 将通知主机未实现该命令。
如果应用程序利用缓存从主机写入,则应处理 SYNCHRONIZE_CACHE 命令。 如果主机知道存储设备即将断开连接,则主机可以发送此命令,例如,在 Windows 中,如果右键单击工具栏中的闪存驱动器图标并选择“弹出 [存储设备名称]”,Windows 将向该设备发出 SYNCHRONIZE_CACHE 命令。
如果应用程序要处理主机中的 SYNCHRONIZE_CACHE 命令,则应使用以下原型实现回调。
UINT ux_slave_class_storage_media_flush(
VOID *storage,
ULONG lun,
ULONG number_blocks,
ULONG lba,
ULONG *media_status);
其中:
- storage 是存储类的实例。
- lun 参数指定命令所指向的 LUN。
- number_blocks 指定要同步的块的数目。
- lba 是要同步的第一个块的扇区地址。
- media_status 应完全按照介质状态回调的返回值来填写。
返回值指示命令是否成功 - 应为 UX_SUCCESS 或 UX_ERROR 。
多个 SCSI LUN
USBX 设备存储类支持多个 LUN。 因此,可以创建一个充当 CD-ROM 的存储设备,并同时创建一个闪存盘。 在这种情况下,初始化将略有不同。 下面是闪存盘和 CD-ROM 的示例:
/* Store the number of LUN in this device storage instance. */
storage_parameter.ux_slave_class_storage_parameter_number_lun = 2;
/* Initialize the storage class parameters for reading/writing to the Flash Disk. */
storage_parameter.ux_slave_class_storage_parameter_lun[0].ux_slave_class_storage_media_last_lba = 0x7bbff;
storage_parameter.ux_slave_class_storage_parameter_lun[0].ux_slave_class_storage_media_block_length = 512;
storage_parameter.ux_slave_class_storage_parameter_lun[0].ux_slave_class_storage_media_type = 0;
storage_parameter.ux_slave_class_storage_parameter_lun[0].ux_slave_class_storage_media_removable_flag = 0x80;
storage_parameter.ux_slave_class_storage_parameter_lun[0].ux_slave_class_storage_media_read = tx_demo_thread_flash_media_read;
storage_parameter.ux_slave_class_storage_parameter_lun[0].ux_slave_class_storage_media_write = tx_demo_thread_flash_media_write;
storage_parameter.ux_slave_class_storage_parameter_lun[0].ux_slave_class_storage_media_status = tx_demo_thread_flash_media_status;
/* Initialize the storage class LUN parameters for reading/writing to the CD-ROM. */
storage_parameter.ux_slave_class_storage_parameter_lun[1].ux_slave_class_storage_media_last_lba = 0x04caaf;
storage_parameter.ux_slave_class_storage_parameter_lun[1].ux_slave_class_storage_media_block_length = 2048;
storage_parameter.ux_slave_class_storage_parameter_lun[1].ux_slave_class_storage_media_type = 5;
storage_parameter.ux_slave_class_storage_parameter_lun[1].ux_slave_class_storage_media_removable_flag = 0x80;
storage_parameter.ux_slave_class_storage_parameter_lun[1].ux_slave_class_storage_media_read = tx_demo_thread_cdrom_media_read;
storage_parameter.ux_slave_class_storage_parameter_lun[1].ux_slave_class_storage_media_write = tx_demo_thread_cdrom_media_write;
storage_parameter.ux_slave_class_storage_parameter_lun[1].ux_slave_class_storage_media_status = tx_demo_thread_cdrom_media_status;
/* Initialize the device storage class for a Flash disk and CD-ROM. The class is connected with interface 0 */ status = ux_device_stack_class_register(_ux_system_slave_class_storage_name,ux_device_class_storage_entry,
ux_device_class_storage_thread,0, (VOID *) &storage_parameter);
写入缓存 SCSI LUN
USBX 设备存储类支持在 LUN 上进行写入缓存。
应用程序需要传递回调函数指针,以允许存储类报告写入缓存启用到主机,并在主机请求上执行刷新。
刷新回调函数具有以下原型:
UINT media_flush(VOID *storage, ULONG lun, ULONG number_blocks, ULONG lba, ULONG *media_status);
调用参数 number_blocks 和 lba 指定 LUN 上需要刷新的区域。
请注意,未分配回调时,主机不会收到写入缓存支持的通知,因此没有选项。 分配回调后,主机会收到启用写入缓存的通知,但不允许更改此设置。
USB 设备 CDC-ACM 类
USB 设备 CDC-ACM 类允许 USB 主机系统将设备作为串行设备进行通信。 此类基于 USB 标准,并且是 CDC 标准的子集。
符合 CDC-ACM 的设备框架需要由设备堆栈声明。 下面是一个示例。
unsigned char device_framework_full_speed[] = {
/*
Device descriptor 18 bytes
0x02 bDeviceClass: CDC class code
0x00 bDeviceSubclass: CDC class sub code 0x00 bDeviceProtocol: CDC Device protocol
idVendor & idProduct - https://www.linux-usb.org/usb.ids
*/
0x12, 0x01, 0x10, 0x01,
0xEF, 0x02, 0x01, 0x08,
0x84, 0x84, 0x00, 0x00,
0x00, 0x01, 0x01, 0x02,
0x03, 0x01,
/* Configuration 1 descriptor 9 bytes */
0x09, 0x02, 0x4b, 0x00, 0x02, 0x01, 0x00,0x40, 0x00,
/* Interface association descriptor. 8 bytes. */
0x08, 0x0b, 0x00,
0x02, 0x02, 0x02, 0x00, 0x00,
/* Communication Class Interface Descriptor Requirement. 9 bytes. */
0x09, 0x04, 0x00, 0x00,0x01,0x02, 0x02, 0x01, 0x00,
/* Header Functional Descriptor 5 bytes */
0x05, 0x24, 0x00,0x10, 0x01,
/* ACM Functional Descriptor 4 bytes */
0x04, 0x24, 0x02,0x0f,
/* Union Functional Descriptor 5 bytes */
0x05, 0x24, 0x06, 0x00,
/* Master interface */
0x01, /* Slave interface */
/* Call Management Functional Descriptor 5 bytes */
0x05, 0x24, 0x01,0x03, 0x01, /* Data interface */
/* Endpoint 1 descriptor 7 bytes */
0x07, 0x05, 0x83, 0x03,0x08, 0x00, 0xFF,
/* Data Class Interface Descriptor Requirement 9 bytes */
0x09, 0x04, 0x01, 0x00, 0x02,0x0A, 0x00, 0x00, 0x00,
/* First alternate setting Endpoint 1 descriptor 7 bytes*/
0x07, 0x05, 0x02,0x02,0x40, 0x00,0x00,
/* Endpoint 2 descriptor 7 bytes */
0x07, 0x05, 0x81,0x02,0x40, 0x00, 0x00,
};
CDC-ACM 类使用复合设备框架对接口(控件和数据)进行分组。 因此,在定义设备描述符时应保持谨慎。 USBX 依赖于接口关联描述符 (IAD) 来了解如何在内部绑定接口。 IAD 描述符应在接口之前声明,并包含 CDC-ACM 类的第一个接口和附加的接口数量。
CDC-ACM 类还使用联合功能描述符,该描述符执行与较新的 IAD 描述符相同的功能。 尽管出于历史原因和与主机端兼容的考虑,必须声明联合功能描述符,但它并不被 USBX 使用。
CDC-ACM 类的初始化需要以下参数。
/* Set the parameters for callback when insertion/extraction of a CDC device. */
parameter.ux_slave_class_cdc_acm_instance_activate = tx_demo_cdc_instance_activate;
parameter.ux_slave_class_cdc_acm_instance_deactivate = tx_demo_cdc_instance_deactivate;
parameter.ux_slave_class_cdc_acm_parameter_change = tx_demo_cdc_instance_parameter_change;
/* Initialize the device cdc class. This class owns both interfaces starting with 0. */
status = ux_device_stack_class_register(_ux_system_slave_class_cdc_acm_name,ux_device_class_cdc_acm_entry,
1,0, ¶meter);
定义的 2 个参数是指向用户应用程序的回调指针,这些指针在堆栈激活或停用此类时被调用。
定义的第三个参数是指向用户应用程序的回调指针,该指针在行编码或行状态参数发生更改时被调用。 例如,当主机请求将 DTR 状态改为 TRUE 时,回调被调用,在回调中用户应用程序可以通过 IOCTL 函数检查行状态,以了解主机是否可用于通信。
CDC-ACM 基于 USB-IF 标准,可被 MACO 和 Linux 操作系统自动识别。 在 Windows 平台上,此类需要在 Windows 10 之前的版本中使用 .inf 文件。 Windows 10 不需要任何 .inf 文件。 我们提供了 CDC-ACM 类的模板,可以在 usbx_windows_host_files 目录中找到它。 对于较新版本的 Windows,应使用 CDC_ACM_Template_Win7_64bit.inf 文件(Win10 除外)。 需要对此文件进行修改,以反映设备使用的 PID/VID。 将公司和产品注册到 USB-IF 后,该 PID/VID 将特定于最终客户。 在 inf 文件中,要修改的字段位于此处。
[DeviceList]
%DESCRIPTION%=DriverInstall, USB\VID_8484&PID_0000
[DeviceList.NTamd64]
%DESCRIPTION%=DriverInstall, USB\VID_8484&PID_0000
在 CDC-ACM 设备的设备框架中,PID/VID 存储在设备描述符(请参见上面声明的设备描述符)中。
USB 主机系统发现 USB CDC-ACM 设备后,它将装载一个串行类,并且该设备可以与任何串行终端程序一起使用。 有关参考信息,请参阅“主机操作系统”。
下面定义了 CDC-ACM 类 API 函数。
ux_device_class_cdc_acm_ioctl
在 CDC-ACM 接口上执行 IOCTL
原型
UINT ux_device_class_cdc_acm_ioctl (
UX_SLAVE_CLASS_CDC_ACM *cdc_acm,
ULONG ioctl_function,
VOID *parameter);
说明
应用程序需要执行对 cdc acm 接口的各种 ioctl 调用时,将调用此函数
参数
- cdc_acm:指向 cdc 类实例的指针。
- ioctl_function:要执行的 ioctl 函数。
- parameter:指向特定于 ioctl 的参数的指针。
返回值
- UX_SUCCESS (0x00) 此操作成功。
- UX_ERROR:(0xFF) 函数出错
示例
/* Start cdc acm callback transmission. */
status = _ux_device_class_cdc_acm_ioctl(cdc_acm_slave,
UX_SLAVE_CLASS_CDC_ACM_IOCTL_TRANSMISSION_START, &callback);
if(status != UX_SUCCESS)
return;
Ioctl 函数:
函数 | “值” |
---|---|
UX_SLAVE_CLASS_CDC_ACM_IOCTL_SET_LINE_CODING | 1 |
UX_SLAVE_CLASS_CDC_ACM_IOCTL_GET_LINE_CODING | 2 |
UX_SLAVE_CLASS_CDC_ACM_IOCTL_GET_LINE_STATE | 3 |
UX_SLAVE_CLASS_CDC_ACM_IOCTL_ABORT_PIPE | 4 |
UX_SLAVE_CLASS_CDC_ACM_IOCTL_SET_LINE_STATE | 5 |
UX_SLAVE_CLASS_CDC_ACM_IOCTL_TRANSMISSION_START | 6 |
UX_SLAVE_CLASS_CDC_ACM_IOCTL_TRANSMISSION_STOP | 7 |
ux_device_class_cdc_acm_ioctl:UX_SLAVE_CLASS_CDC_ACM_IOCTL_SET_LINE_CODING
在 CDC-ACM 接口上执行 IOCTL 设置行编码
原型
UINT ux_device_class_cdc_acm_ioctl (
UX_SLAVE_CLASS_CDC_ACM*cdc_acm,
ULONG ioctl_function,
VOID *parameter);
说明
应用程序需要设置行编码参数时,将调用此函数。
参数
- cdc_acm:指向 cdc 类实例的指针。
- ioctl_function:ux_SLAVE_CLASS_CDC_ACM_IOCTL_SET_LINE_CODING
- 参数:指向行参数结构的指针:
typedef struct UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_PARAMETER_STRUCT
{
ULONG ux_slave_class_cdc_acm_parameter_baudrate;
UCHAR ux_slave_class_cdc_acm_parameter_stop_bit;
UCHAR ux_slave_class_cdc_acm_parameter_parity;
UCHAR ux_slave_class_cdc_acm_parameter_data_bit;
} UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_PARAMETER;
返回值
UX_SUCCESS (0x00) 此操作成功。
示例
/* Change the line coding values. */
line_coding.ux_slave_class_cdc_acm_line_coding_dter = 9600;
line_coding.ux_slave_class_cdc_acm_line_coding_stop_bit =
UX_HOST_CLASS_CDC_ACM_LINE_CODING_STOP_BIT_15;
line_coding.ux_slave_class_cdc_acm_line_coding_parity =
UX_HOST_CLASS_CDC_ACM_LINE_CODING_PARITY_EVEN;
line_coding.ux_slave_class_cdc_acm_line_coding_data_bits = 5;
status = _ux_slave_class_cdc_acm_ioctl(cdc_acm,
UX_SLAVE_CLASS_CDC_ACM_IOCTL_SET_LINE_CODING, &line_coding);
if (status != UX_SUCCESS)
break;
ux_device_class_cdc_acm_ioctl:UX_SLAVE_CLASS_CDC_ACM_IOCTL_GET_LINE_CODING
在 CDC-ACM 接口上执行 IOCTL 获取行编码
原型
device_class_cdc_acm_ioctl (
UX_SLAVE_CLASS_CDC_ACM *cdc_acm,
ULONG ioctl_function,
VOID *parameter);
说明
应用程序需要获取行编码参数时,将调用此函数。
参数
- cdc_acm:指向 cdc 类实例的指针。
- ioctl_function:ux_SLAVE_CLASS_CDC_ACM_IOCTL_GET_ LINE_CODING
- 参数:指向行参数结构的指针:
typedef struct UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_PARAMETER_STRUCT
{
ULONG ux_slave_class_cdc_acm_parameter_baudrate;
UCHAR ux_slave_class_cdc_acm_parameter_stop_bit;
UCHAR ux_slave_class_cdc_acm_parameter_parity;
UCHAR ux_slave_class_cdc_acm_parameter_data_bit;
} UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_PARAMETER;
返回值
- UX_SUCCESS (0x00) 此操作成功。
示例
/* This is to retrieve BAUD rate. */
status = _ux_device_class_cdc_acm_ioctl(cdc_acm_slave,
UX_SLAVE_CLASS_CDC_ACM_IOCTL_GET_LINE_CODING, &line_coding);
/* Any error ? */
if (status == UX_SUCCESS)
{
/* Decode BAUD rate. */
switch (line_coding.ux_slave_class_cdc_acm_parameter_baudrate)
{
case 1200 :
status = tx_demo_thread_slave_cdc_acm_response("1200", 4);
break;
case 2400 :
status = tx_demo_thread_slave_cdc_acm_response("2400", 4);
break;
case 4800 :
status = tx_demo_thread_slave_cdc_acm_response("4800", 4);
break;
case 9600 :
status = tx_demo_thread_slave_cdc_acm_response("9600", 4);
break;
case 115200 :
status = tx_demo_thread_slave_cdc_acm_response("115200", 6);
break;
}
}
ux_device_class_cdc_acm_ioctl:UX_SLAVE_CLASS_CDC_ACM_IOCTL_GET_LINE_STATE
在 CDC-ACM 接口上执行 IOCTL 获取行状态
原型
UINT ux_device_class_cdc_acm_ioctl (
UX_SLAVE_CLASS_CDC_ACM*cdc_acm,
ULONG ioctl_function,
VOID *parameter);
说明
应用程序需要获取行状态参数时,将调用此函数。
参数
- cdc_acm:指向 cdc 类实例的指针。
- ioctl_function:ux_SLAVE_CLASS_CDC_ACM_IOCTL_GET_LINE_STATE
- 参数:指向行参数结构的指针:
typedef struct UX_SLAVE_CLASS_CDC_ACM_LINE_STATE_PARAMETER_STRUCT
{
UCHAR ux_slave_class_cdc_acm_parameter_rts;
UCHAR ux_slave_class_cdc_acm_parameter_dtr;
} UX_SLAVE_CLASS_CDC_ACM_LINE_STATE_PARAMETER;
返回值
- UX_SUCCESS (0x00) 此操作成功。
示例
/* This is to retrieve RTS state. */
status = _ux_device_class_cdc_acm_ioctl(cdc_acm_slave,
UX_SLAVE_CLASS_CDC_ACM_IOCTL_GET_LINE_STATE, &line_state);
/* Any error ? */
if (status == UX_SUCCESS)
{
/* Check state. */
if (line_state.ux_slave_class_cdc_acm_parameter_rts == UX_TRUE)
/* State is ON. */
status = tx_demo_thread_slave_cdc_acm_response("RTS ON", 6);
else
/* State is OFF. */
status = tx_demo_thread_slave_cdc_acm_response("RTS OFF", 7);
}
ux_device_class_cdc_acm_ioctl:UX_SLAVE_CLASS_CDC_ACM_IOCTL_SET_LINE_STATE
在CDC-ACM 接口上执行 IOCTL 设置行状态
原型
UINT ux_device_class_cdc_acm_ioctl (
UX_SLAVE_CLASS_CDC_ACM *cdc_acm,
ULONG ioctl_function,
VOID *parameter);
说明
应用程序需要获取行状态参数时,将调用此函数
参数
- cdc_acm:指向 cdc 类实例的指针。
- ioctl_function:ux_SLAVE_CLASS_CDC_ACM_IOCTL_SET_LINE_STATE
- 参数:指向行参数结构的指针:
typedef struct UX_SLAVE_CLASS_CDC_ACM_LINE_STATE_PARAMETER_STRUCT
{
UCHAR ux_slave_class_cdc_acm_parameter_rts;
UCHAR ux_slave_class_cdc_acm_parameter_dtr;
} UX_SLAVE_CLASS_CDC_ACM_LINE_STATE_PARAMETER;
返回值
- UX_SUCCESS (0x00) 此操作成功。
示例
/* This is to set RTS state. */
line_state.ux_slave_class_cdc_acm_parameter_rts = UX_TRUE;
status = _ux_device_class_cdc_acm_ioctl(cdc_acm_slave,
UX_SLAVE_CLASS_CDC_ACM_IOCTL_SET_LINE_STATE, &line_state);
/* If status is UX_SUCCESS, the operation was successful. */
ux_device_class_cdc_acm_ioctl:UX_SLAVE_CLASS_CDC_ACM_IOCTL_ABORT_PIPE
在 CDC-ACM 接口上执行 IOCTL 中止管道
原型
UINT ux_device_class_cdc_acm_ioctl (
UX_SLAVE_CLASS_CDC_ACM *cdc_acm,
ULONG ioctl_function,
VOID *parameter);
说明
应用程序需要中止管道时,将调用此函数。 例如,若要中止正在进行的写入,UX_SLAVE_CLASS_CDC_ACM_ENDPOINT_XMIT 应作为参数传递。
参数
- cdc_acm:指向 cdc 类实例的指针。
- ioctl_function:ux_SLAVE_CLASS_CDC_ACM_IOCTL_ABORT_PIPE
- 参数:管道方向:
UX_SLAVE_CLASS_CDC_ACM_ENDPOINT_XMIT 1
UX_SLAVE_CLASS_CDC_ACM_ENDPOINT_RCV 2
返回值
- UX_SUCCESS (0x00) 此操作成功。
- UX_ENDPOINT_HANDLE_UNKNOWN (0x53) 管道方向无效。
示例
/* This is to abort the Xmit pipe. */
status = _ux_device_class_cdc_acm_ioctl(cdc_acm_slave,
UX_SLAVE_CLASS_CDC_ACM_IOCTL_ABORT_PIPE,
UX_SLAVE_CLASS_CDC_ACM_ENDPOINT_XMIT);
/* If status is UX_SUCCESS, the operation was successful. */
ux_device_class_cdc_acm_ioctl:UX_SLAVE_CLASS_CDC_ACM_IOCTL_TRANSMISSION_START
在 CDC-ACM 接口上执行 IOCTL 传输启动
原型
UINT ux_device_class_cdc_acm_ioctl (
UX_SLAVE_CLASS_CDC_ACM *cdc_acm,
ULONG ioctl_function,
VOID *parameter);
说明
应用程序想要使用带回叫的传输时,将调用此函数。
参数
- cdc_acm:指向 cdc 类实例的指针。
- ioctl_function:ux_SLAVE_CLASS_CDC_ACM_IOCTL_TRANSMISSION_START
- 参数:指向开始传输参数结构的指针:
typedef struct UX_SLAVE_CLASS_CDC_ACM_CALLBACK_PARAMETER_STRUCT
{
UINT (*ux_device_class_cdc_acm_parameter_write_callback)(struct UX_SLAVE_CLASS_CDC_ACM_STRUCT *cdc_acm,
UINT status, ULONG length);
UINT (*ux_device_class_cdc_acm_parameter_read_callback)(struct UX_SLAVE_CLASS_CDC_ACM_STRUCT *cdc_acm,
UINT status, UCHAR *data_pointer, ULONG length);
} UX_SLAVE_CLASS_CDC_ACM_CALLBACK_PARAMETER;
返回值
- UX_SUCCESS (0x00) 此操作成功。
- UX_ERROR (0xFF) 传输已开始。
- UX_MEMORY_INSUFFICIENT (0x12) 内存分配失败。
示例
/* Set the callback parameter. */
callback.ux_device_class_cdc_acm_parameter_write_callback
= tx_demo_thread_slave_write_callback;
callback.ux_device_class_cdc_acm_parameter_read_callback
= tx_demo_thread_slave_read_callback;
/* Program the start of transmission. */
status = _ux_device_class_cdc_acm_ioctl(cdc_acm_slave,
UX_SLAVE_CLASS_CDC_ACM_IOCTL_TRANSMISSION_START, &callback);
/* If status is UX_SUCCESS, the operation was successful. */
ux_device_class_cdc_acm_ioctl:UX_SLAVE_CLASS_CDC_ACM_IOCTL_TRANSMISSION_STOP
在 CDC-ACM 接口上执行 IOCTL 传输停止
原型
UINT ux_device_class_cdc_acm_ioctl(
UX_SLAVE_CLASS_CDC_ACM *cdc_acm,
ULONG ioctl_function,
VOID *parameter);
说明
应用程序想要停止使用带回叫的传输时,将调用此函数。
参数
- cdc_acm:指向 cdc 类实例的指针。
- ioctl_function:ux_SLAVE_CLASS_CDC_ACM_IOCTL_TRANSMISSI ON_STOP
- 参数:未使用
返回值
- UX_SUCCESS (0x00) 此操作成功。
- UX_ERROR (0xFF) 无正在进行的传输。
示例
/* Program the stop of transmission. */
status = _ux_device_class_cdc_acm_ioctl(cdc_acm_slave,
UX_SLAVE_CLASS_CDC_ACM_IOCTL_TRANSMISSION_STOP, UX_NULL);
/* If status is UX_SUCCESS, the operation was successful. */
ux_device_class_cdc_acm_read
从 CDC-ACM 管道读取
原型
UINT ux_device_class_cdc_acm_read(
UX_SLAVE_CLASS_CDC_ACM *cdc_acm,
UCHAR *buffer,
ULONG requested_length,
ULONG *actual_length);
说明
应用程序需要从输出数据管道(从主机输出,从设备输入)读取数据时,将调用此函数。 它正在阻止。
注意
此函数从主机读取原始批量数据,因此它将保持挂起状态,直到缓冲区已满,或者主机通过短数据包(包括长度为零的数据包)终止传输。 有关详细信息,请参阅批量传输的一般注意事项。 函数按数据包从主机数据包读取字节。 如果准备的缓冲区大小小于数据包,并且主机发送的数据比预期多 (换句话说,则准备的缓冲区大小不是 USB 终结点最大数据包大小) 的倍数,则会发生缓冲区溢出。 为了避免此问题,建议的读取方法是 (USB 终结点最大数据包大小) 分配一个缓冲区。 这样,如果数据更多,下一次读取即可获取数据,并且不会发生缓冲区溢出。 如果数据较少,则当前读取可能会获取短数据包,而不是生成错误。
参数
- cdc_acm:指向 cdc 类实例的指针。
- buffer:将要存储数据的缓冲区地址。
- requested_length:所需的最大长度。
- actual_length:返回到缓冲区的长度。
返回值
- UX_SUCCESS (0x00) 此操作成功。
- UX_CONFIGURATION_HANDLE_UNKNOWN (0x51) 设备不再处于已配置状态。
- UX_TRANSFER_NO_ANSWER (0x22) 设备无应答。 传输挂起时,设备可能已断开连接。
示例
/* Read from the CDC class. */
status = ux_device_class_cdc_acm_read(cdc, buffer, UX_DEMO_BUFFER_SIZE, &actual_length);
if(status != UX_SUCCESS) return;
ux_device_class_cdc_acm_write
写入 CDC-ACM 管道
原型
UINT ux_device_class_cdc_acm_write(
UX_SLAVE_CLASS_CDC_ACM *cdc_acm,
UCHAR *buffer,
ULONG requested_length,
ULONG *actual_length);
说明
应用程序需要写入输入数据管道(从主机输入,从设备输出)时,将调用此函数。 它正在阻止。
参数
- cdc_acm:指向 cdc 类实例的指针。
- buffer:存储数据的缓冲区地址。
- requested_length:要写入的缓冲区的长度。
- actual_length:执行写入后返回到缓冲区的长度。
返回值
- UX_SUCCESS (0x00) 此操作成功。
- UX_CONFIGURATION_HANDLE_UNKNOWN (0x51) 设备不再处于已配置状态。
- UX_TRANSFER_NO_ANSWER (0x22) 设备无应答。 传输挂起时,设备可能已断开连接。
示例
/* Write to the CDC class bulk in pipe. */
status = ux_device_class_cdc_acm_write(cdc, buffer, UX_DEMO_BUFFER_SIZE, &actual_length);
if(status != UX_SUCCESS)
return;
ux_device_class_cdc_acm_write_with_callback
写入带回叫的 CDC-ACM 管道
原型
UINT ux_device_class_cdc_acm_write_with_callback(
UX_SLAVE_CLASS_CDC_ACM *cdc_acm,
UCHAR *buffer,
ULONG requested_length);
说明
应用程序需要写入输入数据管道(从主机输入,从设备输出)时,将调用此函数。 此函数是非阻塞的,完成将通过 UX_SLAVE_CLASS_CDC_ACM_IOCTL_TRANSMISSION_START中设置的回调来完成。
参数
- cdc_acm:指向 cdc 类实例的指针。
- buffer:存储数据的缓冲区地址。
- requested_length:要写入的缓冲区的长度。
- actual_length:执行写入后返回到缓冲区的长度
返回值
- UX_SUCCESS (0x00) 此操作成功。
- UX_CONFIGURATION_HANDLE_UNKNOWN (0x51) 设备不再处于已配置状态。
- UX_TRANSFER_NO_ANSWER (0x22) 设备无应答。 传输挂起时,设备可能已断开连接。
示例
/* Write to the CDC class bulk in pipe non blocking mode. */
status = ux_device_class_cdc_acm_write_with_callback(cdc, buffer, UX_DEMO_BUFFER_SIZE);
if(status != UX_SUCCESS)
return;
USB 设备 CDC-ECM 类
USB 设备 CDC-ECM 类允许 USB 主机系统作为以太网设备与设备进行通信。 此类基于 USB 标准,并且是 CDC 标准的子集。
符合 CDC-ECM 的设备框架需要由设备堆栈声明。 下面是一个示例。
unsigned char device_framework_full_speed[] = {
/* Device descriptor 18 bytes
0x02 bDeviceClass: CDC_ECM class code
0x06 bDeviceSubclass: CDC_ECM class sub code
0x00 bDeviceProtocol: CDC_ECM Device protocol
idVendor & idProduct - https://www.linux-usb.org/usb.ids
0x3939 idVendor: Azure RTOS test.
*/
0x12, 0x01, 0x10, 0x01,
0x02, 0x00, 0x00, 0x08,
0x39, 0x39, 0x08, 0x08, 0x00, 0x01, 0x01, 0x02, 03,0x01,
/* Configuration 1 descriptor 9 bytes. */
0x09, 0x02, 0x58, 0x00,0x02, 0x01, 0x00,0x40, 0x00,
/* Interface association descriptor. 8 bytes. */
0x08, 0x0b, 0x00, 0x02, 0x02, 0x06, 0x00, 0x00,
/* Communication Class Interface Descriptor Requirement 9 bytes */
0x09, 0x04, 0x00, 0x00,0x01,0x02, 0x06, 0x00, 0x00,
/* Header Functional Descriptor 5 bytes */
0x05, 0x24, 0x00, 0x10, 0x01,
/* ECM Functional Descriptor 13 bytes */
0x0D, 0x24, 0x0F, 0x04,0x00, 0x00, 0x00, 0x00, 0xEA, 0x05, 0x00,
0x00,0x00,
/* Union Functional Descriptor 5 bytes */
0x05, 0x24, 0x06, 0x00,0x01,
/* Endpoint descriptor (Interrupt) */
0x07, 0x05, 0x83, 0x03, 0x08, 0x00, 0x08,
/* Data Class Interface Descriptor Alternate Setting 0, 0 endpoints. 9 bytes */
0x09, 0x04, 0x01, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00,
/* Data Class Interface Descriptor Alternate Setting 1, 2 endpoints. 9 bytes */
0x09, 0x04, 0x01, 0x01, 0x02, 0x0A, 0x00, 0x00,0x00,
/* First alternate setting Endpoint 1 descriptor 7 bytes */
0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00,
/* Endpoint 2 descriptor 7 bytes */
0x07, 0x05, 0x81, 0x02, 0x40, 0x00,0x00
};
CDC-ECM 类使用与 CDC-ACM 非常相似的设备描述符方法,并且还需要 IAD 描述符。 有关定义,请参见 CDC-ACM 类。
除常规设备框架外,CDC-ECM 还需要特殊的字符串描述符。 下面给出了一个示例。
unsigned char string_framework[] = {
/* Manufacturer string descriptor: Index 1 - "Azure RTOS" */
0x09, 0x04, 0x01, 0x0c,
0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x20, 0x4c,
0x6f, 0x67, 0x69, 0x63,
/* Product string descriptor: Index 2 - "EL CDCECM Device" */
0x09, 0x04, 0x02, 0x10,
0x45, 0x4c, 0x20, 0x43, 0x44, 0x43, 0x45, 0x43,
0x4d, 0x20, 0x44, 0x65, 0x76, 0x69, 0x63, 0x64,
/* Serial Number string descriptor: Index 3 - "0001" */
0x09, 0x04, 0x03, 0x04,
0x30, 0x30, 0x30, 0x31,
/* MAC Address string descriptor: Index 4 - "001E5841B879" */
0x09, 0x04, 0x04, 0x0C,
0x30, 0x30, 0x31, 0x45, 0x35, 0x38,
0x34, 0x31, 0x42, 0x38, 0x37, 0x39
};
CDC-ECM 类使用 MAC 地址字符串描述符来响应主机查询,就像设备基于 TCP/IP 协议响应 MAC 地址一样。 它可以设置为设备选项,但必须在此处定义。
CDC-ECM 类的初始化如下所示。
/* Set the parameters for callback when insertion/extraction of a CDC device. Set to NULL.*/
cdc_ecm_parameter.ux_slave_class_cdc_ecm_instance_activate = UX_NULL;
cdc_ecm_parameter.ux_slave_class_cdc_ecm_instance_deactivate = UX_NULL;
/* Define a NODE ID. */
cdc_ecm_parameter.ux_slave_class_cdc_ecm_parameter_local_node_id[0] = 0x00;
cdc_ecm_parameter.ux_slave_class_cdc_ecm_parameter_local_node_id[1] = 0x1e;
cdc_ecm_parameter.ux_slave_class_cdc_ecm_parameter_local_node_id[2] = 0x58;
cdc_ecm_parameter.ux_slave_class_cdc_ecm_parameter_local_node_id[3] = 0x41;
cdc_ecm_parameter.ux_slave_class_cdc_ecm_parameter_local_node_id[4] = 0xb8;
cdc_ecm_parameter.ux_slave_class_cdc_ecm_parameter_local_node_id[5] = 0x78;
/* Define a remote NODE ID. */
cdc_ecm_parameter.ux_slave_class_cdc_ecm_parameter_remote_node_id[0] = 0x00;
cdc_ecm_parameter.ux_slave_class_cdc_ecm_parameter_remote_node_id[1] = 0x1e;
cdc_ecm_parameter.ux_slave_class_cdc_ecm_parameter_remote_node_id[2] = 0x58;
cdc_ecm_parameter.ux_slave_class_cdc_ecm_parameter_remote_node_id[3] = 0x41;
cdc_ecm_parameter.ux_slave_class_cdc_ecm_parameter_remote_node_id[4] = 0xb8;
cdc_ecm_parameter.ux_slave_class_cdc_ecm_parameter_remote_node_id[5] = 0x79;
/* Initialize the device cdc_ecm class. */
status = ux_device_stack_class_register(_ux_system_slave_class_cdc_ecm_name,
ux_device_class_cdc_ecm_entry, 1,0,&cdc_ecm_parameter);
此类的初始化在此练习中设置为 NULL 以便不执行回调,但实际上,激活和停用该初始化需要使用相同的函数回调。
后面的参数用于定义节点 ID。 CDC-ECM 需要 2 个节点:本地节点和远程节点。 本地节点指定设备的 MAC 地址,而远程节点指定主机的 MAC 地址。 远程节点必须与设备框架字符串描述符中声明的节点相同。
CDC-ECM 类中的内置 API 可用于通过两种方式传输数据,但由于用户应用程序将通过 NetX 与 USB 以太网设备进行通信,因此它们对应用程序是隐藏的。
USBX CDC-ECM 类与 Azure RTOS NetX 网络堆栈密切相关。 使用 NetX 和 USBX CDC-ECM 类的应用程序将以普通方式激活 NetX 网络堆栈,但还需要激活 USB 网络堆栈,如下所示。
/* Initialize the NetX system. */
nx_system_initialize();
/* Perform the initialization of the network driver. This will initialize the USBX network layer.*/
ux_network_driver_init();
USB 网络堆栈只需激活一次,且不特定于 CDCECM,但需要 NetX 服务的任何 USB 类都需要此堆栈。
CDC-ECM 类将由 MAC OS 和 Linux 主机识别。 但是,Microsoft Windows 不提供任何驱动程序来识别本地 CDC-ECM。 对于 Windows 平台而言,确实存在一些商业产品并且它们有自己的 .inf 文件。 但需要修改此文件(方法与 CDC-ACM inf 模板相同),以匹配 USB 网络设备的 PID/VID。
USB 设备 HID 类
USB 设备 HID 类允许 USB 主机系统使用特定的 HID 客户端功能连接到 HID 设备。
与主机端相比,USBX HID 设备类相对简单。 它与设备及其 HID 描述符的行为密切相关。
任何 HID 客户端都需要首先定义一个 HID 设备框架,如以下示例所示。
UCHAR device_framework_full_speed[] = {
/* Device descriptor */
0x12, 0x01, 0x10, 0x01, 0x00, 0x00, 0x00, 0x08,
0x81, 0x0A, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01,
/* Configuration descriptor */
0x09, 0x02, 0x22, 0x00, 0x01, 0x01, 0x00, 0xc0, 0x32,
/* Interface descriptor */
0x09, 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00,
/* HID descriptor */
0x09, 0x21, 0x10, 0x01, 0x21, 0x01, 0x22, 0x3f, 0x00,
/* Endpoint descriptor (Interrupt) */
0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x08
};
HID 框架包含一个接口描述符,用于描述 HID 类和 HID 设备子类。 HID 接口可以是独立的类,也可以是复合设备的一部分。 如果 HID 设备支持多个报表,则其 report_id 参数应设置为 UX_TRUE,否则应将其设置为 UX_FALSE。
可使用 USB 键盘初始化 HID 类,如以下所示。
/* Initialize the hid class parameters for a keyboard. */
hid_parameter.ux_device_class_hid_parameter_report_address = hid_keyboard_report;
hid_parameter.ux_device_class_hid_parameter_report_length = HID_KEYBOARD_REPORT_LENGTH;
hid_parameter.ux_device_class_hid_parameter_callback = tx_demo_thread_hid_callback;
hid_parameter.ux_device_class_hid_parameter_get_callback = tx_demo_thread_hid_get_callback;
hid_parameter.ux_device_class_hid_parameter_report_id = 0;
/* Initialize the device hid class. The class is connected with interface 0 */
status = ux_device_stack_class_register(_ux_system_slave_class_hid_name,
ux_device_class_hid_entry, 1,0,(VOID *)&hid_parameter);
if (status!=UX_SUCCESS)
return;
应用程序需要将 HID 报告描述符及其长度传递给 HID 类。 报告描述符是描述设备的项的集合。 有关 HID 语法的详细信息,请参阅 HID USB 类规范。
除报告描述符外,应用程序在发生 HID 事件时还会传递回调。
USBX HID 类支持主机中的以下标准 HID 命令。
命令名称 | “值” | 说明 |
---|---|---|
UX_DEVICE_CLASS_HID_COMMAND_GET_REPORT | 0x01 | 从设备获取报表,将调用回调以允许应用程序填充事件 |
UX_DEVICE_CLASS_HID_COMMAND_GET_IDLE | 0x02 | 获取中断终结点的空闲频率 |
UX_DEVICE_CLASS_HID_COMMAND_GET_PROTOCOL | 0x03 | 获取设备上运行的协议 |
UX_DEVICE_CLASS_HID_COMMAND_SET_REPORT | 0x09 | 设置对设备的报告,将调用回调以通知应用程序 |
UX_DEVICE_CLASS_HID_COMMAND_SET_IDLE | 0x0a | 设置中断终结点的空闲频率 |
UX_DEVICE_CLASS_HID_COMMAND_SET_PROTOCOL | 0x0b | 获取设备上运行的协议 |
获取和设置报告是 HID 最常用的命令,用于在主机和设备之间来回传输数据。 最常见的情况是,主机在控制终结点上发送数据,但也可以在中断终结点上或者通过发出 GET_REPORT 命令来接收数据以在控制终结点上提取数据。
HID 类可以通过使用 ux_device_class_hid_event_set 函数将数据发送回中断终结点上的主机。
ux_device_class_hid_event_set
为 HID 类设置事件
原型
UINT ux_device_class_hid_event_set(
UX_SLAVE_CLASS_HID *hid,
UX_SLAVE_CLASS_HID_EVENT *hid_event);
说明
应用程序需要将一个 HID 事件发送回主机时,将调用此函数。 该函数不会阻塞,它只是把报告放入循环队列,然后返回到应用程序。
参数
- hid:指向 hid 类实例的指针。
- hid_event:指向 hid 事件的结构的指针。
返回值
- UX_SUCCESS (0x00) 此操作成功。
- UX_ERROR (0xFF) 错误,循环队列中无可用空间。
示例
/* Insert a key into the keyboard event. Length is fixed to 8. */
hid_event.ux_device_class_hid_event_length = 8;
/* First byte is a modifier byte. */
hid_event.ux_device_class_hid_event_buffer[0] = 0;
/* Second byte is reserved. */
hid_event.ux_device_class_hid_event_buffer[1] = 0;
/* The 6 next bytes are keys. We only have one key here. */
hid_event.ux_device_class_hid_event_buffer[2] = key;
/* Set the keyboard event. */
ux_device_class_hid_event_set(hid, &hid_event);
在初始化 HID 类时定义的回调将执行与发送事件相反的任务。 它获取由主机发送的事件作为输入。 回调的原型如下所示。
hid_callback
通知:收到主机通过控制 SET_REPORT 请求发送的事件
原型
UINT hid_callback(
UX_SLAVE_CLASS_HID *hid,
UX_SLAVE_CLASS_HID_EVENT *hid_event);
说明
主机向应用程序发送 HID SET_REPORT 报告时,将调用此函数。
参数
- hid:指向 hid 类实例的指针。
- hid_event:指向 hid 事件的结构的指针。
示例
以下示例演示了如何解释 HID 键盘事件:
UINT tx_demo_thread_hid_callback(UX_SLAVE_CLASS_HID *hid, UX_SLAVE_CLASS_HID_EVENT *hid_event
{
/* There was an event. Analyze it. Is it NUM LOCK ? */
if (hid_event -> ux_device_class_hid_event_buffer[0] & HID_NUM_LOCK_MASK)
/* Set the Num lock flag. */
num_lock_flag = UX_TRUE;
else
/* Reset the Num lock flag. */
num_lock_flag = UX_FALSE;
/* There was an event. Analyze it. Is it CAPS LOCK ? */
if (hid_event -> ux_device_class_hid_event_buffer[0] & HID_CAPS_LOCK_MASK)
/* Set the Caps lock flag. */
caps_lock_flag = UX_TRUE;
else
/* Reset the Caps lock flag. */
caps_lock_flag = UX_FALSE;
}
hid_get_callback
通知:主机通过控制 GET_REPORT 请求请求事件
原型
UINT hid_get_callback(
UX_SLAVE_CLASS_HID *hid,
UX_SLAVE_CLASS_HID_EVENT *hid_event);
说明
当主机通过控制 GET_REPORT 请求请求事件时会调用此函数,应用程序必须在回调中填充事件结构,数据大小不得超过事件长度和最大事件缓冲区大小。
参数
- hid:指向 hid 类实例的指针。
- hid_event:指向 hid 事件的结构的指针。
示例
以下示例显示了如何准备事件:
UINT tx_demo_thread_hid_get_callback(UX_SLAVE_CLASS_HID *hid, UX_SLAVE_CLASS_HID_EVENT *hid_event)
{
/* Host is asking input for GET_REPORT, prepare event data here. */
/* Check if report ID is used,
* note in application the setting could be consistent so the check can be optimized.
*/
if (hid -> ux_device_class_hid_report_id)
{
/* First byte in buffer must be report ID, if report ID is required.
* See HID spec. for more details.
*/
event->ux_device_class_hid_event_report_id = demo_hid_event_report_id;
event->ux_device_class_hid_event_report_type = demo_hid_event_report_type;
if (demo_hid_event_length < UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH - 1)
demo_hid_event_length += 1;
event->ux_device_class_hid_event_length = demo_hid_event_length;
*(event->ux_device_class_hid_event_buffer) = (UCHAR)event->ux_device_class_hid_event_report_id;
_ux_utility_memory_copy(event->ux_device_class_hid_event_buffer + 1,
demo_hid_event_buffer,
event->ux_device_class_hid_event_length - 1);
}
else
/* A prepared event can be copied here. Note if copying data to event buffer,
* keep the total size inside UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH.
*/
_ux_utility_memory_copy(event, my_event, sizeof(UX_SLAVE_CLASS_HID_EVENT));
}