USB设备栈架构概述01:控制器驱动层

USB设备栈架构:基于Zephyr1.9.0版本

Zephyr嵌入式操作系统的USB设备栈分为三层:

(1) USB设备控制器驱动层 (USB Device Controller)-----跟硬件相关的寄存器配置

(2) USB设备核心驱动层   (USB Device Core Layer)-----跟硬件无关的

(3) USB设备设备类驱动层 (USB Device Class Layer)-----跟硬件无关的

一、控制器驱动层API简介

设备控制器驱动层实现了直接与底层硬件打交道的逻辑(配置IC的寄存器)。所有的设备控制器驱动都应当实现 usb_dc.h 中描述的 API。这样做的好处是当新的 USB 设备集成到系统中时不需要修改上层内容。

所有跟USB控制器驱动相关的API都在头文件usb_dc.h中有声明。DcDevice_controller

源文件目录:\driver目录,头文件目录:\include\driver目录。

1. USB设备电气使能函数

函数原型

int usb_dc_attach(void)

{

……………………………………………….

return 0

}

函数功能

使能USB的PLL,如果使能成功,则USB 设备能够在 USB 总线上进行收发数据、产生中断。

参数

返回值

成功,则返回0;使能失败,则返回一个负的错误代码值。

定义处(源文件)

\drivers\usb\device\aotg_udc.c

声明处(头文件)

\include\drivers\usb\usb_dc.h

调用示例1:

int usb_enable(struct usb_cfg_data *config)

{

……………………………………………………………..

int ret;

ret = usb_dc_attach();

if (ret < 0)

         return ret;

………………………………………………………………

         return 0;

}

 

2. 卸载USB设备函数

函数原型

int usb_dc_detach (void)

{

……………………………………………….

return 0

}

函数功能

卸载USB设备,执行成功,则USB设备的硬件PLL会被断电,设备失去通信功能。

参数

返回值

卸载成功,则返回0;失败,则返回一个负的错误代码值。

定义处(源文件)

\drivers\usb\device\aotg_udc.c

声明处(头文件)

\include\drivers\usb\usb_dc.h

调用示例1:

int usb_disable(void)

{

         int ret;

………………………………………………………….

         ret = usb_dc_detach();

         if (ret < 0)

                   return ret;

………………………………………………………….

         return 0;

}

注:在USB设备栈中,这两个函数成对使用,并且分别在对应的usb_enable()和usb_disable()中被调用。

 

3. USB控制器复位函数

函数原型

int usb_dc_reset(void)

{

}

函数功能

把USB 设备复位至默认状态

参数

返回值

复位成功,则返回0;失败,则返回一个负的错误代码值。

定义处(源文件)

\drivers\usb\device\aotg_udc.c

声明处(头文件)

\include\drivers\usb\usb_dc.h

调用示例1:

/* 删除配置函数 */

int usb_deconfig(void)

{

…………………………………………………

/* Reset USB controller */

usb_dc_reset();

………………………………………………

}

 

4. 设置Device地址函数

函数原型

int usb_dc_set_address(const u8_t addr)

{

}

函数功能

该函数用于设置 USB 设备的地址。

参数

Addr:设备的地址。

返回值

设置成功,则返回0;设置失败,则返回一个负的错误代码值。

定义处(源文件)

\drivers\usb\device\aotg_udc.c

声明处(头文件)

\include\drivers\usb\usb_dc.h

调用示例1:在标准设备请求的回调函数中被调用

static bool usb_handle_std_device_req(struct usb_setup_packet *setup,

                   s32_t *len, u8_t **data_buf)

{

……………………………………………………………………..

switch (setup->bRequest)  {

………………………………………………………………………

/* 当主机的请求是:设置地址的请求时(这是一个标准请求) */

case REQ_SET_ADDRESS:

                   SYS_LOG_ERR("REQ_SET_ADDRESS, addr=0x%x\n", setup->wValue);

                   usb_dc_set_address(setup->wValue);

                   break;

}

}

 

5. 注册控制器的状态回调函数

函数原型

int usb_dc_set_status_callback(const usb_dc_status_callback cb)

{

usb_aotg_ctrl.status_cb = cb;

         return 0;

}

函数功能

所注册的回调函数用于报告设备控制器的状态。状态码由枚举 usb_dc_status_code 进行描述。

参数

参数是一个函数指针(注册的时候,传入函数名)

返回值

