第 2 章 - USBX 设备类注意事项

USB 设备 RNDIS 类

USB 主机系统可以通过 USB 设备 RNDIS 类来与充当以太网设备的设备通信。 此类基于 Microsoft 专有的实现,并特定于 Windows 平台。

RNDIS 合规的设备框架需要由设备堆栈声明。 下面提供了一个示例。

unsigned char device_framework_full_speed[] = {
    /* VID: 0x04b4
    PID: 0x1127
    */

    /* Device Descriptor */
    0x12, /* bLength */
    0x01, /* bDescriptorType */
    0x10, 0x01, /* bcdUSB */
    0x02, /* bDeviceClass - CDC */
    0x00, /* bDeviceSubClass */
    0x00, /* bDeviceProtocol */
    0x40, /* bMaxPacketSize0 */
    0xb4, 0x04, /* idVendor */
    0x27, 0x11, /* idProduct */
    0x00, 0x01, /* bcdDevice */
    0x01, /* iManufacturer */
    0x02, /* iProduct */
    0x03, /* iSerialNumber */
    0x01, /* bNumConfigurations */

    /* Configuration Descriptor */
    0x09, /* bLength */
    0x02, /* bDescriptorType */
    0x38, 0x00, /* wTotalLength */
    0x02, /* bNumInterfaces */
    0x01, /* bConfigurationValue */
    0x00, /* iConfiguration */
    0x40, /* bmAttributes - Self-powered */
    0x00, /* bMaxPower */

    /* Interface Association Descriptor */
    0x08, /* bLength */
    0x0b, /* bDescriptorType */
    0x00, /* bFirstInterface */
    0x02, /* bInterfaceCount */
    0x02, /* bFunctionClass - CDC - Communication */
    0xff, /* bFunctionSubClass - Vendor Defined – In this case, RNDIS */
    0x00, /* bFunctionProtocol - No class specific protocol required */
    0x00, /* iFunction */

    /* Interface Descriptor */
    0x09, /* bLength */
    0x04, /* bDescriptorType */
    0x00, /* bInterfaceNumber */
    0x00, /* bAlternateSetting */
    0x01, /* bNumEndpoints */
    0x02, /* bInterfaceClass - CDC - Communication */
    0xff, /* bInterfaceSubClass - Vendor Defined – In this case, RNDIS */
    0x00, /* bInterfaceProtocol - No class specific protocol required */
    0x00, /* iInterface */

    /* Endpoint Descriptor */
    0x07, /* bLength */
    0x05, /* bDescriptorType */
    0x83, /* bEndpointAddress */
    0x03, /* bmAttributes - Interrupt */
    0x08, 0x00, /* wMaxPacketSize */
    0xff, /* bInterval */

    /* Interface Descriptor */
    0x09, /* bLength */
    0x04, /* bDescriptorType */
    0x01, /* bInterfaceNumber */
    0x00, /* bAlternateSetting */
    0x02, /* bNumEndpoints */
    0x0a, /* bInterfaceClass - CDC - Data */
    0x00, /* bInterfaceSubClass - Should be 0x00 */
    0x00, /* bInterfaceProtocol - No class specific protocol required */
    0x00, /* iInterface */

    /* Endpoint Descriptor */
    0x07, /* bLength */
    0x05, /* bDescriptorType */
    0x02, /* bEndpointAddress */
    0x02, /* bmAttributes - Bulk */
    0x40, 0x00, /* wMaxPacketSize */
    0x00, /* bInterval */

    /* Endpoint Descriptor */
    0x07, /* bLength */
    0x05, /* bDescriptorType */
    0x81, /* bEndpointAddress */
    0x02, /* bmAttributes - Bulk */
    0x40, 0x00, /* wMaxPacketSize */
    0x00, /* bInterval */
};

RNDIS 类对 CDC-ACM 和 CDC-ECM 使用非常类似的设备描述符方法,此外还需要 IAD 描述符。 有关设备描述符的定义和要求,请参阅 CDC-ACM 类。

RNDIS 类的激活方式如下所示。

/* Set the parameters for callback when insertion/extraction of a CDC device. Set to NULL.*/

parameter.ux_slave_class_rndis_instance_activate = UX_NULL;
parameter.ux_slave_class_rndis_instance_deactivate = UX_NULL;

/* Define a local NODE ID. */

parameter.ux_slave_class_rndis_parameter_local_node_id[0] = 0x00;
parameter.ux_slave_class_rndis_parameter_local_node_id[1] = 0x1e;
parameter.ux_slave_class_rndis_parameter_local_node_id[2] = 0x58;
parameter.ux_slave_class_rndis_parameter_local_node_id[3] = 0x41;
parameter.ux_slave_class_rndis_parameter_local_node_id[4] = 0xb8;
parameter.ux_slave_class_rndis_parameter_local_node_id[5] = 0x78;

/* Define a remote NODE ID. */

parameter.ux_slave_class_rndis_parameter_remote_node_id[0] = 0x00;
parameter.ux_slave_class_rndis_parameter_remote_node_id[1] = 0x1e;
parameter.ux_slave_class_rndis_parameter_remote_node_id[2] = 0x58;
parameter.ux_slave_class_rndis_parameter_remote_node_id[3] = 0x41;
parameter.ux_slave_class_rndis_parameter_remote_node_id[4] = 0xb8;
parameter.ux_slave_class_rndis_parameter_remote_node_id[5] = 0x79;

/* Set extra parameters used by the RNDIS query command with certain OIDs. */

parameter.ux_slave_class_rndis_parameter_vendor_id = 0x04b4 ;
parameter.ux_slave_class_rndis_parameter_driver_version = 0x1127;
ux_utility_memory_copy(parameter.ux_slave_class_rndis_parameter_vendor_description,
    "ELOGIC RNDIS", 12);

/* Initialize the device rndis class. This class owns both interfaces. */
status = ux_device_stack_class_register(_ux_system_slave_class_rndis_name,
    ux_device_class_rndis_entry, 1,0, &parameter);

对于 CDC-ECM,RNDIS 类需要 2 个节点(一个是本地节点,一个是远程节点),但不要求使用字符串描述符来描述远程节点。

不过,由于 Microsoft 专有的消息传递机制,还需要提供一些额外的参数。 首先必须传递供应商 ID。 同理,需要传递 RNDIS 的驱动程序版本。 此外,必须指定供应商字符串。

RNDIS 类具有用于双向传输数据的内置 API,但这些 API 对于应用程序是隐藏的,因为用户应用程序通过 NetX 来与 USB 以太网设备通信。

USBX RNDIS 类与 Azure RTOS NetX 网络堆栈密切相关。 使用 NetX 和 USBX RNDIS 类的应用程序将以普通方式激活 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 网络堆栈只需激活一次,且不特定于 RNDIS;但是,需要 NetX 服务的任何 USB 类都需要此堆栈。

MAC OS 和 Linux 主机无法识别 RNDIS 类,因为此类特定于 Microsoft 操作系统。 在 Windows 平台上,与设备描述符匹配的主机上需有一个 .inf 文件。 Microsoft 为 RNDIS 类提供了一个模板,可在 usbx_windows_host_files 目录中找到该模板。 对于较新的 Windows 版本,应使用文件 RNDIS_Template.inf。 需要对此文件进行修改,以反映设备使用的 PID/VID。 将公司和产品注册到 USB-IF 后,该 PID/VID 将特定于最终客户。 在 inf 文件中,要修改的字段位于此处。

[DeviceList]
%DeviceName%=DriverInstall, USB\\VID_xxxx&PID_yyyy&MI_00

[DeviceList.NTamd64]
%DeviceName%=DriverInstall, USB\\VID_xxxx&PID_yyyy&MI_00

在 RNDIS 设备的设备框架中,PID/VID 存储在设备描述符中(请参阅上面声明的设备描述符)

当 USB 主机系统发现 USB RNDIS 设备时,它会装载一个网络接口,然后,可以配合网络协议堆栈使用该设备。 有关参考信息,请参阅“主机操作系统”。

USB 设备 DFU 类

USB 主机系统可以通过 USB 设备 DFU 类基于主机应用程序更新设备固件。 DFU 类是一个 USB-IF 标准类。

USBX DFU 类相对较为简单。 它的设备描述符只需包含控制终结点,此外不需要包含其他任何内容。 大多数情况下,此类将嵌入到 USB 复合设备中。 设备可以是任何设备,例如存储设备或通信设备;添加的 DFU 接口可让主机知道设备能够动态更新其固件。

DFU 类的工作方式分为 3 个步骤。 首先,设备像往常一样使用导出的类进行装载。 主机(Windows 或 Linux)上的应用程序将执行 DFU 类,并发送一个将设备重置为 DFU 模式的请求。 该设备将在总线中消失较短的一段时间(此时间足以让主机和设备检测到 RESET 序列),重启后,该设备将以独占方式处于 DFU 模式,等待主机应用程序发送固件升级。 完成固件升级后,主机应用程序将重置设备,重新枚举后,该设备将恢复正常运行并使用新固件。

DFU 设备框架如下所示。

UCHAR device_framework_full_speed[] = {

    /* Device descriptor */
    0x12, 0x01, 0x10, 0x01, 0x00, 0x00, 0x00, 0x40,
    0x99, 0x99, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
    0x03, 0x01,

    /* Configuration descriptor */
    0x09, 0x02, 0x1b, 0x00, 0x01, 0x01, 0x00, 0xc0,
    0x32,

    /* Interface descriptor for DFU. */
    0x09, 0x04, 0x00, 0x00, 0x00, 0xFE, 0x01, 0x01, 0x00,

    /* Functional descriptor for DFU. */
    0x09, 0x21, 0x0f, 0xE8, 0x03, 0x40, 0x00, 0x00,
    0x01,

};

在此示例中,DFU 描述符不与任何其他类相关联。 它有一个简单的接口描述符,并且其上未附加其他终结点。 有一个功能描述符描述设备的 DFU 功能细节。

下面是 DFU 功能的描述。

名称 Offset 大小 type 说明
bmAttributes 2 1 位域 位 3:当设备收到 DFU_DETACH 请求时,将执行总线拆离-附加序列。 主机不得发出 USB 重置命令。 (bitWillDetach) 0 = 否,1 = 是。位 2:设备在“具体化”阶段之后可以通过 USB 进行通信。 (bitManifestationTolerant) 0 = 否,必须看到总线重置,1 = 是。位 1:支持上传。(bitCanUpload) 0 = 否,1 = 是。位 0:支持下载。(bitCanDnload) 0 = 否,1 = 是
wDetachTimeOut 3 2 数字 设备在收到 DFU_DETACH 请求后等待的时间,以毫秒为单位。 如果此时间已消逝但未发生 USB 重置,则设备将终止“重新配置”阶段并恢复正常运行。 这表示设备可以等待的最长时间(根据其计时器等)。 USBX 将此值设置为 1000 毫秒。
wTransferSize 5 2 数字 设备在每个控制写入操作中可以接受的最大字节数。 USBX 将此值设置为 64 字节。

DFU 类的声明如下:

/* Store the DFU parameters. */

