上一篇: https://blog.csdn.net/qq_40088639/article/details/110116222
二、设备核心层
USB 设备核心层是介于 USB 设备控制器驱动层和 USB 设备类驱动或应用层之间的与硬件无关的接口层。设备栈三层架构如下图:
在核心层主要完成这些事:
(1)响应标准设备请求并返回标准描述符。
(2)提供 USB 设备类或者应用程序所用的编程接口。这些 API 在头文件 usb_device.h 中有声明。
(3)使用设备控制器驱动提供的API与USB 设备控制器交互。
1. 重要函数声明
1.1 设备状态回调函数的声明(函数指针的定义)
/** * Callback function signature for the device */ typedef void (*usb_status_callback)(enum usb_dc_status_code status_code, u8_t *param); |
1.2 端点状态回调函数的声明
/** * Callback function signature for the USB Endpoint status */ typedef void (*usb_ep_callback)(u8_t ep,enum usb_dc_ep_cb_status_code cb_status); |
1.3 类请求句柄函数的声明
/** * Function which handles Class specific requests corresponding to an * interface number specified in the device descriptor table */ typedef int (*usb_request_handler) (struct usb_setup_packet *detup, s32_t *transfer_len, u8_t **payload_data); |
处理与设备描述符表中指定的接口号相对应的类特定请求的函数。
如果数据是从主机到设备,则len和payload_data分别表示接收的数据长度和指向接收数据缓存区Buff的指针。对于是从设备到主机的类请求,则len和payload_data分别表示要发送的数据长度和要传输的数据缓冲区地址,这两个参数是在回调函数里面进行设置的。
1.4 接口配置函数
/** * Function for interface runtime configuration */ typedef void (*usb_interface_config)(u8_t bInterfaceNumber); |
2. 结构体
2.1 USB端点配置结构体
/* * USB Endpoint Configuration */ struct usb_ep_cfg_data {
/** * Callback function for notification of data received and * available to application or transmit done, NULL if callback * not required by application code */ usb_ep_callback ep_cb;
/** * The number associated with the EP in the device configuration * structure * IN EP = 0x80 | \<endpoint number\> * OUT EP = 0x00 | \<endpoint number\> */ u8_t ep_addr; }; |
这个结构体描述了端点的配置。
(1)ep_cb: 接收到数据且对应用程序有效时,或传输完成时进行通知的回调函数。NULL 表示应用程序不需要回调。
(2)ep_addr: 终点地址。终点地址,在设备配置结构中与 EP 关联的数量。
示例1:一般是定义成一个结构体数组(数组的每一个成员都是一个结构体)
/* Describe Endpoints configuration */ static struct usb_ep_cfg_data ep_data[] = { { .ep_cb = out_ep_cb, .ep_addr = VENDOR_BULK_EP_ADDR } }; |
2.2 USB接口配置结构体(接口的配置实体)
/** * USB Interface Configuration */ struct usb_interface_cfg_data { /** Handler for USB Class specific Control (EP 0) communications */ usb_request_handler class_handler;
/** Handler for USB Vendor specific commands */ usb_request_handler vendor_handler;
/** * The custom request handler gets a first chance at handling * the request before it is handed over to the 'chapter 9' request * handler */ usb_request_handler custom_handler;
/** * This data area, allocated by the application, is used to store * Class specific command data and must be large enough to store the * largest payload associated with the largest supported Class' * command set. This data area may be used for USB IN or OUT * communications */ u8_t *payload_data;
/** * This data area, allocated by the application, is used to store * Vendor specific payload */ u8_t *vendor_data;
}; |
这个结构体包含 USB 接口配置。
(1) class_handler: USB设备类请求的处理函数句柄
(2) vendor_handler:制造商自定义的请求处理函数句柄
(3) custom_handler: 自定义请求处理者函数句柄
(4) payload_data: 这个数据区域由应用程序分配,用于存储特类特殊的命令数据,并且必须足够大,能够支持最大命令集的容量。此数据区也可用在USB输入或输出通信上。
示例1:设备类请求、自定义请求、厂商请求都有对应的句柄函数,并且在里面都实例化了配置实例。
\subsys\usb\usb_device.c
static int class_handler(struct usb_setup_packet *pSetup,s32_t *len, u8_t **data){ --------------------------------------------- struct usb_interface_cfg_data *iface; --------------------------------------------- }
static int custom_handler (struct usb_setup_packet *pSetup,s32_t *len, u8_t **data){ --------------------------------------------- struct usb_interface_cfg_data *iface; --------------------------------------------- }
static int vendor_handler (struct usb_setup_packet *pSetup,s32_t *len, u8_t **data){ --------------------------------------------- struct usb_interface_cfg_data *iface; --------------------------------------------- }
|
2.3 USB设备配置结构体(一个类设备实体,由该结构体来描述)
/* * @brief USB device configuration * * The Application instantiates this with given parameters added * using the "usb_set_config" function. Once this function is called * changes to this structure will result in undefined behaviour. This structure * may only be updated after calls to usb_deconfig */ struct usb_cfg_data { /** * USB device description, see * http://www.beyondlogic.org/usbnutshell/usb5.shtml#DeviceDescriptors */ const u8_t *usb_device_description;
/** Pointer to interface descriptor */ const void *interface_descriptor;
/** Function for interface runtime configuration */ usb_interface_config interface_config;
/** Callback to be notified on USB connection status change */ usb_status_callback cb_usb_status;
/** USB interface (Class) handler and storage space */ struct usb_interface_cfg_data interface;
/** Number of individual endpoints in the device configuration */ u8_t num_endpoints;
/** * Pointer to an array of endpoint structs of length equal to the * number of EP associated with the device description, * not including control endpoints */ struct usb_ep_cfg_data *endpoint;
};
|
对结构体参数的说明:
(1) usb_device_description: 指向USB 设备描述的指针
关于描述符,可参考官网
http://www.beyondlogic.org/usbnutshell/usb5.shtml#DeviceDescriptors。
其中,几张重要的图如下
图1
图2
图3
(2) interface_descriptor:指向接口描述符的指针
(3) interface_config:接口配置函数
(4) cb_usb_status: USB设备连接状态改变时被通知的回调函数。
(5) interface: USB 类处理者和存储空间(接口的配置实体)。
(6) num_endpoints: 设备配置中端点的数量。
(7) endpoint: 指向一个端点配置结构体的数组,该数组的长度等于与设备描述相关联的端点数量,不包括控制端点。
实例化一个结构体变量之后,变量要作为一个参数传入usb_set_config()函数里面,对设备进行配置。也就是说,它们是成对使用,完成对一个USB设备的实例化。
示例1:HID类设备
//在core.c中,定义了一个usb_cfg_data结构体。 USBD_CFG_DATA_DEFINE(hid) struct usb_cfg_data hid_config = { .usb_device_description = NULL, .interface_config = hid_interface_config, .interface_descriptor = &hid_cfg.if0, .cb_usb_status = hid_status_cb, .interface = { .class_handler = hid_class_handle_req, .custom_handler = hid_custom_handle_req, .payload_data = NULL, }, .num_endpoints = ARRAY_SIZE(hid_ep_data), .endpoint = hid_ep_data, };
//在usb_hid_init()中将整个结构体作为参数,传入usb_set_config()实例化一个设备 int usb_hid_init(void) { ………………………………………………….. /* Initialize the USB driver with the right configuration */ ret = usb_set_config(&hid_config); if (ret < 0) { SYS_LOG_ERR("Failed to config USB"); return ret; }
/* Enable USB driver */ ret = usb_enable(&hid_config); if (ret < 0) { SYS_LOG_ERR("Failed to enable USB"); return ret; } ……………………………………………………………. return 0; } |
3. 核心层的API简介
3.1 USB设备配置函数
函数原型 | int usb_set_config(struct usb_cfg_data *config) { } |
函数功能 | 使用正确的配置来配置(初始化)USB控制器。 |
参数 | Config:指向配置结构体的指针。传参的时候,先填好结构体,再把结构体的地址传入配置控制器的函数,完成一个USB设备的实例化。 |
返回值 | 配置成功,则返回0;失败,则返回一个负的错误代码值。 |
定义处(源文件) | \subsys\usb\usb_device.c |
声明处(头文件) | \include\usb\usb_device.h |
示例1:实例化一个HID类设备
ATS350B\subsys\usb\class\hid\core.c
USBD_CFG_DATA_DEFINE(hid) struct usb_cfg_data hid_config = { .usb_device_description = NULL, .interface_config = hid_interface_config, .interface_descriptor = &hid_cfg.if0, .cb_usb_status = hid_status_cb, .interface = { .class_handler = hid_class_handle_req, .custom_handler = hid_custom_handle_req, .payload_data = NULL, }, .num_endpoints = ARRAY_SIZE(hid_ep_data), .endpoint = hid_ep_data, };
int usb_hid_init(void) { ……………………………………………. /* Initialize the USB driver with the right configuration */ ret = usb_set_config(&hid_config); if (ret < 0) { SYS_LOG_ERR("Failed to config USB"); return ret; } ……………………………………………..
} |
3.2 USB设备复位函数
函数原型 | int usb_deconfig(void) { /* unregister descriptors */ usb_register_descriptors(NULL);
/* unegister standard request handler */ usb_register_request_handler(REQTYPE_TYPE_STANDARD, NULL, NULL);
/* unregister class request handlers for each interface*/ usb_register_request_handler(REQTYPE_TYPE_CLASS, NULL, NULL);
/* unregister class request handlers for each interface*/ usb_register_custom_req_handler(NULL);
/* unregister status callback */ usb_register_status_callback(NULL);
/* Reset USB controller */ usb_dc_reset();
return 0; } |
函数功能 | 将 USB 设备还原到初始状态。 |
参数 | 无 |
返回值 | 成功,则返回0;失败,则返回一个负的错误代码值。 |
定义处(源文件) | \subsys\usb\usb_device.c |
声明处(头文件) | \include\usb\usb_device.h |
从函数原型里可以看出,对一个USB设备进行复位,需要进行的步骤:清空描述符的注册、取消标准请求处理句柄函数的注册、取消接口的类请求处理句柄函数的注册、取消状态回调函数的注册、最后是复位USB控制器[核心层调用驱动层],进入默认状态。
usb_set_config()和usb_deconfig()是成对使用的,并且逻辑上实现相反的功能。和驱动层的电气使能、卸载设备;设置断点STALL、清除端点STALL;端点使能、端点禁止等组合是类似的。
3.3 USB设备使能函数
函数原型 | int usb_enable(struct usb_cfg_data *config) { } |
函数功能 | 使能USB控制器,功能和usb_set_config()是类似,用法一样,都是传入配置结构体的地址。先实例化一个配置结构体,把结构体的地址作为参数传入。 |
参数 | Config:指向配置结构体的指针。 |
返回值 | 使能成功,则返回0;失败,则返回一个负的错误代码值。 |
定义处(源文件) | \subsys\usb\usb_device.c |
声明处(头文件) | \include\usb\usb_device.h |
示例1:
int usb_hid_init(void) { ………………………………………………….. /* Enable USB driver */ ret = usb_enable(&hid_config); if (ret < 0) { SYS_LOG_ERR("Failed to enable USB"); return ret; } ………………………………………………………… } |
3.4 USB设备禁止函数
函数原型 | int usb_disable(void) { int ret;
if (true != usb_dev.enabled) { /*Already disabled*/ return 0; }
ret = usb_dc_detach(); if (ret < 0) return ret;
/* Disable VBUS if needed */ usb_vbus_set(false);
usb_dev.enabled = false;
return 0; } |
函数功能 | 禁止成功后,USB 模块的时钟在硬件上被关闭,之后就不能产生中断。 |
参数 | Void |
返回值 | 成功,则返回0;失败,则返回一个负的错误代码值。 |
定义处(源文件) | \subsys\usb\usb_device.c |
声明处(头文件) | \include\usb\usb_device.h |
3.5 USB端点写数据函数
函数原型 | int usb_write(u8_t ep, const u8_t *data, u32_t data_len,u32_t *bytes_ret) { /*封装的是控制器驱动层的API*/ return usb_dc_ep_write(ep, data, data_len, bytes_ret); } |
函数功能 | Core层的端点写数据函数,内部封装的是驱动层的端点写数据函数。 |
参数 | 参照控制器驱动层 |
返回值 | 成功,则返回0;失败,则返回一个负的错误代码值。 |
定义处(源文件) | \subsys\usb\usb_device.c |
声明处(头文件) | \include\usb\usb_device.h |
3.6 USB端点读数据函数
函数原型 | int usb_read(u8_t ep, u8_t *data, u32_t max_data_len,u32_t *ret_bytes) { /*封装的是控制器驱动层的API*/ return usb_dc_ep_read(ep, data, max_data_len, ret_bytes); } |
函数功能 | Core层的端点读数据函数,内部封装的是驱动层的端点读数据函数。 |
参数 | 参照控制器驱动层 |
返回值 | 成功,则返回0;失败,则返回一个负的错误代码值。 |
定义处(源文件) | \subsys\usb\usb_device.c |
声明处(头文件) | \include\usb\usb_device.h |
3.7 USB设置端点STALL函数
函数原型 | int usb_ep_set_stall(u8_t ep) { /*封装的是控制器驱动层的API*/ return usb_dc_ep_set_stall(ep); } |
函数功能 | 参照控制器驱动层 |
参数 | 参照控制器驱动层 |
返回值 | 成功,则返回0;失败,则返回一个负的错误代码值。 |
定义处(源文件) | \subsys\usb\usb_device.c |
声明处(头文件) | \include\usb\usb_device.h |
3.8 USB清除端点STALL函数
函数原型 | int usb_ep_clear_stall(u8_t ep) { /*封装的是控制器驱动层的API*/ return usb_dc_ep_clear_stall(ep); } |
函数功能 | 参照控制器驱动层 |
参数 | 参照控制器驱动层 |
返回值 | 成功,则返回0;失败,则返回一个负的错误代码值。 |
定义处(源文件) | \subsys\usb\usb_device.c |
声明处(头文件) | \include\usb\usb_device.h |
3.9 USB端点读等待函数
函数原型 | int usb_ep_read_wait(u8_t ep, u8_t *data, u32_t max_data_len, u32_t *ret_bytes) { return usb_dc_ep_read_wait(ep, data, max_data_len, ret_bytes); } |
函数功能 | 参照控制器驱动层 |
参数 | 参照控制器驱动层 |
返回值 | 成功,则返回0;失败,则返回一个负的错误代码值。 |
定义处(源文件) | \subsys\usb\usb_device.c |
声明处(头文件) | \include\usb\usb_device.h |
3.10 USB端点读继续函数
函数原型 | int usb_ep_read_continue(u8_t ep) { return usb_dc_ep_read_continue(ep); } |
函数功能 | 参照控制器驱动层 |
参数 | 参照控制器驱动层 |
返回值 | 成功,则返回0;失败,则返回一个负的错误代码值。 |
定义处(源文件) | \subsys\usb\usb_device.c |
声明处(头文件) | \include\usb\usb_device.h |
3.11 小结
(1)核心层(Core)的3.5~3.10 API,内部封装的都是控制器驱动层的API。
(2)最上面一层,设备类驱动层或者是纯应用层书写API接口的规范应该是:向下封装。也就是应用层调用核心层,核心层再调用最底层控制器驱动层。
示例1:HID类设备发数据到主机,以usb_audio_dongle为例。
\samples\actions_sdk_demo\usb_audio_dongle\src\usb_handler.c
//类设备自己封装的,发数据的API int usb_hid_tx(const u8_t *buf, u16_t len) { u32_t wrote; int ret; /* wait one interval at most, unit: 10us */ int count = CONFIG_HID_INTERRUPT_EP_INTERVAL * 100;
do { ret = hid_int_ep_write(buf, len, &wrote); if (ret == -EAGAIN) { k_busy_wait(10); } } while ((ret == -EAGAIN) && (--count > 0));
if (ret) { SYS_LOG_ERR("ret: %d", ret); } else if (!ret && wrote != len) { SYS_LOG_ERR("wrote: %d, len: %d", wrote, len); } return ret; } //core.c里面的hid_int_ep_write()调用的是核心层的usb_write()
//核心层的usb_write()封装的是控制器驱动层的usb_dc_ep_write()
|
3.12 数据传输完成的回调函数声明
/** * Callback function signature for transfer completion. */ typedef void (*usb_transfer_callback)(u8_t ep, int tsize, void *priv); |
3.13 开始一个传输
函数原型 | int usb_transfer(u8_t ep, u8_t *data, size_t dlen, unsigned int flags, usb_transfer_callback cb, void * priv) {
} |
函数功能 | 启动与数据缓冲区之间的USB传输。此函数是异步的,可以用在IRQ上下文中。当线程上下文中的传输完成(或传输错误)时,指定的回调函数(第5个参数)被调用。 |
参数 | Ep:端点地址 Data:指向缓存区的指针,可以向缓存区存储数据,也可以从缓存区里面读取数据。 Dlen:数据缓存区的大小 Flags:指的是传输的标志。定义如下: /* USB transfer flags */ #define USB_TRANS_READ BIT(0) /** Read transfer flag */ #define USB_TRANS_WRITE BIT(1) /** Write transfer flag */ #define USB_TRANS_NO_ZLP BIT(2) /** No zero-length packet flag */ Cb:数据传输完成或者传输出错的回调函数。 Priv:指向数据缓存区的指针,如果回调函数被回调,则该指针会传给回调函数(在回调函数里面就可以知道,到底成功传输了多少数据)。
|
返回值 | 成功,则返回0;失败,则返回一个负的错误代码值。 |
定义处(源文件) | \subsys\usb\usb_device.c |
声明处(头文件) | \include\usb\usb_device.h |
3.14 同步传输
函数原型 | int usb_transfer_sync(u8_t ep, u8_t *data, size_t dlen, unsigned int flags) {
} |
函数功能 | 启动一个传输,并且等待传输完成。 |
参数 | Ep:端点地址 Data:指向缓存区的指针,可以向缓存区存储数据,也可以从缓存区里面读取数据。 Dlen:数据缓存区的大小 Flags:指的是传输的标志。定义如下: /* USB transfer flags */ #define USB_TRANS_READ BIT(0) /** Read transfer flag */ #define USB_TRANS_WRITE BIT(1) /** Write transfer flag */ #define USB_TRANS_NO_ZLP BIT(2) /** No zero-length packet flag */
|
返回值 | 成功,则返回0;失败,则返回一个负的错误代码值。 |
定义处(源文件) | \subsys\usb\usb_device.c |
声明处(头文件) | \include\usb\usb_device.h |
3.15 取消一个传输
函数原型 | void usb_cancel_transfer(u8_t ep) {
} |
函数功能 | 取消指定端点上正在进行的任何传输 |
参数 | Ep:端点号
|
返回值 | 成功,则返回0;失败,则返回一个负的错误代码值。 |
定义处(源文件) | \subsys\usb\usb_device.c |
声明处(头文件) | \include\usb\usb_device.h |
下一篇: https://blog.csdn.net/qq_40088639/article/details/110121746