注册成功,则返回0;失败,则返回一个负的错误代码值。

定义处(源文件)

\drivers\usb\device\aotg_udc.c

声明处(头文件)

\include\drivers\usb\usb_dc.h

usb_dc_status_code的数据结构如下:

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 连接状态

调用示例1:以HID设备状态回调为例子(示例仅供参考)

实例化一个HID设备配置

在core.c里面,其中就指定了设备的状态回调函数(hid_status_cb)。如下:

定义处:\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,

};

函数hid_status_cb()在core.c中定义如下:

而hid_config在usb_hid_init()中被调用,如下:

针对usb_audio_dongle这个demo来说,usb_hid_init()又被usb_handler.c中的composite_pre_init()调用如下:

\actions_sdk_demo\usb_audio_dongle\src\usb_handler.c

倒过来分析整个流程:

//初始化入口处

composite_pre_init()[usb_handler.c]----------->调用----------->usb_hid_init()[core.c]

usb_hid_init()[core.c] ----------->调用----------->usb_set_config()/usb_enable()[usb_device.c]

 

调用usb_set_config() 和usb_enable(),传入的是整个结构体。

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;

}

HID类设备的配置实例结构体里面,指定了状态回调函数。

USBD_CFG_DATA_DEFINE(hid) struct usb_cfg_data hid_config = {

……………………………………………………………………………

         .cb_usb_status = hid_status_cb,

………………………………………………………………………………

         };

 

/*HID类设备状态回调函数*/

static void hid_status_cb(enum usb_dc_status_code status, u8_t *param)

{

         /* Check the USB status and do needed action if required */

         switch (status) {

         case USB_DC_ERROR:

                   SYS_LOG_ERR("USB device error");

                   break;

         case USB_DC_RESET:

                   SYS_LOG_ERR("USB device reset detected");

                   break;

         case USB_DC_CONNECTED:

                   SYS_LOG_ERR("USB device connected");

                   break;

         case USB_DC_CONFIGURED:

                   SYS_LOG_ERR("USB device configured");

                   break;

         case USB_DC_DISCONNECTED:

                   SYS_LOG_ERR("USB device disconnected");

                   break;

         case USB_DC_SUSPEND:

                   SYS_LOG_ERR("USB device suspended");

                   break;

         case USB_DC_RESUME:

                   SYS_LOG_ERR("USB device resumed");

                   break;

         case USB_DC_UNKNOWN:

         default:

                   SYS_LOG_ERR("USB unknown state");

                   break;

         }

}

usb_set_config()里调用了状态注册函数:\subsys\usb\usb_device.c

int usb_set_config(struct usb_cfg_data *config)

{

………………………………………………………………………………………………….

         /* register status callback */

         if (config->cb_usb_status != NULL) {

                   usb_register_status_callback(config->cb_usb_status);

         }

…………………………………………………………………………………………………..

         return 0;

}

usb_enable()里调用了状态注册函数:ATS350B\subsys\usb\usb_device.c

int usb_set_config(struct usb_cfg_data *config)

{

………………………………………………………………………………………………….

         /* register status callback */

         if (config->cb_usb_status != NULL) {

                   usb_register_status_callback(config->cb_usb_status);

         }

…………………………………………………………………………………………………..

         return 0;

}

总结:

(1)最终起重要作用的还是这行代码:

usb_register_status_callback(config->cb_usb_status);

 

(2)UAC等其他类,注册状态回调函数的流程跟HID类设备是一样的。

 

6. 端点配置函数

函数原型

int usb_dc_ep_configure(const struct usb_dc_ep_cfg_data * const ep_cfg)

{

}

函数功能

该函数用于配置一个端点

参数

端点配置结构体指针(传入端点,一个配置用一个结构体来描述)

返回值

配置成功,则返回0;失败,则返回一个负的错误代码值。

定义处(源文件)

\drivers\usb\device\aotg_udc.c

声明处(头文件)

\include\drivers\usb\usb_dc.h

调用示例1:配置控制输出/输入端点

\subsys\usb\usb_device.c

static int usb_composite_init(struct device *dev)