dfu_parameter.ux_slave_class_dfu_parameter_instance_activate =
    tx_demo_thread_dfu_activate;

dfu_parameter.ux_slave_class_dfu_parameter_instance_deactivate =
    tx_demo_thread_dfu_deactivate;

dfu_parameter.ux_slave_class_dfu_parameter_read =
    tx_demo_thread_dfu_read;

dfu_parameter.ux_slave_class_dfu_parameter_write =
    tx_demo_thread_dfu_write;

dfu_parameter.ux_slave_class_dfu_parameter_get_status =
    tx_demo_thread_dfu_get_status;

dfu_parameter.ux_slave_class_dfu_parameter_notify =
    tx_demo_thread_dfu_notify;

dfu_parameter.ux_slave_class_dfu_parameter_framework =
    device_framework_dfu;

dfu_parameter.ux_slave_class_dfu_parameter_framework_length =
    DEVICE_FRAMEWORK_LENGTH_DFU;

/* Initialize the device dfu class. The class is connected with interface
1 on configuration 1. */
status = ux_device_stack_class_register(_ux_system_slave_class_dfu_name,
    ux_device_class_dfu_entry, 1, 0,
    (VOID *)&dfu_parameter);

if (status!=UX_SUCCESS) return;

DFU 类需要使用特定于目标的设备固件应用程序。 因此,它定义了多次回调来读取和写入固件块,并从固件更新应用程序获取状态。 DFU 类还具有一个通知回调函数,用于在固件传输开始和结束时告知应用程序。

下面是典型 DFU 应用程序流的说明。

DFU application flow

DFU 类的主要挑战在于如何使主机上的适当应用程序执行固件下载。 Microsoft 或 USB-IF 不提供这种应用程序。 有一些共享软件,它们可以在 Linux 上非常合理地运行,但在 Windows 上其运行合理程度要低一些。

在 Linux 上,用户可以使用以下链接中提供的 dfu-util:https://wiki.openmoko.org/wiki/Dfu-util。在以下链接中也可以找到有关 dfu-util 的大量信息:https://github.com/libusb/libusb/wiki

DFU 的 Linux 实现在主机与设备之间正确执行重置序列,因此设备无需执行此操作。 Linux 可以接受 bmAttributes bitWillDetach 为 0。 另一方面,Windows 要求设备执行重置。

在 Windows 上,USB 注册表必须能够将 USB 设备与其 PID/VID 和 USB 库相关联,然后,DFU 应用程序又可以使用该库。 可以使用以下链接中提供的免费实用工具 Zadig 轻松实现此目的:https://sourceforge.net/projects/libwdi/files/zadig/

首次运行 Zadig 时会显示以下屏幕:

Running Zadig for the first time

在设备列表中找到你的设备,并将其与 libusb Windows 驱动程序相关联。 这会将设备的 PID/VID 与 DFU 实用工具使用的 Windows USB 库绑定在一起。

若要运行 DFU 命令,只需将压缩的 DFU 实用工具解压缩到某个目录,并确保 libusb dll 也在同一目录中。 必须在 DOSBox 中的命令行下运行 DFU 实用工具。

首先,键入命令 dfu-util -l 确定是否列出了该设备。 如果未列出,请运行 Zadig 来确保设备将被列出并与 USB 库相关联。 应会看到如下所示的屏幕:

C:\usb specs\DFU\dfu-util-0.6>dfu-util -l dfu-util 0.6

Copyright 2005-2008 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2012 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Found Runtime: [0a5c:21bc] devnum=0, cfg=1, intf=3, alt=0, name="UNDEFINED"

下一步是准备要下载的文件。 USBX DFU 类不会对此文件执行任何验证,并且不知道此文件的内部格式。 此固件文件与目标密切相关,但与 DFU 和 USBX 不相关。

然后,可以键入以下命令来指示 dfu-util 发送文件:

dfu-util –R –t 64 -D file_to_download.hex

dfu-util 应显示文件下载过程,直到固件已完全下载。

USB 设备 PIMA 类(PTP 响应方)

USB 主机系统(发起方)可以通过 USB 设备 PIMA 类连接到

PIMA 设备(响应方)以传输媒体文件。 USBX PIMA 类遵从 USB-IF PIMA 15740 类(也称为 PTP(图片传输协议)类)的规范。

USBX 设备端 PIMA 类支持以下操作。

操作代码 说明
UX_DEVICE_CLASS_PIMA_OC_GET_DEVICE_INFO 0x1001 获取设备支持的操作和事件
UX_DEVICE_CLASS_PIMA_OC_OPEN_SESSION 0x1002 打开主机与设备之间的会话
UX_DEVICE_CLASS_PIMA_OC_CLOSE_SESSION 0x1003 关闭主机与设备之间的会话
UX_DEVICE_CLASS_PIMA_OC_GET_STORAGE_IDS 0x1004 返回设备的存储 ID。 USBX PIMA 仅使用一个存储 ID
UX_DEVICE_CLASS_PIMA_OC_GET_STORAGE_INFO 0x1005 返回有关存储对象的信息,例如最大容量和可用空间
UX_DEVICE_CLASS_PIMA_OC_GET_NUM_OBJECTS 0x1006 返回存储设备中包含的对象数
UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_HANDLES 0x1007 返回存储设备上对象的句柄数组
UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_INFO 0x1008 返回有关对象的信息,例如对象的名称、创建日期和修改日期
UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT 0x1009 返回与特定对象相关的数据
UX_DEVICE_CLASS_PIMA_OC_GET_THUMB 0x100A 发送有关对象的缩略图(如果有)
UX_DEVICE_CLASS_PIMA_OC_DELETE_OBJECT 0x100B 删除媒体上的对象
UX_DEVICE_CLASS_PIMA_OC_SEND_OBJECT_INFO 0x100C 将有关对象的信息发送到设备,以便在媒体上创建该对象
UX_DEVICE_CLASS_PIMA_OC_SEND_OBJECT 0x100D 将对象数据发送到设备
UX_DEVICE_CLASS_PIMA_OC_FORMAT_STORE 0x100F 清理设备媒体
UX_DEVICE_CLASS_PIMA_OC_RESET_DEVICE 0x0110 重置目标设备
操作代码 说明
UX_DEVICE_CLASS_PIMA_EC_CANCEL_TRANSACTION 0x4001 取消当前事务
UX_DEVICE_CLASS_PIMA_EC_OBJECT_ADDED 0x4002 已将对象添加到设备媒体,该对象可由主机检索。
UX_DEVICE_CLASS_PIMA_EC_OBJECT_REMOVED 0x4003 已从设备媒体中删除对象
UX_DEVICE_CLASS_PIMA_EC_STORE_ADDED 0x4004 已将媒体添加到设备
UX_DEVICE_CLASS_PIMA_EC_STORE_REMOVED 0x4005 已从设备中删除媒体
UX_DEVICE_CLASS_PIMA_EC_DEVICE_PROP_CHANGED 0x4006 已更改设备属性
UX_DEVICE_CLASS_PIMA_EC_OBJECT_INFO_CHANGED 0x4007 已更改对象信息
UX_DEVICE_CLASS_PIMA_EC_DEVICE_INFO_CHANGE 0x4008 已更改设备
UX_DEVICE_CLASS_PIMA_EC_REQUEST_OBJECT_TRANSFER 0x4009 设备请求从主机传输对象
UX_DEVICE_CLASS_PIMA_EC_STORE_FULL 0x400A 设备报告媒体已满
UX_DEVICE_CLASS_PIMA_EC_DEVICE_RESET 0x400B 设备报告它已重置
UX_DEVICE_CLASS_PIMA_EC_STORAGE_INFO_CHANGED 0x400C 已更改设备上的存储信息
UX_DEVICE_CLASS_PIMA_EC_CAPTURE_COMPLETE 0x400D 已完成捕获

USBX PIMA 设备类使用 TX 线程侦听来自主机的 PIMA 命令。

PIMA 命令由命令块、数据块和状态阶段组成。

函数 ux_device_class_pima_thread 向堆栈发送请求,以从主机端接收 PIMA 命令。 将解码 PIMA 命令并验证其内容。 如果命令块有效,则它会分支到相应的命令处理程序。

大多数 PIMA 命令仅在主机已打开会话时才能执行。 只有命令 UX_DEVICE_CLASS_PIMA_OC_GET_DEVICE_INFO 例外。 使用 USBX PIMA 实现时,在任意时间只能在发起方与响应方之间打开一个会话。 单个会话中的所有事务将会阻塞,在前一个事务完成之前无法开始新的事务。

PIMA 事务由 3 个阶段组成:命令阶段、可选的数据阶段,以及响应阶段。 如果存在数据阶段,此阶段只能单向进行。

发起方始终会确定 PIMA 操作流,但响应方可以向发起方反向发起事件,以告知在会话期间发生的状态更改。

下图显示了主机与 PIMA 设备类之间的数据对象传输。

PIMA transactions

PIMA 设备类的初始化

在初始化期间,PIMA 设备类需要应用程序提供的一些参数。

以下参数描述设备和存储信息。

  • ux_device_class_pima_manufacturer
  • ux_device_class_pima_model
  • ux_device_class_pima_device_version
  • ux_device_class_pima_serial_number
  • ux_device_class_pima_storage_id
  • ux_device_class_pima_storage_type
  • ux_device_class_pima_storage_file_system_type
  • ux_device_class_pima_storage_access_capability
  • ux_device_class_pima_storage_max_capacity_low
  • ux_device_class_pima_storage_max_capacity_high
  • ux_device_class_pima_storage_free_space_low
  • ux_device_class_pima_storage_free_space_high
  • ux_device_class_pima_storage_free_space_image
  • ux_device_class_pima_storage_description
  • ux_device_class_pima_storage_volume_label

PIMA 类还要求在应用程序中注册回调,以告知应用程序发生了某些事件,或者从/向本地媒体检索/存储数据。 回调如下所示。

  • ux_device_class_pima_object_number_get
  • ux_device_class_pima_object_handles_get
  • ux_device_class_pima_object_info_get
  • ux_device_class_pima_object_data_get
  • ux_device_class_pima_object_info_send
  • ux_device_class_pima_object_data_send
  • ux_device_class_pima_object_delete

以下示例演示如何初始化 PIMA 的客户端。 此示例使用 Pictbridge 作为 PIMA 的客户端。

/* Initialize the first XML object valid in the pictbridge instance.

Initialize the handle, type and file name.

The storage handle and the object handle have a fixed value of 1 in our implementation. */

object_info = pictbridge -> ux_pictbridge_object_client;

object_info -> ux_device_class_pima_object_format = UX_DEVICE_CLASS_PIMA_OFC_SCRIPT;
object_info -> ux_device_class_pima_object_storage_id = 1;
object_info -> ux_device_class_pima_object_handle_id = 2;

ux_utility_string_to_unicode(_ux_pictbridge_ddiscovery_name,
    object_info -> ux_device_class_pima_object_filename);

/* Initialize the head and tail of the notification round robin buffers.
   At first, the head and tail are pointing to the beginning of the array.
*/

pictbridge -> ux_pictbridge_event_array_head =
    pictbridge -> ux_pictbridge_event_array;

