USB 设备栈¶
- USB 设备栈分为如下三层:
- USB 设备控制器驱动(硬件相关的)
- USB 设备核心驱动(硬件无关的)
- USB 设备类驱动(硬件无关的)
USB 设备控制器驱动¶
设备控制器驱动层实现了直接与底层硬件打交道的逻辑。所有的设备控制器驱动都应当实现 usb_dc.h 中描述的 API。这样做的好处是当新的 USB 设备集成到系统中时不需要修改上层内容。目前只支持 Quark SE 的 USB 设备控制器(Designware IP)。
结构体¶
struct usb_dc_ep_cfg_data {
uint8_t ep_addr;
uint16_t ep_mps;
enum usb_dc_ep_type ep_type;
};
- 这个结构体中包含 USB 终点配置。
- ep_addr: 终点地址,在设备配置结构中与 EP 关联的数量。 IN EP = 0x80 | <endpoint number>. OUT EP = 0x00 | <endpoint number>
- ep_mps: 终点最大包尺寸。
- ep_type: 终点类型,可以是 Bulk、Interrupt 或者 Control。当前不支持同步终点。
enum usb_dc_status_code {
USB_DC_ERROR,
USB_DC_RESET,
USB_DC_CONNECTED,
USB_DC_CONFIGURED,
USB_DC_DISCONNECTED,
USB_DC_SUSPEND,
USB_DC_RESUME,
USB_DC_UNKNOWN
};
- 由注册的设备状态回调函数返回的状态码。
- USB_DC_ERROR: 控制器返回的 USB 错误。
- USB_DC_RESET: USB 复位。
- USB_DC_CONNECTED: USB 连接已建立 - 硬件枚举已完成。
- USB_DC_CONFIGURED: USB 配置完成。
- USB_DC_DISCONNECTED: USB 连接丢失。
- USB_DC_SUSPEND: USB 连接被主机挂起。
- USB_DC_RESUME: USB 连接被主机恢复。
- USB_DC_UNKNOWN: 初始化的 USB 连接状态。
enum usb_dc_ep_cb_status_code {
USB_DC_EP_SETUP,
USB_DC_EP_DATA_OUT,
USB_DC_EP_DATA_IN,
};
- 由注册的终点回调函数返回的状态码。
- USB_DC_EP_SETUP: SETUP packet received.
- USB_DC_EP_DATA_OUT: Out transaction on this endpoint. Data is available for read.
- USB_DC_EP_DATA_IN: In transaction done on this endpoint.
API¶
下列 API 是由设备控制器驱动程序提供的:
usb_dc_attach()
该函数负责附着(连接) USB 设备。附着成功后,USB 的 PLL 被使能,且 USB 设备能够在 USB 总线上收发数据、产生中断。
usb_dc_detach()
- 该函数用于卸载 USB 设备。卸载成功后,USB 的 PLL 被关电,且 USB 通信功能被禁止。
usb_dc_reset()
该函数用于将 USB 设备返回到初始化状态。
usb_dc_set_address()
该函数用于设置 USB 设备的地址。
usb_dc_set_status_callback()
该函数用于设置 USB 设备控制器的状态回调。所注册的回调函数用于报告设备控制器产生的状态变化。状态码由枚举 usb_dc_status_code 进行描述。
usb_dc_ep_configure()
该函数用于配置一个终点。结构体 usb_dc_ep_cfg_data 用于提供终点配置参数:终点地址、终点最大包尺寸和终点类型。
usb_dc_ep_set_stall()
该函数用于为所选定的终点设置 stall 条件。
usb_dc_ep_clear_stall()
该函数用于为所选定的终点清除 stall 条件。
usb_dc_ep_is_stalled()
该函数用于检查所选终点是否被 install。
usb_dc_ep_halt()
该函数用于停止(halt)所选终点。
usb_dc_ep_enable()
该函数用于使能所选终点。使能成功后,相应终点的中断会被使能,并且收发数据已就绪。
usb_dc_ep_disable()
该函数用于禁止所选终点。禁止成功后,相应终点的终点被禁止,且不能够再收发数据。
usb_dc_ep_flush()
该函数用于冲刷所选终点的 FIFO。
usb_dc_ep_write()
该函数用于向指定的终点写数据。当数据传输出去后,所设置的回调函数 usb_ep_callback 会被调用。
usb_dc_ep_read()
当某个点接收到 OUT 中断胡,终点处理函数会调用该函数。应用程序只能使用所提供的 usb_ep_callback 函数间接调用本函数。
usb_dc_ep_set_callback()
当接收到数据,且该数据对应用程序有效是,或者所选终点的传输完成时,调用该函数设置回调函数。
USB 设备核心层¶
USB 设备核心层是介于 USB 设备控制器驱动和 USB 设备类驱动或应用层之间的与硬件无关的接口。它是 LPCUSB 设备栈的一部分。它提供下面的功能:
- 响应标准设备请求并返回标准描述符,从根本上处理‘第 9 章’过程,尤其是通用串行规范 2.0 的表 9-3。
- 提供 USB 设备类或者应用程序所用的编程接口。这些 API 在文件 usb_device.h 中进行描述。
- 使用设备控制器驱动提供的 API 与 USB 设备控制器交互。
结构体¶
typedef void (*usb_status_callback)(enum usb_dc_status_code status_code);
设备状态的回调函数签名。
typedef void (*usb_ep_callback)(uint8_t ep,
enum usb_dc_ep_cb_status_code cb_status);
USB 终点的回调函数签名。
typedef int (*usb_request_handler) (struct usb_setup_packet *setup,
int *transfer_len, uint8_t **payload_data);
类指定请求的回调函数签名。从主机到设备方向,‘len’表示所接收数据的长度,‘payload_data’指向所接收的数据。从设备到主机类请求,回调函数应当将‘len’和‘payload_data’设置为所传输缓冲的数据长度和地址。
struct usb_ep_cfg_data {
usb_ep_callback ep_cb;
uint8_t ep_addr;
};
- 这个结构体包含了终端的配置。
- ep_cb: 接收到数据且对应用程序有效时,或传输完成时进行通知的回调函数。NULL 表示应用程序不需要回调。
- ep_addr: 终点地址。终点地址,在设备配置结构中与 EP 关联的数量。
struct usb_interface_cfg_data {
usb_request_handler class_handler;
usb_request_handler custom_handler;
uint8_t *payload_data;
};
- 这个结构体包含 USB 接口配置。
- class_handler: USB 类相关控制(EP 0)通信的处理者。
- custom_handler: 自定义请求处理者最先有机会在被移交给‘第 9 章’的请求处理者前处理请求。
- payload_data: 这段由应用程序分配的数据区用于存放类相关的命令,它必须能够容纳与所支持的最大命令集相关联的最大载荷。
struct usb_cfg_data {
const uint8_t *usb_device_description;
usb_status_callback cb_usb_status;
struct usb_interface_cfg_data interface;
uint8_t num_endpoints;
struct usb_ep_cfg_data *endpoint;
};
- 这个结构体包含 USB 设备配置。
- usb_device_description: USB 设备描述,参考 http://www.beyondlogic.org/usbnutshell/usb5.shtml#DeviceDescriptors。
- cb_usb_status: USB 连接状态改变时被通知的回调。
- interface: USB 类处理者和存储空间。
- num_endpoints: 设备配置中终点的数量。
- endpoint: 指向一个的终点配置结构体的数组,该数组的长度等于与设备描述相关联的终点数量。不包括控制终点。
类驱动程序使用 “usb_set_config” 所给的参数来实例化它。
API¶
usb_set_config()
该函数用于配置 USB 设备。
usb_deconfig()
该函数用于将 USB 设备返回到初始状态。
usb_enable()
该函数用于使能 USB 主机/设备连接。使能成功后,USB 模型在硬件上的时钟会开启,之后能够在 USB 总线上收发数据,且能够产生中断。
usb_disable()
- 该函数用于禁止 USB 是被。禁止成功后,USB 模块的始终在硬件上被关闭,之后就不能产生中断。
usb_write()
向指定终点写数据。当传输完成时会调用所提供的 usb_ep_callback。
usb_read()
当 OUT 中断被终点接收首,终点处理函数会调用该函数。应用程序只能通过所提供的函数 usb_ep_callback 间接调用该函数。
USB 设备类驱动¶
初始化设备类驱动实例时,USB 设备类驱动程序应当 usb_set_config(),作为参数传递实例的配置结构。
例如,对于 CDC_ACM 例程应用:
static const uint8_t cdc_acm_usb_description[] = {
/* Device descriptor */
USB_DEVICE_DESC_SIZE, /* Descriptor size */
USB_DEVICE_DESC, /* Descriptor type */
LOW_BYTE(USB_1_1),
HIGH_BYTE(USB_1_1), /* USB version in BCD format */
COMMUNICATION_DEVICE_CLASS, /* Class */
0x00, /* SubClass - Interface specific */
0x00, /* Protocol - Interface specific */
MAX_PACKET_SIZE_EP0, /* Max Packet Size */
LOW_BYTE(VENDOR_ID),
HIGH_BYTE(VENDOR_ID), /* Vendor Id */
LOW_BYTE(CDC_PRODUCT_ID),
HIGH_BYTE(CDC_PRODUCT_ID), /* Product Id */
LOW_BYTE(BCDDEVICE_RELNUM),
HIGH_BYTE(BCDDEVICE_RELNUM), /* Device Release Number */
0x01, /* Index of Manufacturer String Descriptor */
0x02, /* Index of Product String Descriptor */
0x03, /* Index of Serial Number String Descriptor */
CDC_NUM_CONF, /* Number of Possible Configuration */
/* Configuration descriptor */
USB_CONFIGURATION_DESC_SIZE, /* Descriptor size */
USB_CONFIGURATION_DESC, /* Descriptor type */
LOW_BYTE(CDC_CONF_SIZE),
HIGH_BYTE(CDC_CONF_SIZE), /* Total length in bytes of data returned */
CDC_NUM_ITF, /* Number of interfaces */
0x01, /* Configuration value */
0x00, /* Index of the Configuration string */
USB_CONFIGURATION_ATTRIBUTES, /* Attributes */
MAX_LOW_POWER, /* Max power consumption */
/* Interface descriptor */
USB_INTERFACE_DESC_SIZE, /* Descriptor size */
USB_INTERFACE_DESC, /* Descriptor type */
0x00, /* Interface index */
0x00, /* Alternate setting */
CDC1_NUM_EP, /* Number of Endpoints */
COMMUNICATION_DEVICE_CLASS, /* Class */
ACM_SUBCLASS, /* SubClass */
V25TER_PROTOCOL, /* Protocol */
0x00, /* Index of the Interface String Descriptor */
/* Header Functional Descriptor */
USB_HFUNC_DESC_SIZE, /* Descriptor size */
CS_INTERFACE, /* Descriptor type */
USB_HFUNC_SUBDESC, /* Descriptor SubType */
LOW_BYTE(USB_1_1),
HIGH_BYTE(USB_1_1), /* CDC Device Release Number */
/* Call Management Functional Descriptor */
USB_CMFUNC_DESC_SIZE, /* Descriptor size */
CS_INTERFACE, /* Descriptor type */
USB_CMFUNC_SUBDESC, /* Descriptor SubType */
0x00, /* Capabilities */
0x01, /* Data Interface */
/* ACM Functional Descriptor */
USB_ACMFUNC_DESC_SIZE, /* Descriptor size */
CS_INTERFACE, /* Descriptor type */
USB_ACMFUNC_SUBDESC, /* Descriptor SubType */
/* Capabilities - Device supports the request combination of:
* Set_Line_Coding,
* Set_Control_Line_State,
* Get_Line_Coding
* and the notification Serial_State
*/
0x02,
/* Union Functional Descriptor */
USB_UFUNC_DESC_SIZE, /* Descriptor size */
CS_INTERFACE, /* Descriptor type */
USB_UFUNC_SUBDESC, /* Descriptor SubType */
0x00, /* Master Interface */
0x01, /* Slave Interface */
/* Endpoint INT */
USB_ENDPOINT_DESC_SIZE, /* Descriptor size */
USB_ENDPOINT_DESC, /* Descriptor type */
CDC_ENDP_INT, /* Endpoint address */
USB_DC_EP_INTERRUPT, /* Attributes */
LOW_BYTE(CDC_INTERRUPT_EP_MPS),
HIGH_BYTE(CDC_INTERRUPT_EP_MPS),/* Max packet size */
0x0A, /* Interval */
/* Interface descriptor */
USB_INTERFACE_DESC_SIZE, /* Descriptor size */
USB_INTERFACE_DESC, /* Descriptor type */
0x01, /* Interface index */
0x00, /* Alternate setting */
CDC2_NUM_EP, /* Number of Endpoints */
COMMUNICATION_DEVICE_CLASS_DATA,/* Class */
0x00, /* SubClass */
0x00, /* Protocol */
0x00, /* Index of the Interface String Descriptor */
/* First Endpoint IN */
USB_ENDPOINT_DESC_SIZE, /* Descriptor size */
USB_ENDPOINT_DESC, /* Descriptor type */
CDC_ENDP_IN, /* Endpoint address */
USB_DC_EP_BULK, /* Attributes */
LOW_BYTE(CDC_BULK_EP_MPS),
HIGH_BYTE(CDC_BULK_EP_MPS), /* Max packet size */
0x00, /* Interval */
/* Second Endpoint OUT */
USB_ENDPOINT_DESC_SIZE, /* Descriptor size */
USB_ENDPOINT_DESC, /* Descriptor type */
CDC_ENDP_OUT, /* Endpoint address */
USB_DC_EP_BULK, /* Attributes */
LOW_BYTE(CDC_BULK_EP_MPS),
HIGH_BYTE(CDC_BULK_EP_MPS), /* Max packet size */
0x00, /* Interval */
/* String descriptor language, only one, so min size 4 bytes.
* 0x0409 English(US) language code used
*/
USB_STRING_DESC_SIZE, /* Descriptor size */
USB_STRING_DESC, /* Descriptor type */
0x09,
0x04,
/* Manufacturer String Descriptor "Intel" */
0x0C,
USB_STRING_DESC,
'I', 0, 'n', 0, 't', 0, 'e', 0, 'l', 0,
/* Product String Descriptor "CDC-ACM" */
0x10,
USB_STRING_DESC,
'C', 0, 'D', 0, 'C', 0, '-', 0, 'A', 0, 'C', 0, 'M', 0,
/* Serial Number String Descriptor "00.01" */
0x0C,
USB_STRING_DESC,
'0', 0, '0', 0, '.', 0, '0', 0, '1', 0,
};
static struct usb_ep_cfg_data cdc_acm_ep_data[] = {
{
.ep_cb = cdc_acm_int_in,
.ep_addr = CDC_ENDP_INT
},
{
.ep_cb = cdc_acm_bulk_out,
.ep_addr = CDC_ENDP_OUT
},
{
.ep_cb = cdc_acm_bulk_in,
.ep_addr = CDC_ENDP_IN
}
};
static struct usb_cfg_data cdc_acm_config = {
.usb_device_description = cdc_acm_usb_description,
.cb_usb_status = cdc_acm_dev_status_cb,
.interface = {
.class_handler = cdc_acm_class_handle_req,
.custom_handler = NULL,
.payload_data = NULL,
},
.num_endpoints = CDC1_NUM_EP + CDC2_NUM_EP,
.endpoint = cdc_acm_ep_data
};
ret = usb_set_config(&cdc_acm_config);
if (ret < 0) {
DBG("Failed to config USB\n");
return ret;
}
要使能 USB 设备主机/设备连接:
ret = usb_enable(&cdc_acm_config);
if (ret < 0) {
DBG("Failed to enable USB\n");
return ret;
}
类设备请求被 USB 栈核心驱动程序通过所注册的类处理者转发给类驱动程序。对于 CDC_ACM 例程类驱动程序,‘cdc_acm_class_handle_req’ 处理 SET_LINE_CODING、CDC_SET_CONTROL_LINE_STATE 和 CDC_GET_LINE_CODING 类请求:
int cdc_acm_class_handle_req(struct usb_setup_packet *pSetup,
int32_t *len, uint8_t **data)
{
struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(cdc_acm_dev);
switch (pSetup->bRequest) {
case CDC_SET_LINE_CODING:
memcpy(&dev_data->line_coding, *data, sizeof(dev_data->line_coding));
DBG("\nCDC_SET_LINE_CODING %d %d %d %d\n",
sys_le32_to_cpu(dev_data->line_coding.dwDTERate),
dev_data->line_coding.bCharFormat,
dev_data->line_coding.bParityType,
dev_data->line_coding.bDataBits);
break;
case CDC_SET_CONTROL_LINE_STATE:
dev_data->line_state = (uint8_t)sys_le16_to_cpu(pSetup->wValue);
DBG("CDC_SET_CONTROL_LINE_STATE 0x%x\n", dev_data->line_state);
break;
case CDC_GET_LINE_CODING:
*data = (uint8_t *)(&dev_data->line_coding);
*len = sizeof(dev_data->line_coding);
DBG("\nCDC_GET_LINE_CODING %d %d %d %d\n",
sys_le32_to_cpu(dev_data->line_coding.dwDTERate),
dev_data->line_coding.bCharFormat,
dev_data->line_coding.bParityType,
dev_data->line_coding.bDataBits);
break;
default:
DBG("CDC ACM request 0x%x, value 0x%x\n",
pSetup->bRequest, pSetup->wValue);
return -EINVAL;
}
return 0;
}
类驱动程序在传输数据区应当等待 USB_DC_CONFIGURED 设备状态码。
数据传输给主机时,类驱动程序应当调用 usb_write()。完成后,所注册的终点回调函数会被调用。在发送另一个包前,类驱动程序应当等待之前的传输完成。
当数据被接收后,所注册的终点回调函数会被调用。usb_read() 会被用于恢复接收到的数据。它必须总是通过所注册的终点回调函数被调用。对于 CDC ACM 例程驱动程序,这是通过终点数组(cdc_acm_ep_data)中所提到的 OUT bluk 终点处理者完成的。
当前只提供了 CDC ACM 和 DFU 类驱动例程。