{

   ………………………………………………………………………….

   struct usb_dc_ep_cfg_data ep0_cfg;

   ……………………………………………………………………….

   /* Configure control EP */

         ep0_cfg.ep_mps = MAX_PACKET_SIZE0;

         ep0_cfg.ep_type = USB_DC_EP_CONTROL;

 

         ep0_cfg.ep_addr = USB_CONTROL_OUT_EP0;

         ret = usb_dc_ep_configure(&ep0_cfg);

         if (ret < 0) {

                   return ret;

         }

 

         ep0_cfg.ep_addr = USB_CONTROL_IN_EP0;

         ret = usb_dc_ep_configure(&ep0_cfg);

         if (ret < 0) {

                   return ret;

         }

}

 

//先填充结构体,再传入整个结构体。

//一个结构体足以描述一个端点

 

7. 端点配置检查函数

函数原型

int usb_dc_ep_check_cap(const struct usb_dc_ep_cfg_data * const cfg)

{

}

函数功能

该函数用于检查某个端点的配置是否正确(类型、端点传输的最大包长、地址是否超出范围等)

参数

端点配置结构体指针(传入端点,一个配置用一个结构体来描述)

返回值

成功,则返回0;失败,则返回一个负的错误代码值。

定义处(源文件)

\drivers\usb\device\aotg_udc.c

声明处(头文件)

\include\drivers\usb\usb_dc.h

调用示例1:这个函数在源码中用得比较少,可以自己尝试:先检查某个端点设置是否合法,如果合法,再进行配置。

 

8. 设置STALL

函数原型

int usb_dc_ep_set_stall(const u8_t ep)

{

}

函数功能

为端点设置STALL条件。STALL:暂停

参数

端点的地址

返回值

设置成功,则返回0;失败,则返回一个负的错误代码值。

定义处(源文件)

\drivers\usb\device\aotg_udc.c

声明处(头文件)

\include\drivers\usb\usb_dc.h

 

9. 清除STALL

函数原型

int usb_dc_ep_clear_stall(const u8_t ep)

{

}

函数功能

为端点清除STALL条件。STALL:暂停

参数

端点的地址

返回值

成功,则返回0;失败,则返回一个负的错误代码值。

定义处(源文件)

\drivers\usb\device\aotg_udc.c

声明处(头文件)

\include\drivers\usb\usb_dc.h

 

10. 判断某个端点是否是STALL

函数原型

int usb_dc_ep_is_stalled(const u8_t ep, u8_t *const stalled)

{

}

函数功能

查询某个端点是否是stall

参数

Ep:端点的地址  参数2:stall的状态

返回值

判断成功,则返回0;失败,则返回一个负的错误代码值。

定义处(源文件)

\drivers\usb\device\aotg_udc.c

声明处(头文件)

\include\drivers\usb\usb_dc.h

 

11. 端点停止函数

函数原型

int usb_dc_ep_halt(const u8_t ep)

{

}

函数功能

停止端点

参数

Ep:端点的地址

返回值

成功,则返回0;失败,则返回一个负的错误代码值。

定义处(源文件)

\drivers\usb\device\aotg_udc.c

声明处(头文件)

\include\drivers\usb\usb_dc.h

 

12. 端点使能函数

函数原型

int usb_dc_ep_enable(const u8_t ep)

{

}

函数功能

该函数用于使能端点,如果使能成功,则相应终点的中断会被使能,并且收发数据已就绪(可进行数据收发)。

参数

Ep:端点的地址

返回值

使能成功,则返回0;使能失败,则返回一个负的错误代码值。

定义处(源文件)

\drivers\usb\device\aotg_udc.c

声明处(头文件)

\include\drivers\usb\usb_dc.h

示例1:使能控制端点0

int usb_enable(struct usb_cfg_data *config)

{

…………………………………………………………………………………………

/* enable control EP */

//#define USB_CONTROL_OUT_EP0         0

         ret = usb_dc_ep_enable(USB_CONTROL_OUT_EP0);

         if (ret < 0)

                   return ret;

 

    //#define USB_CONTROL_IN_EP0          0x80

         ret = usb_dc_ep_enable(USB_CONTROL_IN_EP0);

         if (ret < 0)

                   return ret;

……………………………………………………………………………………………..

 

}

 

13. 端点禁止函数

函数原型

int usb_dc_ep_disable (const u8_t ep)

{

}

函数功能

该函数用于禁止端点,禁止成功后,相应端点的中断被禁止,不可以进行收发数据。

参数

Ep:端点的地址

返回值

禁止成功,则返回0;禁止失败,则返回一个负的错误代码值。

定义处(源文件)

\drivers\usb\device\aotg_udc.c

声明处(头文件)