pictbridge -> ux_pictbridge_event_array_tail =
    pictbridge -> ux_pictbridge_event_array;

pictbridge -> ux_pictbridge_event_array_end =
    pictbridge -> ux_pictbridge_event_array +
    UX_PICTBRIDGE_MAX_EVENT_NUMBER;

/* Initialialize the pima device parameter. */
pictbridge -> ux_pictbridge_pima_parameter.ux_device_class_pima_parameter_manufacturer =
    pictbridge -> ux_pictbridge_dpslocal.ux_pictbridge_devinfo_vendor_name;

pictbridge -> ux_pictbridge_pima_parameter.ux_device_class_pima_parameter_model =
    pictbridge -> ux_pictbridge_dpslocal.ux_pictbridge_devinfo_product_name;

pictbridge -> ux_pictbridge_pima_parameter.ux_device_class_pima_parameter_serial_number =
    pictbridge -> ux_pictbridge_dpslocal.ux_pictbridge_devinfo_serial_no;

pictbridge -> ux_pictbridge_pima_parameter.ux_device_class_pima_parameter_storage_id = 1;

pictbridge -> ux_pictbridge_pima_parameter.ux_device_class_pima_parameter_storage_type =
    UX_DEVICE_CLASS_PIMA_STC_FIXED_RAM;

pictbridge -> ux_pictbridge_pima_parameter.ux_device_class_pima_parameter_storage_file_system_type =
    UX_DEVICE_CLASS_PIMA_FSTC_GENERIC_FLAT;

pictbridge -> ux_pictbridge_pima_parameter.ux_device_class_pima_parameter_storage_access_capability =
    UX_DEVICE_CLASS_PIMA_AC_READ_WRITE;

pictbridge -> ux_pictbridge_pima_parameter.ux_device_class_pima_parameter_storage_max_capacity_low =
    pictbridge -> ux_pictbridge_dpslocal.ux_pictbridge_devinfo_storage_size;

pictbridge -> ux_pictbridge_pima_parameter.ux_device_class_pima_parameter_storage_max_capacity_high = 0;

pictbridge -> ux_pictbridge_pima_parameter.ux_device_class_pima_parameter_storage_free_space_low =
    pictbridge -> ux_pictbridge_dpslocal.ux_pictbridge_devinfo_storage_size;

pictbridge -> ux_pictbridge_pima_parameter.ux_device_class_pima_parameter_storage_free_space_high = 0;

pictbridge -> ux_pictbridge_pima_parameter.ux_device_class_pima_parameter_storage_free_space_image = 0;

pictbridge -> ux_pictbridge_pima_parameter.ux_device_class_pima_parameter_storage_description =
    _ux_pictbridge_volume_description;

pictbridge -> ux_pictbridge_pima_parameter.ux_device_class_pima_parameter_storage_volume_label =
    _ux_pictbridge_volume_label;

pictbridge -> ux_pictbridge_pima_parameter.ux_device_class_pima_parameter_object_number_get =
    ux_pictbridge_dpsclient_object_number_get;

pictbridge -> ux_pictbridge_pima_parameter.ux_device_class_pima_parameter_object_handles_get =
    ux_pictbridge_dpsclient_object_handles_get;

pictbridge -> ux_pictbridge_pima_parameter.ux_device_class_pima_parameter_object_info_get =
    ux_pictbridge_dpsclient_object_info_get;

pictbridge -> ux_pictbridge_pima_parameter.ux_device_class_pima_parameter_object_data_get =
    ux_pictbridge_dpsclient_object_data_get;

pictbridge -> ux_pictbridge_pima_parameter.ux_device_class_pima_parameter_object_info_send =
    ux_pictbridge_dpsclient_object_info_send;

pictbridge -> ux_pictbridge_pima_parameter.ux_device_class_pima_parameter_object_data_send =
    ux_pictbridge_dpsclient_object_data_send;

pictbridge -> ux_pictbridge_pima_parameter.ux_device_class_pima_parameter_object_delete =
    ux_pictbridge_dpsclient_object_delete;

/* Store the instance owner. */
pictbridge -> ux_pictbridge_pima_parameter.ux_device_class_pima_parameter_application =
    (VOID *) pictbridge;

/* Initialize the device pima class. The class is connected with interface 0 */

status = ux_device_stack_class_register(_ux_system_slave_class_pima_name,
    ux_device_class_pima_entry, 1, 0, (VOID *)&pictbridge -> ux_pictbridge_pima_parameter);

/* Check status. */
if (status != UX_SUCCESS)

ux_device_class_pima_object_add

添加对象并将事件发送到主机

原型

UINT ux_device_class_pima_object_add(
    UX_SLAVE_CLASS_PIMA *pima, 
    ULONG object_handle);

说明

当 PIMA 类需要添加对象并告知主机时,将调用此函数。

参数

  • pima:指向 PIMA 类实例的指针
  • object_handle:对象的句柄。

示例

/* Send the notification to the host that an object has been added. */

status = ux_device_class_pima_object_add(pima, UX_PICTBRIDGE_OBJECT_HANDLE_CLIENT_REQUEST);

ux_device_class_pima_object_number_get

从应用程序获取对象数

原型

UINT ux_device_class_pima_object_number_get(
    UX_SLAVE_CLASS_PIMA *pima, 
    ULONG *object_number);

说明

当 PIMA 类需要检索本地系统中的对象数并将其发回到主机时,将调用此函数。

参数

  • pima:指向 PIMA 类实例的指针
  • object_number:要返回的对象数的地址

示例

UINT ux_pictbridge_dpsclient_object_number_get(UX_SLAVE_CLASS_PIMA *pima, ULONG *number_objects)
{
    /* We force the number of objects to be 1 only here. This will be the XML scripts. */
    *number_objects = 1;
    return(UX_SUCCESS);
}

ux_device_class_pima_object_handles_get

返回对象句柄数组

原型

UINT **ux_device_class_pima_object_handles_get**(
    UX_SLAVE_CLASS_PIMA_STRUCT *pima,
    ULONG object_handles_format_code,
    ULONG object_handles_association,
    ULONG *object_handles_array,
    ULONG object_handles_max_number);

说明

当 PIMA 类需要检索本地系统中的对象句柄数组并将其发回到主机时,将调用此函数。

参数

  • pima:指向 PIMA 类实例的指针。
  • object_handles_format_code:句柄的格式代码
  • object_handles_association:对象关联代码
  • object_handle_array:将句柄存储到的地址
  • object_handles_max_number:数组中的最大句柄数

示例

UINT ux_pictbridge_dpsclient_object_handles_get(UX_SLAVE_CLASS_PIMA *pima,
    ULONG object_handles_format_code,
    ULONG object_handles_association,
    ULONG *object_handles_array,
    ULONG object_handles_max_number)
{

    UX_PICTBRIDGE *pictbridge;
    UX_SLAVE_CLASS_PIMA_OBJECT *object_info;

    /* Get the pointer to the Pictbridge instance. */
    pictbridge = (UX_PICTBRIDGE *) pima -> ux_device_class_pima_application;

    /* Set the pima pointer to the pictbridge instance. */
    pictbridge -> ux_pictbridge_pima = (VOID *) pima;

    /* We say we have one object but the caller might specify different format code and associations. */
    object_info = pictbridge -> ux_pictbridge_object_client;

    /* Insert in the array the number of found handles so far: 0. */
    ux_utility_long_put((UCHAR *)object_handles_array, 0);

    /* Check the type demanded. */

    if (object_handles_format_code == 0 || object_handles_format_code ==
        0xFFFFFFFF || object_info -> ux_device_class_pima_object_format ==
        object_handles_format_code)
    {
        /* Insert in the array the number of found handles. This handle is for the client XML script. */
        ux_utility_long_put((UCHAR *)object_handles_array, 1);

        /* Adjust the array to point after the number of elements. */
        object_handles_array++;

        /* We have a candicate. Store the handle. */
        ux_utility_long_put((UCHAR *)object_handles_array, object_info ->
            ux_device_class_pima_object_handle_id);
    }

    return(UX_SUCCESS);
}

ux_device_class_pima_object_info_get

返回对象信息

原型

UINT ux_device_class_pima_object_info_get(
    struct UX_SLAVE_CLASS_PIMA_STRUCT *pima,
    ULONG object_handle, 
    UX_SLAVE_CLASS_PIMA_OBJECT **object);

说明

当 PIMA 类需要检索本地系统中的对象句柄数组并将其发回到主机时,将调用此函数。

参数

  • pima:指向 PIMA 类实例的指针。
  • object_handles:对象的句柄
  • object:对象指针地址

示例

UINT ux_pictbridge_dpsclient_object_info_get(UX_SLAVE_CLASS_PIMA *pima,
    ULONG object_handle, UX_SLAVE_CLASS_PIMA_OBJECT **object)
{
    UX_PICTBRIDGE *pictbridge;
    UX_SLAVE_CLASS_PIMA_OBJECT *object_info;
    /* Get the pointer to the Pictbridge instance. */
    pictbridge = (UX_PICTBRIDGE *)pima -> ux_device_class_pima_application;

    /* Check the object handle. If this is handle 1 or 2 , we need to return the XML script object.
       If the handle is not 1 or 2, this is a JPEG picture or other object to be printed. */
    if ((object_handle == UX_PICTBRIDGE_OBJECT_HANDLE_HOST_RESPONSE) ||
        (object_handle == UX_PICTBRIDGE_OBJECT_HANDLE_CLIENT_REQUEST)) {

        /* Check what XML object is requested. It is either a request script or a response. */
        if (object_handle == UX_PICTBRIDGE_OBJECT_HANDLE_HOST_RESPONSE)
            object_info = (UX_SLAVE_CLASS_PIMA_OBJECT *) pictbridge -> ux_pictbridge_object_host;
        else
            object_info = (UX_SLAVE_CLASS_PIMA_OBJECT *) pictbridge ->
                ux_pictbridge_object_client;
    } else
        /* Get the object info from the job info structure. */
        object_info = (UX_SLAVE_CLASS_PIMA_OBJECT *) pictbridge ->
            ux_pictbridge_jobinfo.ux_pictbridge_jobinfo_object;
    /* Return the pointer to this object. */
    *object = object_info;

    /* We are done. */
    return(UX_SUCCESS);
}

ux_device_class_pima_object_data_get

返回对象数据

原型

UINT ux_device_class_pima_object_info_get(
    UX_SLAVE_CLASS_PIMA *pima,
    ULONG object_handle,
    UCHAR *object_buffer,
    ULONG object_offset,
    ULONG object_length_requested,
    ULONG *object_actual_length);

说明

当 PIMA 类需要检索本地系统中的对象数据并将其发回到主机时,将调用此函数。

参数

  • pima:指向 PIMA 类实例的指针。
  • object_handle:对象的句柄
  • object_buffer:对象缓冲区地址
  • object_length_requested:客户端向应用程序请求的对象数据长度
  • object_actual_length:应用程序返回的对象数据长度

示例

