USB设备栈架构概述02:设备核心层

上一篇: 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

其中,几张重要的图如下

https://www.beyondlogic.org/usbnutshell/desctree.gif

                                                                                                      图1

 

https://www.beyondlogic.org/usbnutshell/intftree.gif

                                                                                                   图2

https://www.beyondlogic.org/usbnutshell/confsize.gif

                                                                                                 图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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值