\include\drivers\usb\usb_dc.h

 

14. 清除端点的FIFO函数

函数原型

int usb_dc_ep_flush(const u8_t ep);

{

 

}

函数功能

清除端点的FIFO

参数

Ep:端点的地址

返回值

成功,则返回0;失败,则返回一个负的错误代码值。

定义处(源文件)

\drivers\usb\device\aotg_udc.c

声明处(头文件)

\include\drivers\usb\usb_dc.h

 

15. 端点写数据函数

函数原型

int usb_dc_ep_write(const u8_t ep, const u8_t *const data,

const u32_t data_len, u32_t * const ret_bytes)

{

 

}

函数功能

向指定的端点写数据

参数

Ep:端点地址

Data:指向要发送数据Buffer的指针

data_len:要发送的数据长度。当发送的是0长度的状态数据包时,长度值可设置为0。

ret_bytes:计划传输的字节数。如果想把要发送的数据Buffer里面的数据全都发出去,那么,就设置为NULL。---指针

返回值

成功,则返回0;使能失败,则返回一个负的错误代码值。

定义处(源文件)

\drivers\usb\device\aotg_udc.c

声明处(头文件)

\include\drivers\usb\usb_dc.h

 

16. 端点读数据函数

函数原型

int usb_dc_ep_read(const u8_t ep, u8_t *const data,

const u32_t max_data_len, u32_t *const read_bytes)

{

}

函数功能

读取端点数据。该函数被端点句柄函数所调用,端点产生一个输出中断之后,应用程序使用所提供的 usb_ep_callback 函数间接调用本函数读取端点FIFO这个数据结构里面的数据。调用该函数,可清除端点的NAK,以至于能从主机接收更多的数据。

参数

Ep:端点地址

Data:是一个指针,该指针指向要把数据写入的目标缓存区。

max_data_len:读取的数据的最大字节数。

ret_bytes:读取的字节数(这是个指针)。如果数据指针data指向NULL,并且max_data_len的值为0,那么就返回可供读取的字节数。

返回值

读数据成功,则返回0;读失败,则返回一个负的错误代码值。

定义处(源文件)

\drivers\usb\device\aotg_udc.c

声明处(头文件)

\include\drivers\usb\usb_dc.h

示例1:以读取主机下发的第一个数据包SETUP包为例

ATS350B\subsys\usb\usb_device.c

/*端点句柄函数*/

static void usb_handle_control_transfer(u8_t ep,

                   enum usb_dc_ep_cb_status_code ep_status)

{

………………………………………………………………………………………………………

         struct usb_setup_packet *setup = &usb_dev.setup;

    SYS_LOG_ERR("Transfer: ep=0x%02x, status=0x%02x\n",ep,ep_status);

………………………………………………………………………………………………………..

         if (ep == USB_CONTROL_OUT_EP0 && ep_status == USB_DC_EP_SETUP)

 {

                   SYS_LOG_ERR("Data Direction: Host---->Device\n");

                   /*

                    * OUT transfer, Setup packet,

                    * reset request message state machine

                    */

         /*先读取来自主机的第一个数据包(SETUP包[令牌包])*/

                    if (usb_dc_ep_read(ep, (u8_t *)setup, sizeof(*setup), NULL) < 0)

{

                               SYS_LOG_DBG("Read Setup Packet failed\n");

                               usb_dc_ep_set_stall(USB_CONTROL_IN_EP0);

                                return;

                       }

………………………………………………………………………………………………………

}

 

17. 注册端点状态的回调函数

函数原型

int usb_dc_ep_set_callback(const u8_t ep, const usb_dc_ep_callback cb)

{

}

函数功能

用该函数来注册端点的回调函数。比如当端点收到数据,并且数据有效的时候;或者端点完成一次数据传输的时候,都需要一系列的回调函数,要使用这些函数来注册这些回调函数。

如果应用程序代码不需要回调,则为空。回调状态代码由usb_dc_ep_cb_status_code这个数据结构来描述。

 

 

 

在源码中,端点回调状态代码定义为枚举数据结构:

/**

 * USB Endpoint Callback Status Codes

 */

/*

逻辑端点状态回调码:

(1)收到令牌包(SETUP数据包)

(2)端点上发生了输出事务,并且数据已经可读[收到来自Host的数据]。

(3)端点上结束了一次输入事务[结束了一次将数据发到Host的过程]

*/

enum usb_dc_ep_cb_status_code {