UINT ux_pictbridge_dpsclient_object_data_get(UX_SLAVE_CLASS_PIMA *pima,
    ULONG object_handle, UCHAR *object_buffer, ULONG object_offset,
    ULONG object_length_requested, ULONG *object_actual_length)
{

    UX_PICTBRIDGE *pictbridge;
    UX_SLAVE_CLASS_PIMA_OBJECT *object_info;
    UCHAR *pima_object_buffer;
    ULONG actual_length;
    UINT status;

    /* Get the pointer to the Pictbridge instance. */
    pictbridge = (UX_PICTBRIDGE *)pima -> ux_device_class_pima_application;

    /* Check the object handle. If this is handle 1 or 2 , we need to return the XML script object.
       If the handle is not 1 or 2, this is a JPEG picture or other object to be printed. */
    if ((object_handle == UX_PICTBRIDGE_OBJECT_HANDLE_HOST_RESPONSE) ||
        (object_handle == UX_PICTBRIDGE_OBJECT_HANDLE_CLIENT_REQUEST))
    {
        /* Check what XML object is requested. It is either a request script or a response. */
        if (object_handle == UX_PICTBRIDGE_OBJECT_HANDLE_HOST_RESPONSE)
            object_info = (UX_SLAVE_CLASS_PIMA_OBJECT *) pictbridge ->
                ux_pictbridge_object_host;
        else
            object_info = (UX_SLAVE_CLASS_PIMA_OBJECT *) pictbridge ->
                ux_pictbridge_object_client;

       /* Is this the corrent handle ? */
       if (object_info -> ux_device_class_pima_object_handle_id == object_handle)
       {
           /* Get the pointer to the object buffer. */
           pima_object_buffer = object_info -> ux_device_class_pima_object_buffer;

           /* Copy the demanded object data portion. */
           ux_utility_memory_copy(object_buffer, pima_object_buffer +
               object_offset, object_length_requested);

           /* Update the length requested. for a demo, we do not do any checking. */
           *object_actual_length = object_length_requested;

           /* What cycle are we in ? */
           if (pictbridge -> ux_pictbridge_host_client_state_machine &
               UX_PICTBRIDGE_STATE_MACHINE_HOST_REQUEST)
            {
                /* Check if we are blocking for a client request. */
                if (pictbridge -> ux_pictbridge_host_client_state_machine &
                    UX_PICTBRIDGE_STATE_MACHINE_CLIENT_REQUEST_PENDING)

                    /* Yes we are pending, send an event to release the pending request. */
                    ux_utility_event_flags_set(&pictbridge ->
                        ux_pictbridge_event_flags_group,
                        UX_PICTBRIDGE_EVENT_FLAG_STATE_MACHINE_READY, TX_OR);

               /* Since we are in host request, this indicates we are done with the cycle. */
               pictbridge -> ux_pictbridge_host_client_state_machine =
                   UX_PICTBRIDGE_STATE_MACHINE_IDLE;

            }

            /* We have copied the requested data. Return OK. */
            return(UX_SUCCESS);

        }
    }
    else
    {

        /* Get the object info from the job info structure. */
        object_info = (UX_SLAVE_CLASS_PIMA_OBJECT *) pictbridge ->
            ux_pictbridge_jobinfo.ux_pictbridge_jobinfo_object;

        /* Obtain the data from the application jobinfo callback. */
        status = pictbridge -> ux_pictbridge_jobinfo.
            ux_pictbridge_jobinfo_object_data_read(pictbridge, object_buffer, object_offset,
            object_length_requested, &actual_length);

        /* Save the length returned. */
        *object_actual_length = actual_length;

        /* Return the application status. */
        return(status);

    }

    /* Could not find the handle. */

    return(UX_DEVICE_CLASS_PIMA_RC_INVALID_OBJECT_HANDLE);
}

ux_device_class_pima_object_info_send

主机发送对象信息

原型

UINT ux_device_class_pima_object_info_send(
    UX_SLAVE_CLASS_PIMA *pima,
    UX_SLAVE_CLASS_PIMA_OBJECT *object, 
    ULONG *object_handle);

说明

当 PIMA 类需要接收本地系统中的对象信息以便将来进行存储时,将调用此函数。

参数

  • pima:指向 PIMA 类实例的指针
  • object:指向对象的指针
  • object_handle:对象的句柄

示例

UINT ux_pictbridge_dpsclient_object_info_send(UX_SLAVE_CLASS_PIMA *pima,
    UX_SLAVE_CLASS_PIMA_OBJECT *object, ULONG *object_handle)
{
    UX_PICTBRIDGE *pictbridge;
    UX_SLAVE_CLASS_PIMA_OBJECT *object_info; UCHAR
    string_discovery_name[UX_PICTBRIDGE_MAX_FILE_NAME_SIZE];

    /* Get the pointer to the Pictbridge instance. */
    pictbridge = (UX_PICTBRIDGE *)pima -> ux_device_class_pima_application;

    /* We only have one object. */
    object_info = (UX_SLAVE_CLASS_PIMA_OBJECT *) pictbridge ->
        ux_pictbridge_object_host;

    /* Copy the demanded object info set. */
    ux_utility_memory_copy(object_info, object,
        UX_SLAVE_CLASS_PIMA_OBJECT_DATA_LENGTH);

    /* Store the object handle. In Pictbridge we only receive XML scripts so the handle is hardwired to 1. */
    object_info -> ux_device_class_pima_object_handle_id = 1;
    *object_handle = 1;

    /* Check state machine. If we are in discovery pending mode, check file name of this object. */
    if (pictbridge -> ux_pictbridge_discovery_state ==
        UX_PICTBRIDGE_DPSCLIENT_DISCOVERY_PENDING)
    {
        /* We are in the discovery mode. Check for file name. It must match
           HDISCVRY.DPS in Unicode mode. */

        /* Check if this is a script. */
        if (object_info -> ux_device_class_pima_object_format ==
            UX_DEVICE_CLASS_PIMA_OFC_SCRIPT)
        {

            /* Yes this is a script. We need to search for the HDISCVRY.DPS file name. Get the file name in a ascii format. */
            ux_utility_unicode_to_string(object_info ->
                ux_device_class_pima_object_filename,
                string_discovery_name);

            /* Now, compare it to the HDISCVRY.DPS file name. Check length first. */
            if (ux_utility_string_length_get(_ux_pictbridge_hdiscovery_name)
                == ux_utility_string_length_get(string_discovery_name))
            {

                /* So far, the length of name of the files are the same. Compare names now. */
                if(ux_utility_memory_compare(_ux_pictbridge_hdiscovery_name,
                    string_discovery_name,
                    ux_utility_string_length_get(string_discovery_name)) == UX_SUCCESS)
                {
                    /* We are done with discovery of the printer. We can now send notifications when the camera wants to print an object. */
                    pictbridge -> ux_pictbridge_discovery_state =
                        UX_PICTBRIDGE_DPSCLIENT_DISCOVERY_COMPLETE;

                    /* Set an event flag if the application is listening. */
                    ux_utility_event_flags_set(&pictbridge ->
                        ux_pictbridge_event_flags_group,
                        UX_PICTBRIDGE_EVENT_FLAG_DISCOVERY, TX_OR);

                    /* There is no object during th discovery cycle. */
                    return(UX_SUCCESS);
                }
            }
        }
    }
    /* What cycle are we in ? */
    if (pictbridge -> ux_pictbridge_host_client_state_machine ==
        UX_PICTBRIDGE_STATE_MACHINE_IDLE)

        /* Since we are in idle state, we must have received a request from the host. */
        pictbridge -> ux_pictbridge_host_client_state_machine =
            UX_PICTBRIDGE_STATE_MACHINE_HOST_REQUEST;

    /* We have copied the requested data. Return OK. */
    return(UX_SUCCESS);
}

ux_device_class_pima_object_data_send

主机发送对象数据

原型

UINT ux_device_class_pima_object_data_send(
    UX_SLAVE_CLASS_PIMA *pima,
    ULONG object_handle, 
    ULONG phase, 
    UCHAR *object_buffer,
    ULONG object_offset, 
    ULONG object_length);

说明

当 PIMA 类需要接收本地系统中的对象数据以便进行存储时,将调用此函数。

参数

  • pima:指向 PIMA 类实例的指针
  • object_handle:对象的句柄
  • phase:传输阶段(活动或完成)
  • object_buffer:对象缓冲区地址
  • object_offset:数据的地址
  • object_length:应用程序发送的对象数据长度

示例

UINT ux_pictbridge_dpsclient_object_data_send(UX_SLAVE_CLASS_PIMA *pima,
    ULONG object_handle,
    ULONG phase,
    UCHAR *object_buffer,
    ULONG object_offset,
    ULONG object_length)
{
    UINT status;
    UX_PICTBRIDGE *pictbridge;
    UX_SLAVE_CLASS_PIMA_OBJECT *object_info;
    ULONG event_flag;
    UCHAR *pima_object_buffer;

    /* Get the pointer to the Pictbridge instance. */
    pictbridge = (UX_PICTBRIDGE *)pima -> ux_device_class_pima_application;

    /* Get the pointer to the pima object. */
    object_info = (UX_SLAVE_CLASS_PIMA_OBJECT *) pictbridge ->
        ux_pictbridge_object_host;

    /* Is this the corrent handle ? */
    if (object_info -> ux_device_class_pima_object_handle_id == object_handle)
    {
        /* Get the pointer to the object buffer. */
        pima_object_buffer = object_info ->
            ux_device_class_pima_object_buffer;

        /* Check the phase. We should wait for the object to be completed and the response sent back before parsing the object. */
        if (phase == UX_DEVICE_CLASS_PIMA_OBJECT_TRANSFER_PHASE_ACTIVE)
        {
            /* Copy the demanded object data portion. */
            ux_utility_memory_copy(pima_object_buffer + object_offset,
                object_buffer, object_length);

            /* Save the length of this object. */
            object_info -> ux_device_class_pima_object_length = object_length;

            /* We are not done yet. */
            return(UX_SUCCESS);
        }
        else
        {
            /* Completion of transfer. We are done. */
            return(UX_SUCCESS);
        }
    }
}

ux_device_class_pima_object_delete

删除本地对象

原型

UINT ux_device_class_pima_object_delete(
    UX_SLAVE_CLASS_PIMA *pima,
    ULONG object_handle);

说明

当 PIMA 类需要删除本地存储中的对象时,将调用此函数。

参数

  • pima:指向 PIMA 类实例的指针
  • object_handle:对象的句柄

示例

UINT ux_pictbridge_dpsclient_object_delete(UX_SLAVE_CLASS_PIMA *pima,
    ULONG object_handle)
{
    /* Delete the object pointer by the handle. */

}

USB 设备音频类

USB 主机系统可以通过 USB 设备音频类来与充当音频设备的设备通信。 此类基于 USB 标准以及 USB 音频类 1.0 或 2.0 标准。

USB 音频合规的设备框架需要由设备堆栈声明。 音频 2.0 扬声器的示例如下:

unsigned char device_framework_high_speed[] = {

    /* --- Device Descriptor 18 bytes
    0x00 bDeviceClass: Refer to interface
    0x00 bDeviceSubclass: Refer to interface
    0x00 bDeviceProtocol: Refer to interface

    idVendor & idProduct - https://www.linux-usb.org/usb.ids
    */

    /* 0 bLength, bDescriptorType */ 18, 0x01,
    /* 2 bcdUSB : 0x200 (2.00) */ 0x00, 0x02,
    /* 4 bDeviceClass : 0x00 (see interface) */ 0x00,
    /* 5 bDeviceSubClass : 0x00 (see interface) */ 0x00,
    /* 6 bDeviceProtocol : 0x00 (see interface) */ 0x00,
    /* 7 bMaxPacketSize0 */ 0x08,
    /* 8 idVendor, idProduct */ 0x84, 0x84, 0x03, 0x00,
    /* 12 bcdDevice */ 0x00, 0x02,
    /* 14 iManufacturer, iProduct, iSerialNumber */ 0, 0, 0,
    /* 17 bNumConfigurations */ 1,
    /* ---------------- Device Qualifier Descriptor */
    /* 0 bLength, bDescriptorType */ 10, 0x06,
    /* 2 bcdUSB : 0x200 (2.00) */ 0x00,0x02,
    /* 4 bDeviceClass : 0x00 (see interface) */ 0x00,
    /* 5 bDeviceSubClass : 0x00 (see interface) */ 0x00,
    /* 6 bDeviceProtocol : 0x00 (see interface) */ 0x00,
    /* 7 bMaxPacketSize0 */ 8,
    /* 8 bNumConfigurations */ 1,
    /* 9 bReserved */ 0,
    /* --- Configuration Descriptor (9+8+73+55=145, 0x91) */
    /* 0 bLength, bDescriptorType */ 9, 0x02,
    /* 2 wTotalLength */ 145, 0,
    /* 4 bNumInterfaces, bConfigurationValue */ 2, 1,
    /* 6 iConfiguration */ 0,
    /* 7 bmAttributes, bMaxPower */ 0x80, 50,
    /* ----------- Interface Association Descriptor */
    /* 0 bLength, bDescriptorType */ 8, 0x0B,
    /* 2 bFirstInterface, bInterfaceCount */ 0, 2,
    /* 4 bFunctionClass : 0x01 (Audio) */ 0x01,
    /* 5 bFunctionSubClass : 0x00 (UNDEFINED) */ 0x00,
    /* 6 bFunctionProtocol : 0x20 (VERSION_02_00) */ 0x20,
    /* 7 iFunction */ 0,
    /* --- Interface Descriptor #0: Control (9+64=73) */
    /* 0 bLength, bDescriptorType */ 9, 0x04,
    /* 2 bInterfaceNumber, bAlternateSetting */ 0, 0,
    /* 4 bNumEndpoints */ 0,
    /* 5 bInterfaceClass : 0x01 (Audio) */ 0x01,
    /* 6 bInterfaceSubClass : 0x01 (AudioControl) */ 0x01,
    /* 7 bInterfaceProtocol : 0x20 (VERSION_02_00) */ 0x20,
    /* 8 iInterface */ 0,
    /* --- Audio 2.0 AC Interface Header Descriptor (9+8+17+18+12=64, 0x40) */
    /* 0 bLength */ 9,
    /* 1 bDescriptorType, bDescriptorSubtype */ 0x24, 0x01,
    /* 3 bcdADC */ 0x00, 0x02,
    /* 5 bCategory : 0x08 (IO Box) */ 0x08,
    /* 6 wTotalLength */ 64, 0,
    /* 8 bmControls */ 0x00,
    /* -------- Audio 2.0 AC Clock Source Descriptor */
    /* 0 bLength */ 8,
    /* 1 bDescriptorType, bDescriptorSubtype */ 0x24, 0x0A,
    /* 3 bClockID */ 0x10,
    /* 4 bmAttributes : 0x05 (Sync|InternalFixedClk) */ 0x05,
    /* 5 bmControls : 0x01 (FreqReadOnly) */ 0x01,
    /* 6 bAssocTerminal, iClockSource */ 0x00, 0,
    /* ------ Audio 2.0 AC Input Terminal Descriptor */
    /* 0 bLength */ 17,
    /* 1 bDescriptorType, bDescriptorSubtype */ 0x24, 0x02, /* 3 bTerminalID */ 0x04,
    /* 4 wTerminalType : 0x0101 (USB Streaming) */ 0x01, 0x01,
    /* 6 bAssocTerminal, bCSourceID */ 0x00, 0x10,
    /* 8 bNrChannels */ 2,
    /* 9 bmChannelConfig */ 0x00, 0x00, 0x00, 0x00,
    /* 13 iChannelNames, bmControls, iTerminal */ 0, 0x00, 0x00, 0,
    /* -------- Audio 2.0 AC Feature Unit Descriptor */
    /* 0 bLength */ 18,
    /* 1 bDescriptorType, bDescriptorSubtype */ 0x24, 0x06,
    /* 3 bUnitID, bSourceID */ 0x05, 0x04,
    /* 5 bmaControls(0) : 0x0F (VolumeRW|MuteRW) */ 0x0F, 0x00, 0x00, 0x00,
    /* 9 bmaControls(1) : 0x00000000 */ 0x00, 0x00, 0x00, 0x00,
    /* 13 bmaControls(1) : 0x00000000 */ 0x00, 0x00, 0x00, 0x00,
    /* . iFeature */ 0,
    /* ----- Audio 2.0 AC Output Terminal Descriptor */
    /* 0 bLength */ 12,
    /* 1 bDescriptorType, bDescriptorSubtype */ 0x24, 0x03, /* 3 bTerminalID */ 0x06,
    /* 4 wTerminalType : 0x0301 (Speaker) */ 0x01, 0x03,
    /* 6 bAssocTerminal, bSourceID, bCSourceID */ 0x00, 0x05, 0x10,
    /* 9 bmControls, iTerminal */ 0x00, 0x00, 0,
    /* --- Interface Descriptor #1: Stream OUT (9+9+16+6+7+8=55) */
    /* 0 bLength, bDescriptorType */ 9, 0x04,
    /* 2 bInterfaceNumber, bAlternateSetting */ 1, 0,
    /* 4 bNumEndpoints */ 0,
    /* 5 bInterfaceClass : 0x01 (Audio) */ 0x01,
    /* 6 bInterfaceSubClass : 0x01 (AudioStream) */ 0x02,
    /* 7 bInterfaceProtocol : 0x20 (VERSION_02_00) */ 0x20,
    /* 8 iInterface */ 0,
    /* ----------------------- Interface Descriptor */
    /* 0 bLength, bDescriptorType */ 9, 0x04,
    /* 2 bInterfaceNumber, bAlternateSetting */ 1, 1,
    /* 4 bNumEndpoints */ 1,
    /* 5 bInterfaceClass : 0x01 (Audio) */ 0x01,
    /* 6 bInterfaceSubClass : 0x01 (AudioStream) */ 0x02,
    /* 7 bInterfaceProtocol : 0x20 (VERSION_02_00) */ 0x20,
    /* 8 iInterface */ 0,
    /* ----------- Audio 2.0 AS Interface Descriptor */
    /* 0 bLength */ 16,
    /* 1 bDescriptorType, bDescriptorSubtype */ 0x24, 0x01,
    /* 3 bTerminalLink, bmControls */ 0x04, 0x00,
    /* 5 bFormatType : 0x01 (FORMAT_TYPE_I) */ 0x01,
    /* 6 bmFormats : 0x000000001 (PCM) */ 0x01, 0x00, 0x00, 0x00, /* 10 bNrChannels */ 2,
    /* 11 bmChannelConfig */ 0x00, 0x00, 0x00, 0x00,
    /* 15 iChannelNames */ 0, /* --------- Audio 2.0 AS Format Type Descriptor */
    /* 0 bLength */ 6,
    /* 1 bDescriptorType, bDescriptorSubtype */ 0x24, 0x02,
    /* 3 bFormatType : 0x01 (FORMAT_TYPE_I) */ 0x01,
    /* 4 bSubslotSize, bBitResolution */ 2, 16,
    /* ------------------------- Endpoint Descriptor */
    /* 0 bLength, bDescriptorType */ 7, 0x05,
    /* 2 bEndpointAddress */ 0x02,
    /* 3 bmAttributes : 0x0D (Sync|ISO) */ 0x0D,
    /* 4 wMaxPacketSize : 0x0100 (256) */ 0x00, 0x01,
    /* 6 bInterval : 0x04 (1ms) */ 4,
    /* - Audio 2.0 AS ISO Audio Data Endpoint Descriptor */
    /* 0 bLength */ 8,
    /* 1 bDescriptorType, bDescriptorSubtype */ 0x25, 0x01,
    /* 3 bmAttributes, bmControls */ 0x00, 0x00,
    /* 5 bLockDelayUnits, wLockDelay */ 0x00, 0x00, 0x00,
};

音频类使用复合设备框架对接口进行分组(控制和流式处理)。 因此,在定义设备描述符时应保持谨慎。 USBX 依赖于 IAD 描述符来了解如何在内部绑定接口。 IAD 描述符应在接口之前声明(一个 AudioControl 接口后接一个或多个 AudioStreaming 接口),并包含音频类的第一个接口(AudioControl 接口)以及附加的接口数。

音频类的工作方式取决于设备是发送还是接收音频,但这两种情况都使用 FIFO 来存储音频帧缓冲区:如果设备向主机发送音频,则应用程序会将音频帧缓冲区添加到 FIFO,然后,这些缓冲区由 USBX 发送到主机;如果设备从主机接收音频,则 USBX 会将从主机收到的音频帧缓冲区添加到 FIFO,然后,应用程序将读取这些缓冲区。 每个音频流都有自己的 FIFO,每个音频帧缓冲区由多个样本构成。