         USB_DC_EP_SETUP,    /* SETUP received */

         /* Out transaction on this EP, data is available for read */

         USB_DC_EP_DATA_OUT,

         USB_DC_EP_DATA_IN,  /* In transaction done on this EP */

};

 

参数

Ep:端点地址

Cb:  回调函数名。回调的入口函数,回调的入口函数,书写的格式,定义如下。

 

所在头文件:usb_dc.h

注意它的两个参数,第一个参数是端点号;第二个参数是端点状态。也就是说,如果注册了一个端点状态回调函数,那么当端点状态发生改变的时候,回调函数会被回调,并且可以从回调函数的参数二中获得当前的端点状态,从而在回调函数实现对应的逻辑处理。

回调函数这一块,几乎都是这么一个套路,有注册回调函数的函数,有回调函数,如果发生符合回调的事件,那么回调函数会被调用,一般来说,回调函数被调用时,回调函数内部会传入一些有特定意义的数据,比如:状态码、发送成功的字节数、服务器返回的数据等。

 

返回值

注册成功,则返回0;注册失败,则返回一个负的错误代码值。

定义处(源文件)

\drivers\usb\device\aotg_udc.c

声明处(头文件)

\include\drivers\usb\usb_dc.h

 

18. 端点读等待函数

函数原型

int usb_dc_ep_read_wait(u8_t ep, u8_t *data, u32_t max_data_len,

u32_t *read_bytes)

{

 

}

函数功能

这个函数和端点读数据函数usb_dc_ep_read很类似,形参都是一样的。不同的是,它不会清除端点的NAK信号,这就导致调用方(比如是设备这一端)没法响应更多的请求(收到一次数据之后,就没法收了),直到它处理完这一次的数据。调用者应该通过调用usb_dc_ep_read_continue()来重新激活端点,才能进行数据的接收。

参数

Ep:端点地址

Data:是一个指针,该指针指向要把数据写入的目标缓存区。

max_data_len:读取的数据的最大字节数。

ret_bytes:读取的字节数(这是个指针)。如果数据指针data指向NULL,并且max_data_len的值为0,那么就返回可供读取的字节数。

返回值

读数据成功,则返回0;读失败,则返回一个负的错误代码值。

定义处(源文件)

\drivers\usb\device\aotg_udc.c

声明处(头文件)

\include\drivers\usb\usb_dc.h

 

19. 端点读继续函数

函数原型

int usb_dc_ep_read_continue(u8_t ep)

{

 

}

函数功能

清除端点NAK,使得端点能够接收来自主机的更多数据。通常来说,如果确认用户可以接收更多数据之后,在调用usb_dc_ep_read_wait()之后再调用该函数。

因此,这两个函数成对调用,一起充当数据流控制机制。

参数

Ep:端点地址

返回值

读数据成功,则返回0;读失败,则返回一个负的错误代码值。

定义处(源文件)

\drivers\usb\device\aotg_udc.c

声明处(头文件)

\include\drivers\usb\usb_dc.h

 

20. 获取端点最大包长函数

函数原型

int usb_dc_ep_mps(u8_t ep)

{

 

}

函数功能

获得端点最大包长

参数

Ep:端点地址

返回值

端点最大包长

定义处(源文件)

\drivers\usb\device\aotg_udc.c

声明处(头文件)

\include\drivers\usb\usb_dc.h

 

21. 小结

(1) 控制器驱动层是直接与底层硬件打交道的,所以是USB设备栈的最下面一层。

(2) 和控制器驱动层相关的源码文件(一个源文件、一个头文件)是:

\drivers\usb\device\aotg_udc.c

\include\drivers\usb\usb_dc.h

(3) 很多API都是成对使用的:

设备电气使能函数<---------------->卸载设备函数

设置STALL函数  <---------------->清除STALL函数

端点使能函数    <---------------->端点禁止函数

端点读数据函数  <---------------->端点写数据函数

端点读等待函数  <---------------->端点读继续函数

(4) 注意区分设备状态(也就是控制器状态)和端点状态。

(5) 大部分的API都是直接对端点进行操作,因为,端点(逻辑端点和物理端点)是最底层。

(6) 不同版本的zephyr中API名称或者参数可能有变化,但是调用关系和回调的流程基本上应该是一样的。

下一篇:https://blog.csdn.net/qq_40088639/article/details/110120639

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值