音频类的初始化需要以下组成部分。

  1. 音频类需要以下流式处理参数:

    /* Set the parameters for Audio streams. */
    /* Set the application-defined callback that is invoked when the
       host requests a change to the alternate setting. */
    audio_stream_parameter[0].ux_device_class_audio_stream_parameter_callbacks
        .ux_device_class_audio_stream_change = demo_audio_read_change;
    
    /* Set the application-defined callback that is invoked whenever
       a USB packet (audio frame) is sent to or received from the host. */
    audio_stream_parameter[0].ux_device_class_audio_stream_parameter_callbacks
        .ux_device_class_audio_stream_frame_done = demo_audio_read_done;
    
    /* Set the number of audio frame buffers in the FIFO. */
    audio_stream_parameter[0].ux_device_class_audio_stream_parameter_max_frame _buffer_nb = UX_DEMO_FRAME_BUFFER_NB;
    
    /* Set the maximum size of each audio frame buffer in the FIFO. */
    audio_stream_parameter[0].ux_device_class_audio_stream_parameter_max_frame _buffer_size = UX_DEMO_MAX_FRAME_SIZE;
    
    /* Set the internally-defined audio processing thread entry pointer. If the application wishes to receive audio from the host
       (which is the case in this example), ux_device_class_audio_read_thread_entry should be used;
       if the application wishes to send data to the host, ux_device_class_audio_write_thread_entry should be used. */
    audio_stream_parameter[0].ux_device_class_audio_stream_parameter_thread_entry = ux_device_class_audio_read_thread_entry;
    
  2. 音频类需要以下函数参数。

    /* Set the parameters for Audio device. */
    
    /* Set the number of streams. */
    audio_parameter.ux_device_class_audio_parameter_streams_nb = 1;
    
    /* Set the pointer to the first audio stream parameter.
       Note that we initialized this parameter in the previous section.
       Also note that for more than one streams, this should be an array. */
    audio_parameter.ux_device_class_audio_parameter_streams = audio_stream_parameter;
    
    /* Set the application-defined callback that is invoked when the audio class
       is activated i.e. device is connected to host. */
    audio_parameter.ux_device_class_audio_parameter_callbacks
        .ux_slave_class_audio_instance_activate = demo_audio_instance_activate;
    
    /* Set the application-defined callback that is invoked when the audio class
       is deactivated i.e. device is disconnected from host. */
    
    audio_parameter.ux_device_class_audio_parameter_callbacks
        .ux_slave_class_audio_instance_deactivate = demo_audio_instance_deactivate;
    
    /* Set the application-defined callback that is invoked when the stack receives a control request from the host.
       See below for more details.
    */
    audio_parameter.ux_device_class_audio_parameter_callbacks
        .ux_device_class_audio_control_process = demo_audio20_request_process;
    
    /* Initialize the device Audio class. This class owns interfaces starting with 0. */
    status = ux_device_stack_class_register(_ux_system_slave_class_audio_name,
        ux_device_class_audio_entry, 1, 0, &audio_parameter);
    if(status!=UX_SUCCESS)
        return;
    

    当堆栈接收来自主机的控制请求时,将调用应用程序定义的控制请求回调(ux_device_class_audio_control_process;已在前面的示例中设置)。 如果已接受并处理请求(已确认或已停滞),则该回调必须返回成功结果,否则应返回错误。

    类特定的控制请求进程定义为应用程序定义的回调,控制请求根据 USB 音频版本的不同而有很大的差异,并且请求进程的很大一部分与设备框架有关。 应用程序应正确处理请求才能使设备正常运行。

    由于对于音频设备而言,音量、静音和采样频率是常见的控制请求,因此,后续部分介绍了不同 USB 音频版本的、可供应用程序使用的简单且可在内部定义的回调。 有关更多详细信息,请参阅“ux_device_class_audio10_control_process”和“ux_device_class_audio_control_request”。

在音频设备的设备框架中,PID/VID 存储在设备描述符中(请参阅上面声明的设备描述符)。

当 USB 主机系统发现 USB 音频设备并装载音频类时,可将该设备与任何音频播放器或录制器(具体取决于框架)配合使用。 有关参考信息,请参阅“主机操作系统”。

下面定义了音频类 API。

ux_device_class_audio_read_thread_entry

用于读取音频函数数据的线程条目。

原型

VOID ux_device_class_audio_read_thread_entry(ULONG audio_stream);

说明

如果需要从主机读取音频,则将此函数传递给音频流初始化参数。 在内部,使用此函数作为入口函数来创建线程;线程本身通过 Audio 函数中的常时等量 OUT 终结点来读取音频数据。

参数

  • audio_stream:指向音频流实例的指针。

示例

/* Set parameter to initialize a stream for reading. */
audio_stream_parameter[0].ux_device_class_audio_stream_parameter_thread_entry
     = ux_device_class_audio_read_thread_entry;

ux_device_class_audio_write_thread_entry

用于写入音频函数数据的线程条目

原型

VOID ux_device_class_audio_write_thread_entry(ULONG audio_stream);

说明

如果需要将音频写入主机,则将此函数传递给音频流初始化参数。 在内部,使用此函数作为入口函数来创建线程;线程本身通过 Audio 函数中的常时等量 IN 终结点来写入音频数据。

参数

  • audio_stream:指向音频流实例的指针。

示例

/* Set parameter to initialize as stream for writing. */
audio_stream_parameter[0].ux_device_class_audio_stream_parameter_thread_en
    try = ux_device_class_audio_write_thread_entry;

ux_device_class_audio_stream_get

获取音频函数的特定流实例

原型

UINT ux_device_class_audio_stream_get(
    UX_DEVICE_CLASS_AUDIO *audio,
    ULONG stream_index, 
    UX_DEVICE_CLASS_AUDIO_STREAM **stream);

说明

此函数用于获取音频类的流实例。

参数

  • audio:指向音频实例的指针
  • stream_index:从 0 开始的流实例索引
  • stream:指向用于存储音频流实例指针的缓冲区的指针

返回值

  • UX_SUCCESS:(0x00) 此操作成功
  • UX_ERROR:(0xFF) 函数出错

示例

/* Get audio stream instance. */
status = ux_device_class_audio_stream_get(audio, 0, &stream);

if(status != UX_SUCCESS)
    return;

ux_device_class_audio_reception_start

开始接收音频流的音频数据

原型

UINT ux_device_class_audio_reception_start(UX_DEVICE_CLASS_AUDIO_STREAM *stream);

说明

此函数用于在音频流中开始读取音频数据。

参数

  • stream:指向音频流实例的指针。

返回值

  • UX_SUCCESS:(0x00) 此操作成功。
  • UX_CONFIGURATION_HANDLE_UNKNOWN:(0x51) 接口已关闭。
  • UX_BUFFER_OVERFLOW:(0x5d) FIFO 缓冲区已满。
  • UX_ERROR:(0xFF) 函数出错

示例

/* Start stream data reception. */
status = ux_device_class_audio_reception_start(stream);

if(status != UX_SUCCESS)
    return;

ux_device_class_audio_sample_read8

从音频流中读取 8 位样本

原型

UINT ux_device_class_audio_sample_read8(
    UX_DEVICE_CLASS_AUDIO_STREAM *stream, 
    UCHAR *buffer);

说明

此函数从指定的流中读取 8 位音频样本数据。

具体而言,它将从 FIFO 中的当前音频帧缓冲区读取样本数据。 读取音频帧中的最后一个样本后,将自动释放该帧,以便可以使用它接受来自主机的其他数据。

参数

  • stream:指向音频流实例的指针。
  • buffer:指向用于保存样本字节的缓冲区的指针。

返回值

  • UX_SUCCESS:(0x00) 此操作成功。
  • UX_CONFIGURATION_HANDLE_UNKNOWN:(0x51) 接口已关闭。
  • UX_BUFFER_OVERFLOW:(0x5d) FIFO 缓冲区为 null。
  • UX_ERROR:(0xFF) 函数出错

示例

/* Read a byte in audio FIFO. */

status = ux_device_class_audio_sample_read8(stream, &sample_byte);

if(status != UX_SUCCESS)
    return;

ux_device_class_audio_sample_read16

从音频流中读取 16 位样本

原型

UINT ux_device_class_audio_sample_read16(
    UX_DEVICE_CLASS_AUDIO_STREAM *stream, 
    USHORT *buffer);

说明

此函数从指定的流中读取 16 位音频样本数据。

具体而言,它将从 FIFO 中的当前音频帧缓冲区读取样本数据。 读取音频帧中的最后一个样本后,将自动释放该帧,以便可以使用它接受来自主机的其他数据。

参数

  • stream:指向音频流实例的指针。
  • buffer:指向用于保存 16 位样本的缓冲区的指针。

返回值

  • UX_SUCCESS:(0x00) 此操作成功。
  • UX_CONFIGURATION_HANDLE_UNKNOWN:(0x51) 接口已关闭。
  • UX_BUFFER_OVERFLOW:(0x5d) FIFO 缓冲区为 null。
  • UX_ERROR:(0xFF) 函数出错

示例

/* Read a 16-bit sample in audio FIFO. */

status = ux_device_class_audio_sample_read16(stream, &sample_word);

if(status != UX_SUCCESS)
    return;

ux_device_class_audio_sample_read24

从音频流中读取 24 位样本

原型

UINT ux_device_class_audio_sample_read24(
    UX_DEVICE_CLASS_AUDIO_STREAM *stream, 
    ULONG *buffer);

说明

此函数从指定的流中读取 24 位音频样本数据。

具体而言,它将从 FIFO 中的当前音频帧缓冲区读取样本数据。 读取音频帧中的最后一个样本后,将自动释放该帧,以便可以使用它接受来自主机的其他数据。

参数

  • stream:指向音频流实例的指针。
  • buffer:指向用于保存 3 字节样本的缓冲区的指针。

返回值

  • UX_SUCCESS:(0x00) 此操作成功。
  • UX_CONFIGURATION_HANDLE_UNKNOWN:(0x51) 接口已关闭。
  • UX_BUFFER_OVERFLOW:(0x5d) FIFO 缓冲区为 null。
  • UX_ERROR:(0xFF) 函数出错

示例

/* Read 3 bytes to in audio FIFO. */

status = ux_device_class_audio_sample_read24(stream, &sample_bytes);

if(status != UX_SUCCESS)
    return;

ux_device_class_audio_sample_read32

从音频流中读取 32 位样本

原型

UINT ux_device_class_audio_sample_read32(
    UX_DEVICE_CLASS_AUDIO_STREAM *stream, 
    ULONG *buffer);

说明

此函数从指定的流中读取 32 位音频样本数据。

具体而言,它将从 FIFO 中的当前音频帧缓冲区读取样本数据。 读取音频帧中的最后一个样本后,将自动释放该帧,以便可以使用它接受来自主机的其他数据。

参数

  • stream:指向音频流实例的指针。
  • buffer:指向用于保存 4 字节数据的缓冲区的指针。

返回值

  • UX_SUCCESS:(0x00) 此操作成功。
  • UX_CONFIGURATION_HANDLE_UNKNOWN:(0x51) 接口已关闭。
  • UX_BUFFER_OVERFLOW:(0x5d) FIFO 缓冲区为 null。
  • UX_ERROR:(0xFF) 函数出错

示例

/* Read 4 bytes in audio FIFO. */

status = ux_device_class_audio_sample_read32(stream, &sample_bytes);

if(status != UX_SUCCESS)
    return;

ux_device_class_audio_read_frame_get

获取对音频流中音频帧的访问权限

原型

UINT ux_device_class_audio_read_frame_get(
    UX_DEVICE_CLASS_AUDIO_STREAM *stream,
    UCHAR **frame_data, 
    ULONG *frame_length);

说明

此函数返回指定的流的 FIFO 中第一个音频帧缓冲区及其长度。 当应用程序处理完数据时,必须使用 ux_device_class_audio_read_frame_free 释放 FIFO 中的帧缓冲区。

参数

  • stream:指向音频流实例的指针。
  • frame_data:指向数据指针的指针,将在该数据指针中返回数据指针。
  • frame_length:指向用于保存帧长度(以字节数计)的缓冲区的指针。

返回值

  • UX_SUCCESS:(0x00) 此操作成功。
  • UX_CONFIGURATION_HANDLE_UNKNOWN:(0x51) 接口已关闭。
  • UX_BUFFER_OVERFLOW:(0x5d) FIFO 缓冲区为 null。
  • UX_ERROR:(0xFF) 函数出错

示例

/* Get frame access. */

status = ux_device_class_audio_read_frame_get(stream, &frame, &frame_length);

if(status != UX_SUCCESS)
    return;

ux_device_class_audio_read_frame_free

释放音频流中的音频帧缓冲区

原型

UINT ux_device_class_audio_read_frame_free(UX_DEVICE_CLASS_AUDIO_STREAM *stream);

说明

此函数释放位于指定流的 FIFO 前面的音频帧缓冲区,使该缓冲区能够从主机接收数据。

参数

  • stream:指向音频流实例的指针。

返回值

  • UX_SUCCESS:(0x00) 此操作成功。
  • UX_CONFIGURATION_HANDLE_UNKNOWN:(0x51) 接口已关闭。
  • UX_BUFFER_OVERFLOW:(0x5d) FIFO 缓冲区为 null。
  • UX_ERROR:(0xFF) 函数出错

示例

/* Refree a frame buffer in FIFO. */

status = ux_device_class_audio_read_frame_free(stream);

if(status != UX_SUCCESS)
    return;

ux_device_class_audio_transmission_start

开始传输音频流的音频数据

原型

UINT ux_device_class_audio_transmission_start(UX_DEVICE_CLASS_AUDIO_STREAM *stream);

说明

此函数用于在音频类中开始发送写入到 FIFO 的音频数据。

参数

  • stream:指向音频流实例的指针。

返回值

  • UX_SUCCESS:(0x00) 此操作成功。
  • UX_CONFIGURATION_HANDLE_UNKNOWN:(0x51) 接口已关闭。
  • UX_BUFFER_OVERFLOW:(0x5d) FIFO 缓冲区为 null。
  • UX_ERROR:(0xFF) 函数出错

示例

/* Start stream data transmission. */

status = ux_device_class_audio_transmission_start(stream);

if(status != UX_SUCCESS)
    return;

ux_device_class_audio_frame_write

将音频帧写入音频流

原型

UINT ux_device_class_audio_frame_write(
    UX_DEVICE_CLASS_AUDIO_STREAM *stream,
    UCHAR *frame,
    ULONG frame_length);

说明

此函数将帧写入音频流的 FIFO。 帧数据将复制到 FIFO 中的可用缓冲区,这样就可以发送到主机。

参数

  • stream:指向音频流实例的指针。
  • frame:指向帧数据的指针。
  • frame_length:帧长度,以字节数计。

返回值

  • UX_SUCCESS:(0x00) 此操作成功。
  • UX_CONFIGURATION_HANDLE_UNKNOWN:(0x51) 接口已关闭。
  • UX_BUFFER_OVERFLOW:(0x5d) FIFO 缓冲区已满。
  • UX_ERROR:(0xFF) 函数出错

示例

/* Get frame access. */

status = ux_device_class_audio_frame_write(stream, frame, frame_length);

if(status != UX_SUCCESS)
    return;

ux_device_class_audio_write_frame_get

获取对音频流中音频帧的访问权限

原型

UINT ux_device_class_audio_write_frame_get(
    UX_DEVICE_CLASS_AUDIO_STREAM *stream,
    UCHAR **frame_data, 
    ULONG *frame_length);

说明

此函数检索 FIFO 的最后一个音频帧缓冲区的地址;它还检索音频帧缓冲区的长度。 在应用程序将所需数据填充到音频帧缓冲区后,必须使用 ux_device_class_audio_write_frame_commit 向 FIFO 添加/提交帧缓冲区。

参数

  • stream:指向音频流实例的指针。
  • frame_data:指向帧数据指针的指针,将在该帧数据指针中返回帧数据指针。
  • frame_length:指向用于保存帧长度(以字节数计)的缓冲区的指针。

返回值

  • UX_SUCCESS:(0x00) 此操作成功。
  • UX_CONFIGURATION_HANDLE_UNKNOWN:(0x51) 接口已关闭。
  • UX_BUFFER_OVERFLOW:(0x5d) FIFO 缓冲区已满。
  • UX_ERROR:(0xFF) 函数出错

示例

/* Get frame access. */

status = ux_device_class_audio_write_frame_get(stream, &frame, &frame_length);

if(status != UX_SUCCESS)
     return;

ux_device_class_audio_write_frame_commit

在音频流中提交音频帧缓冲区。

原型

UINT ux_device_class_audio_write_frame_commit(
    UX_DEVICE_CLASS_AUDIO_STREAM *stream, 
    ULONG length);

说明

此函数将最后一个音频帧缓冲区添加/提交到 FIFO,使该缓冲区随时可传输到主机;请注意,应已通过 ux_device_class_write_frame_get 填充最后一个音频帧缓冲区。

参数

  • stream:指向音频流实例的指针。
  • length:缓冲区中准备就绪的字节数。

返回值

  • UX_SUCCESS:(0x00) 此操作成功。
  • UX_CONFIGURATION_HANDLE_UNKNOWN:(0x51) 接口已关闭。
  • UX_BUFFER_OVERFLOW:(0x5d) FIFO 缓冲区已满。
  • UX_ERROR:(0xFF) 函数出错

示例

/* Commit a frame after fill values in buffer. */

status = ux_device_class_audio_write_frame_commit(stream, 192);

if(status != UX_SUCCESS)
    return;

ux_device_class_audio10_control_process

处理 USB 音频 1.0 控制请求

原型

UINT ux_device_class_audio10_control_process(
    UX_DEVICE_CLASS_AUDIO *audio,
    UX_SLAVE_TRANSFER *transfer_request,
    UX_DEVICE_CLASS_AUDIO10_CONTROL_GROUP *group);

说明

此函数在 USB 音频 1.0 特定类型的控制终结点上管理主机发送的基本请求。

音量和静音请求的音频 1.0 功能将在该函数中处理。 处理请求时,将使用最后一个参数 (group) 传递的预定义数据来应答请求并存储控制更改。

参数

  • audio:指向音频实例的指针。
  • transfer:指向传输请求实例的指针。
  • group:请求进程的数据组。

返回值

  • UX_SUCCESS (0x00) 此操作成功。
  • UX_ERROR:(0xFF) 函数出错

示例

/* Initialize audio 1.0 control values. */

audio_control[0].ux_device_class_audio10_control_fu_id = 2;
audio_control[0].ux_device_class_audio10_control_mute[0] = 0;
audio_control[0].ux_device_class_audio10_control_volume[0] = 0;
audio_control[1].ux_device_class_audio10_control_fu_id = 5;
audio_control[1].ux_device_class_audio10_control_mute[0] = 0;
audio_control[1].ux_device_class_audio10_control_volume[0] = 0;

/* Handle request and update control values.
Note here only mute and volume for master channel is supported.
*/

status = ux_device_class_audio10_control_process(audio, transfer, &group);
if (status == UX_SUCCESS)
{
    /* Request handled, check changes */
    switch(audio_control[0].ux_device_class_audio10_control_changed)
    {
        case UX_DEVICE_CLASS_AUDIO10_CONTROL_MUTE_CHANGED:
        case UX_DEVICE_CLASS_AUDIO10_CONTROL_VOLUME_CHANGED:
        default: break;
    }
}

ux_device_class_audio20_control_process

处理 USB 音频 1.0 控制请求

原型

UINT ux_device_class_audio20_control_process(
    UX_DEVICE_CLASS_AUDIO *audio,
    UX_SLAVE_TRANSFER *transfer_request,
    UX_DEVICE_CLASS_AUDIO20_CONTROL_GROUP *group);

说明

此函数在 USB 音频 2.0 特定类型的控制终结点上管理主机发送的基本请求。

音频2.0 采样率(假设为单一固定频率)以及音量和静音请求的功能将在该函数中处理。 处理请求时,将使用最后一个参数 (group) 传递的预定义数据来应答请求并存储控制更改。

参数

  • audio:指向音频实例的指针。
  • transfer:指向传输请求实例的指针。
  • group:请求进程的数据组。

返回值

  • UX_SUCCESS (0x00) 此操作成功。
  • UX_ERROR:(0xFF) 函数出错

示例

/* Initialize audio 2.0 control values. */

audio_control[0].ux_device_class_audio20_control_cs_id = 0x10;
audio_control[0].ux_device_class_audio20_control_sampling_frequency = 48000;
audio_control[0].ux_device_class_audio20_control_fu_id = 2;
audio_control[0].ux_device_class_audio20_control_mute[0] = 0;
audio_control[0].ux_device_class_audio20_control_volume_min[0] = 0;
audio_control[0].ux_device_class_audio20_control_volume_max[0] = 100;
audio_control[0].ux_device_class_audio20_control_volume[0] = 50;
audio_control[1].ux_device_class_audio20_control_cs_id = 0x10;
audio_control[1].ux_device_class_audio20_control_sampling_frequency = 48000;
audio_control[1].ux_device_class_audio20_control_fu_id = 5;
audio_control[1].ux_device_class_audio20_control_mute[0] = 0;
audio_control[1].ux_device_class_audio20_control_volume_min[0] = 0;
audio_control[1].ux_device_class_audio20_control_volume_max[0] = 100;
audio_control[1].ux_device_class_audio20_control_volume[0] = 50;

/* Handle request and update control values.
Note here only mute and volume for master channel is supported.
*/

status = ux_device_class_audio20_control_process(audio, transfer, &group);
if (status == UX_SUCCESS)
{
    /* Request handled, check changes */
    switch(audio_control[0].ux_device_class_audio20_control_changed)
    {
        case UX_DEVICE_CLASS_AUDIO20_CONTROL_MUTE_CHANGED:
        case UX_DEVICE_CLASS_AUDIO20_CONTROL_VOLUME_CHANGED:
        default: break;
    }
}

USB 设备打印机类

USB 主机系统可以通过 USB 设备打印机类来与充当打印机的设备通信。 此类基于 USB 标准。

打印机合规的设备框架需要由设备堆栈声明。 下面是一个示例。

static unsigned char device_framework_full_speed[] = {

    /* Device descriptor     18 bytes
       0x00 bDeviceClass:    refer to interface descriptor
       0x00 bDeviceSubclass: refer to interface descriptor
       0x00 bDeviceProtocol: refer to interface descriptor
       idVendor & idProduct - http://www.linux-usb.org/usb.ids
    */
    0x12, 0x01, 0x10, 0x01,
    0x00, 0x00, 0x00,
    0x08,
    0x84, 0x84, 0x00, 0x00,
    0x00, 0x01,
    0x01, 0x02, 0x03,
    0x01,

    /* Configuration 1 descriptor 9 bytes. */
    0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0xc0, 0x32,

    /* Interface descriptor. 8 bytes
       0x07 bInterfaceClass: Base class for printers
       0x01 iInterfaceSubClass: subclass code for printers
       0x02 bInterfaceProtocol: Bi-directional interface
    */
    0x09, 0x04, 0x00, 0x00, 0x02, 0x07, 0x01, 0x02, 0x00,

    /* Endpoint descriptor (Bulk Out) */
    0x07, 0x05, 0x01, 0x02, 0x40, 0x00, 0x00,

    /* Endpoint descriptor (Bulk In) */
    0x07, 0x05, 0x82, 0x02, 0x40, 0x00, 0x00,
};

除常规设备框架外,打印机还需要字符串描述符。 下面给出了一个示例。

static unsigned char string_framework[] = {

    /* Manufacturer string descriptor : Index 1 - "AzureRTOS" */
    0x09, 0x04, 0x01, 9,
        'A','z','u','r','e','R','T','O','S',

    /* Product string descriptor : Index 2 - "Printer device" */
    0x09, 0x04, 0x02, 14,
        'P','r','i','n','t','e','r',' ','d','e','v','i','c','e',

    /* Serial Number string descriptor : Index 3 - "0001" */
    0x09, 0x04, 0x03, 0x04,
        0x30, 0x30, 0x30, 0x31
};

打印机类的初始化如下所示。

/* Set the parameters for callback when insertion/extraction of a printer device.  */
_ux_utility_memory_set(&device_printer_parameter, 0, sizeof(device_printer_parameter));
_ux_utility_short_put_big_endian(printer_device_id, sizeof(printer_device_id));
device_printer_parameter.ux_device_class_printer_device_id           = printer_device_id;
device_printer_parameter.ux_device_class_printer_instance_activate   = test_printer_instance_activate;
device_printer_parameter.ux_device_class_printer_instance_deactivate = test_printer_instance_deactivate;
device_printer_parameter.ux_device_class_printer_soft_reset          = test_printer_soft_reset;
/* Initialize the device printer class. This class owns both interfaces starting with 0. */
status  = ux_device_stack_class_register(_ux_system_device_class_printer_name,
                                         ux_device_class_printer_entry,
                                         1, 0, &device_printer_parameter);

应用程序需要向打印机类传递一个设备 ID 字符串数组,其长度保存在其前两个字节中。 下面提供了打印机设备 ID 的示例。

/* Device printer device ID.  */
static UCHAR printer_device_id[] =
 {
    "  "                                // Will be replaced by length (big endian)
    "MFG:Generic;"                      //   manufacturer (case sensitive)
    "MDL:Generic_/_Text_Only;"          //   model (case sensitive)
    "CMD:1284.4;"                       //   PDL command set
    "CLS:PRINTER;"                      //   class
    "DES:Generic text only printer;"    //   description
 };

打印机设备 ID 将封装在转移请求消息中,并在从主机收到“GET_DEVICE_ID”命令后发送回主机。 在上面的示例中,选择了“泛型/文本”驱动程序,然后将 RAW 文本从主机传输到打印机设备。

应用程序的主体中将有 2 个用于激活和停用的函数,如下面的示例中所示。

static VOID    test_printer_instance_activate(VOID *dummy_instance)
{
    if (device_printer == UX_NULL)
        device_printer = (UX_DEVICE_CLASS_PRINTER *)dummy_instance;
}

static VOID    test_printer_instance_deactivate(VOID *dummy_instance)
{
    if ((VOID*)device_printer == dummy_instance)
        device_printer = UX_NULL;
}

不建议在这些函数中执行任何操作,但要记住类的实例,并与应用程序的其余部分同步。

应用程序可以提供软重置回调通知函数,以便从主机端获取“SOFT_RESET”请求的通知。

USBX 打印机类支持主机中的以下标准打印机命令。

命令名称 “值” 说明
GET_DEVICE_ID 0x00 获取设备 ID,设备 ID 数组由初始化参数分配
GET_PORT_STATUS 0x01 获取打印机端口状态,可以通过 UX_DEVICE_CLASS_PRINTER_IOCTL_PORT_STATUS_SET 设置端口状态
SOFT_RESET 0x02 请求执行软重置,重置回调函数由初始化参数分配

下面定义了打印机类 API 函数。

ux_device_class_printer_read

从打印机管道读取

原型

UINT _ux_device_class_printer_read(
    UX_DEVICE_CLASS_PRINTER *printer,
    UCHAR *buffer,
    ULONG requested_length,
    ULONG *actual_length);

说明

应用程序需要从输出数据管道(从主机输出,从设备输入)读取数据时,将调用此函数。 它正在阻止。

注意

此函数从设备读取原始批量数据,因此它将保持挂起状态,直到缓冲区已满,或者设备通过短数据包(包括零长度数据包)终止传输。 有关详细信息,请参阅批量传输的一般注意事项

参数

  • printer:指向打印机类实例的指针。
  • buffer:将要存储数据的缓冲区地址。
  • requested_length:所需的最大长度。
  • actual_length:返回到缓冲区的长度。

返回值

  • UX_SUCCESS (0x00) 此操作成功。
  • UX_CONFIGURATION_HANDLE_UNKNOWN (0x51) 设备不再处于已配置状态。
  • UX_TRANSFER_NO_ANSWER (0x22) 设备无应答。 传输挂起时,设备可能已断开连接。

示例

/* Read from the printer class. */

status = ux_device_class_printer_read(device_printer, device_printer_buffer, 512, &actual_length);

if (status != UX_SUCCESS)
    return;

ux_device_class_printer_write

写入打印机管道

原型

UINT _ux_device_class_printer_write(
    UX_DEVICE_CLASS_PRINTER *printer,
    UCHAR *buffer,
    ULONG requested_length,
    ULONG *actual_length);

说明

应用程序需要写入输入数据管道(从主机输入,从设备输出)时,将调用此函数。 它正在阻止。

注意

此函数将批量数据写入主机。 主机一直等待,直到缓冲区已满或有短数据包。 因此,如果传输大小是 IN 终结点的最大数据包大小的倍数,最好添加另一个传输大小为 0 的调用来发送 ZLP,以告知主机已完成所有数据。 有关详细信息,请参阅批量传输的一般注意事项

参数

  • printer:指向打印机类实例的指针。
  • buffer:存储数据的缓冲区地址。
  • requested_length:要写入的缓冲区的长度。
  • actual_length:执行写入后返回到缓冲区的长度。

返回值

  • UX_SUCCESS (0x00) 此操作成功。
  • UX_CONFIGURATION_HANDLE_UNKNOWN (0x51) 设备不再处于已配置状态。
  • UX_TRANSFER_NO_ANSWER (0x22) 设备无应答。 传输挂起时,设备可能已断开连接。

示例

/* Write to the printer class bulk in pipe. */
status = ux_device_class_printer_write(device_printer, device_buffer, device_buffer_length, &send_total);

if (status != UX_SUCCESS)
    return;

ux_device_class_printer_ioctl

在打印机接口上执行 IOCTL

原型

UINT ux_device_class_printer_ioctl(
    UX_DEVICE_CLASS_PRINTER *printer,
    ULONG ioctl_function,
    VOID *parameter);

说明

应用程序需要执行对打印机接口的各种 ioctl 调用时,将调用此函数

参数

  • printer:指向打印机类实例的指针。
  • ioctl_function:要执行的 ioctl 函数。
  • parameter:指向特定于 ioctl 的参数的指针。

返回值

  • UX_SUCCESS (0x00) 此操作成功。
  • UX_ERROR:(0xFF) 函数出错

示例

status = ux_device_class_printer_ioctl(device_printer,
                                       UX_DEVICE_CLASS_PRINTER_IOCTL_PORT_STATUS_SET,
                                       (VOID *)0x73);

if(status != UX_SUCCESS)
    return;

Ioctl 函数:

函数 “值”
UX_DEVICE_CLASS_PRINTER_IOCTL_PORT_STATUS_SET 1
UX_DEVICE_CLASS_PRINTER_IOCTL_READ_TIMEOUT_SET 2
UX_DEVICE_CLASS_PRINTER_IOCTL_WRITE_TIMEOUT_SET 3

ux_device_class_printer_ioctl:UX_DEVICE_CLASS_PRINTER_IOCTL_PORT_STATUS_SET

在打印机接口上执行 IOCTL 设置端口状态

原型

UINT ux_device_class_printer_ioctl(
    UX_DEVICE_CLASS_PRINTER *printer,
    ULONG ioctl_function,
    VOID *parameter);

说明

应用程序需要设置端口状态时,将调用此函数。

参数

  • printer:指向打印机类实例的指针。
  • ioctl_function:UX_DEVICE_CLASS_PRINTER_IOCTL_PORT_STATUS_SET
  • 参数端口状态:

打印机端口状态。 有关详细信息,请参阅文档“适用于打印设备的通用串行总线设备类定义”。

名称 Offset 大小 type 说明
保留 0 3 位域 保留以供将来使用;应为 0
非错误 3 1 位域 1 = 无错误,0 = 错误
选定 4 1 位域 1 = 已选择,0 = 未选择
纸张为空 5 1 位域 1 = 纸张为空,0 = 纸张不为空
保留 6 2 位域 保留以供将来使用;应为 0

返回值

UX_SUCCESS (0x00) 此操作成功。

示例

在下面的示例中,打印机端口状态设置为“纸张为空”、“未选择”和“错误”。

status = ux_device_class_printer_ioctl(device_printer,
                                       UX_DEVICE_CLASS_PRINTER_IOCTL_PORT_STATUS_SET,
                                       (VOID *)0x73);

if(status != UX_SUCCESS)
    return;

ux_device_class_printer_ioctl:UX_DEVICE_CLASS_PRINTER_IOCTL_READ_TIMEOUT_SET

在打印机接口上执行 IOCTL 设置读取超时

原型

UINT ux_device_class_printer_ioctl(
    UX_DEVICE_CLASS_PRINTER *printer,
    ULONG ioctl_function,
    VOID *parameter);

说明

应用程序需要设置读取超时值时,将调用此函数。

参数

  • printer:指向打印机类实例的指针。
  • ioctl_function:UX_DEVICE_CLASS_PRINTER_IOCTL_READ_TIMEOUT_SET
  • 参数:超时值:

返回值

UX_SUCCESS (0x00) 此操作成功。

示例

在下面的示例中,打印机读取超时值设置为 10000 毫秒。

status = ux_device_class_printer_ioctl(device_printer,
                                       UX_DEVICE_CLASS_PRINTER_IOCTL_READ_TIMEOUT_SET,
                                       (VOID *)10000);

if(status != UX_SUCCESS)
    return;

ux_device_class_printer_ioctl:UX_DEVICE_CLASS_PRINTER_IOCTL_WRITE_TIMEOUT_SET

在打印机接口上执行 IOCTL 设置写入超时

原型

UINT ux_device_class_printer_ioctl(
    UX_DEVICE_CLASS_PRINTER *printer,
    ULONG ioctl_function,
    VOID *parameter);

说明

应用程序需要设置写入超时值时,将调用此函数。

参数

  • printer:指向打印机类实例的指针。
  • ioctl_function:UX_DEVICE_CLASS_PRINTER_IOCTL_WRITE_TIMEOUT_SET
  • 参数:超时值:

返回值

UX_SUCCESS (0x00) 此操作成功。

示例

在下面的示例中,打印机写入超时值设置为 10000 毫秒。

status = ux_device_class_printer_ioctl(device_printer,
                                       UX_DEVICE_CLASS_PRINTER_IOCTL_WRITE_TIMEOUT_SET,
                                       (VOID *)10000);

if(status != UX_SUCCESS)
    return;