跳转至

USB

USB 驱动主要实现设备驱动的底层细节,并为上层提供一套标准的 API 接口以供使用。USB模块主要特性如下:

  • Complies with USB 2.0 Specification
  • Supports High-Speed (HS, 480-Mbps), Full-Speed (FS, 12-Mbps), and Low-Speed (LS, 1.5-Mbps) in Host mode
  • Supports High-Speed (HS, 480 Mbps), Full-Speed (FS, 12 Mbps) in Device mode
  • Supports the UTMI+ Level 3 interface. The 8-bit bidirectional data buses are used
  • Supports bi-directional endpoint0 for Control transfer
  • Supports up to 8 User-Configurable Endpoints for Bulk, Isochronous and Interrupt bi-directional transfers (Endpoint1, Endpoint2, Endpoint3, Endpoint4)
  • Supports up to (4KB+64Bytes) FIFO for EPs (Including EP0)
  • Supports High-Bandwidth Isochronous & Interrupt transfers
  • Automated splitting/combining of packets for Bulk transfers
  • Supports point-to-point and point-to-multipoint transfer in both Host and Peripheral mode
  • Includes automatic ping capabilities
  • Soft connect/disconnect function
  • Performs all transaction scheduling in hardware
  • Power Optimization and Power Management capabilities
  • Includes interface to an external Normal DMA controller for every Eps

模块配置

R128 仅有一个 USB,HAL 驱动支持两个及以上 USB 外设,故部分配置为空。

Kernel Setup --->
    Drivers Setup --->
        SoC HAL Drivers --->
            USB devices --->
                [*] enable USB driver
                [*] USB HOST  --->
                    [*]   Support usb host ehci0 # USB0 配置
                    [*]   Support usb host ohci0
                    [ ]   Support usb host ehci1 # USB1 配置
                    [ ]   Support usb host ohci1 
                    [*]     Mass Storage support
                    [*]       USB CD support
                    [ ]     Carplay host support
                    [ ]     UVC support
                    [ ]     HID support
                [*] USB DEVICE  --->
                    [ ]   enable dma for udc driver
                    [*]   Support usb gadget driver
                        usb gadget driver (adb gadget driver)  --->
                            (X) adb gadget driver
                            ( ) mtp gadget driver
                            ( ) uac gadget driver
                            ( ) ncm gadget driver
                            ( ) msc gadget driver
                [*] USB MANAGER

源码结构

.
├── Kconfig
├── Makefile
├── common               # 公共操作
│   ├── Makefile
│   ├── list_head_ext.c
│   ├── list_head_ext.h
│   ├── usb_init.c
│   ├── usb_init.h
│   ├── usb_phy.c
│   └── usb_phy.h
├── core                 # USB 核心驱动
│   ├── Makefile
│   ├── urb.c
│   ├── urb.h
│   ├── usb_core_base.c
│   ├── usb_core_base.h
│   ├── usb_core_config.c
│   ├── usb_core_config.h
│   ├── usb_core_init.c
│   ├── usb_core_init.h
│   ├── usb_core_interface.c
│   ├── usb_core_interface.h
│   ├── usb_driver_init.c
│   ├── usb_driver_init.h
│   ├── usb_gen_hcd.c
│   ├── usb_gen_hcd.h
│   ├── usb_gen_hcd_rh.c
│   ├── usb_gen_hcd_rh.h
│   ├── usb_gen_hub.c
│   ├── usb_gen_hub.h
│   ├── usb_gen_hub_base.c
│   ├── usb_gen_hub_base.h
│   ├── usb_msg.c
│   ├── usb_msg.h
│   ├── usb_msg_base.c
│   ├── usb_msg_base.h
│   ├── usb_virt_bus.c
│   └── usb_virt_bus.h
├── gadget             # gadget 相关实现
│   ├── Makefile
│   ├── function
│   │   ├── adb.c
│   │   ├── msc
│   │   │   ├── msc.c
│   │   │   ├── scsi.c
│   │   │   ├── scsi.h
│   │   │   ├── usb_slave_msc.c
│   │   │   └── usb_slave_msc.h
│   │   ├── mtp.c
│   │   └── uac.c
│   ├── gadget.c
│   ├── gadget.h
│   └── gadget_hal.c
├── hid                # hid 相关实现
│   ├── Class   
│   │   ├── Hid.c
│   │   ├── HidProtocol.c
│   │   ├── HidProtocol.h
│   │   ├── HidTransport.c
│   │   ├── HidTransport.h
│   │   └── Makefile
│   ├── Client        
│   │   ├── KeyBoard
│   │   │   ├── KeyBoard.c
│   │   │   ├── KeyBoard.h
│   │   │   └── Makefile
│   │   ├── Makefile
│   │   ├── Mouse
│   │   │   ├── Makefile
│   │   │   ├── UsbMouse.c
│   │   │   ├── UsbMouse.h
│   │   │   ├── UsbMouse_DriftControl.c
│   │   │   └── UsbMouse_DriftControl.h
│   │   └── misc_lib.c
│   ├── Include
│   │   ├── Hid.h
│   │   ├── HidFunDrv.h
│   │   ├── HidSpec.h
│   │   ├── Hid_i.h
│   │   └── misc_lib.h
│   └── Makefile
├── host               # Host 驱动
│   ├── Makefile
│   ├── ehci-hcd.c
│   ├── ehci-hub.c
│   ├── ehci-mem.c
│   ├── ehci-q.c
│   ├── ehci-sched.c
│   ├── ehci-sunxi.c
│   ├── ehci-timer.c
│   ├── ehci.h
│   ├── hci_hal.c
│   ├── ohci-hcd.c
│   ├── ohci-hub.c
│   ├── ohci-mem.c
│   ├── ohci-q.c
│   ├── ohci-sunxi.c
│   ├── ohci.h
│   ├── sunxi-hci.c
│   └── sunxi-hci.h
├── include
│   ├── audio.h
│   ├── bitops.h
│   ├── ch11.h
│   ├── ch9.h
│   ├── ehci_def.h
│   ├── endian.h
│   ├── error.h
│   ├── hcd.h
│   ├── mod_devicetable.h
│   ├── mod_usbhost.h
│   ├── storage.h
│   ├── usb.h
│   ├── usb_list.h
│   ├── usb_melis.h
│   ├── usb_os_platform.h
│   └── usb_rtos.h
├── manager           # usb 管理类
│   ├── Makefile
│   ├── sunxi_usb_board.h
│   ├── usb_hw_scan.c
│   ├── usb_hw_scan.h
│   ├── usb_manager.c
│   ├── usb_manager_common.h
│   ├── usb_msg_center.c
│   └── usb_msg_center.h
├── platform          # 芯片平台寄存器定义
│   ├── sun20iw2
│   │   ├── Makefile
│   │   ├── usb_sun20iw2.c
│   │   └── usb_sun20iw2.h
.   .   .
.   .   .
.   .   .
├── storage           # 存储器相关实现
│   ├── Class
│   │   ├── Makefile
│   │   ├── mscProtocol.c
│   │   ├── mscProtocol.h
│   │   ├── mscTransport.c
│   │   ├── mscTransport.h
│   │   ├── mscTransport_i.c
│   │   ├── msc_common.h
│   │   └── usb_msc.c
│   ├── Disk
│   │   ├── BlkDev.c
│   │   ├── BlkDev.h
│   │   ├── CD.c
│   │   ├── CD.h
│   │   ├── Disk.c
│   │   ├── LunMgr.c
│   │   ├── LunMgr_i.h
│   │   ├── Makefile
│   │   └── Scsi2.c
│   ├── Kconfig
│   ├── Makefile
│   ├── Misc
│   │   ├── Makefile
│   │   ├── usbh_buff_manager.c
│   │   ├── usbh_disk_info.c
│   │   └── usbh_disk_remove_time.c
│   └── include
│       ├── LunMgr.h
│       ├── Scsi2.h
│       ├── usb_msc.h
│       ├── usb_msc_i.h
│       ├── usbh_buff_manager.h
│       ├── usbh_disk_info.h
│       └── usbh_disk_remove_time.h
├── udc               # UDC 实现
│   ├── Makefile
│   ├── udc.c
│   ├── udc.h
│   ├── udc_hal.c
│   ├── udc_platform.h
│   ├── usb_dma.c
│   └── usb_dma.h
└── uvc                # UVC 实现
    ├── Class
    │   ├── Makefile
    │   ├── uvc.c
    │   ├── uvc_driver.c
    │   ├── uvc_driver.h
    │   ├── uvc_v4l2.c
    │   ├── uvc_video.c
    │   └── uvc_video.h
    ├── Include
    │   ├── UVC.h
    │   ├── assessibility.h
    │   ├── uvcvideo.h
    │   ├── video.h
    │   └── videodev2.h
    ├── Makefile
    ├── Misc
    │   ├── Makefile
    │   └── assessibility.c
    ├── Webcam
    │   ├── Makefile
    │   ├── usbWebcam.c
    │   ├── usbWebcam.h
    │   ├── usbWebcam_proc.c
    │   └── usbWebcam_proc.h
    └── drv_webcam
        ├── Makefile
        ├── dev_cfg
        │   ├── webcam_dev.c
        │   └── webcam_dev_i.h
        ├── drv_webcam.c
        ├── drv_webcam.h
        ├── drv_webcam_i.h
        ├── fb.h
        └── webcam_core
            ├── dev_webcam.c
            ├── dev_webcam_i.h
            ├── webcam_linklist_manager.c
            └── webcam_linklist_manager.h

模块接口说明

头文件

#include <sunxi_hal_usb.h>
#include <usb_os_platform.h>

UDC 回调事件结构体

typedef enum {
    UDC_EVENT_RX_STANDARD_REQUEST = 1,
    UDC_EVENT_RX_CLASS_REQUEST = 2,
    UDC_EVENT_RX_DATA = 3,
    UDC_EVENT_TX_COMPLETE = 4,
} udc_callback_event_t;

### UDC 错误返回枚举

typedef enum {
    UDC_ERRNO_SUCCESS = 0,
    UDC_ERRNO_CMD_NOT_SUPPORTED = -1,
    UDC_ERRNO_CMD_INVALID = -2,
    UDC_ERRNO_BUF_NULL = -3,
    UDC_ERRNO_BUF_FULL = -4,
    UDC_ERRNO_EP_INVALID = -5,
    UDC_ERRNO_RX_NOT_READY = -6,
    UDC_ERRNO_TX_BUSY = -7,
} udc_errno_t;

USB 驱动初始化

函数原型

void sunxi_usb_init(void);

参数:

返回值:

USB HOST 初始化

函数原型

int hal_usb_core_init(void);

参数:

返回值:

  • 0:成功
  • 负数:失败

USB HOST 去初始化

函数原型

int hal_usb_core_exit(void);

参数:

返回值:

  • 0:成功
  • 负数:失败

USB HOST 加载所有主机驱动(除了0)

函数原型

int hal_usb_hci_init(void);

参数:

返回值:

  • 0:成功
  • 负数:失败

USB HOST 去加载所有主机驱动(除了0)

函数原型

int hal_usb_hci_deinit(void);

参数:

返回值:

  • 0:成功
  • 负数:失败

USB HOST 加载指定主机驱动

函数原型

int hal_usb_hcd_init(int hci_num);

参数:

  • hci_num:指定主机驱动

返回值:

  • 0:成功
  • 负数:失败

USB HOST 去加载指定主机驱动

函数原型

int hal_usb_hcd_deinit(int hci_num);

参数:

  • hci_num:指定主机驱动

返回值:

  • 0:成功
  • 负数:失败

USB HOST PHY 区域显示

函数原型

void hal_hci_phy_range_show(int hci_num);

参数:

  • hci_num:指定主机驱动

返回值:

USB HOST PHY 配置区域

函数原型

void hal_hci_phy_range_set(int hci_num, int val);

参数:

  • hci_num:指定主机驱动
  • val:配置的值

返回值:

USB HOST 显示驱动能力

函数原型

void hal_hci_driverlevel_show(int hci_num);

参数:

  • hci_num:指定主机驱动

返回值:

USB HOST 配置驱动能力

函数原型

void hal_hci_driverlevel_adjust(int hci_num, int driverlevel);

参数:

  • hci_num:指定主机驱动
  • driverlevel:配置的驱动能力

返回值:

USB HOST 眼图测试

函数原型

void hal_hci_ed_test(int hci_num, const char *buf, unsigned int count);

参数:

  • hci_num:指定主机驱动
  • buf:传输的数据
  • count:数据长度

返回值:

USB UDC 初始化

函数原型

int32_t hal_udc_init(void);

参数:

返回值:

  • 0:成功
  • 负数:失败

USB UDC 去初始化

函数原型

int32_t hal_udc_deinit(void);

参数:

返回值:

  • 0:成功
  • 负数:失败

USB UDC EP 读操作

函数原型

int32_t hal_udc_ep_read(uint8_t ep_addr, void *buf, uint32_t len);

参数:

  • ep_addr:ep地址
  • buf:读取的数据指针
  • len:读取长度

返回值:

  • 0:成功
  • 负数:失败

USB UDC EP 写操作

函数原型

int32_t hal_udc_ep_write(uint8_t ep_addr, void *buf , uint32_t len);

参数:

  • ep_addr:ep地址
  • buf:读取的数据指针
  • len:读取长度

返回值:

  • 0:成功
  • 负数:失败

USB UDC 初始化设备描述符

函数原型

void hal_udc_device_desc_init(void *device_desc);

参数:

  • device_desc:设备描述符数据

返回值:

USB UDC 配置描述符初始化

函数原型

void hal_udc_config_desc_init(void *config_desc, uint32_t len);

参数:

  • config_desc:配置描述符指针
  • len:长度

返回值:

USB UDC 字符串描述符初始化

函数原型

void hal_udc_string_desc_init(const void *string_desc);

参数:

  • string_desc:配置字符串描述符的指针

返回值:

USB UDC 注册回调函数

函数原型

void hal_udc_register_callback(udc_callback_t user_callback);

参数:

  • user_callback:回调函数

返回值:

USB UDC 禁用 EP

函数原型

void hal_udc_ep_disable(uint8_t ep_addr);

参数:

  • ep_addr:地址

返回值:

USB UDC 启用 EP

函数原型

void hal_udc_ep_enable(uint8_t ep_addr, uint16_t maxpacket, uint32_t ts_type);

参数:

  • ep_addr:地址
  • maxpacket:最大包大小
  • ts_type:模式

返回值:

USB UDC 设置 EP 发送/接收 buffer

函数原型

void hal_udc_ep_set_buf(uint8_t ep_addr, void *buf, uint32_t len);

参数:

  • ep_addr:地址
  • buf:buf的指针
  • len:buf的长度

返回值:

USB UDC 显示驱动能力

函数原型

void hal_udc_driverlevel_show(void);

参数:

返回值:

USB UDC 调整驱动能力

函数原型

void hal_udc_driverlevel_adjust(int driverlevel);

参数:

  • driverlevel:驱动能力

返回值:

USB UDC 显示范围

函数原型

void hal_udc_phy_range_show(int usbc_num);

参数:

  • usbc_num:usb控制器号

返回值:

USB UDC 配置范围

函数原型

void hal_udc_phy_range_set(int usbc_num, int val);

参数:

  • usbc_num:usb控制器号
  • val:值

返回值:

USB UDC 眼图测试

函数原型

void hal_udc_ed_test(const char *buf, size_t count);

参数:

  • buf:测试使用的buf
  • count:数量

返回值:

USB Manager 初始化

函数原型

int hal_usb_manager_init(void);

参数:

返回值:

  • 0:成功
  • 负数:失败

USB Manager 去初始化

函数原型

int hal_usb_manager_deinit(void);

参数:

返回值:

  • 0:成功
  • 负数:失败

USB Gadget 初始化

函数原型

int hal_gadget_init(void);

参数:

返回值:

  • 0:成功
  • 负数:失败

USB Gadget 去初始化

函数原型

void hal_gadget_exit(void);

参数:

返回值:

USB Gadget 启用功能

函数原型

int usb_gadget_function_enable(const char *name);

参数:

  • name:功能名

返回值:

  • 0:成功
  • 负数:失败

USB Gadget 禁用功能

函数原型

int usb_gadget_function_disable(const char *name);

参数:

  • name:功能名

返回值:

  • 0:成功
  • 负数:失败

USB Gadget 读取

函数原型

int usb_gadget_function_read(int ep_idx, char *buf, int size);

参数:

  • ep_idx:端点号
  • buf:buf指针
  • size:buf的大小

返回值:

  • 0:成功
  • 负数:失败

USB Gadget 限时读取

函数原型

int usb_gadget_function_read_timeout(int ep_idx, char *buf, int size, int ms);

参数:

  • ep_idx:端点号
  • buf:buf指针
  • size:buf的大小
  • ms:超时时间

返回值:

  • 0:成功
  • 负数:失败

USB Gadget 写数据

函数原型

int usb_gadget_function_write(int ep_idx, char *buf, int size);

参数:

  • ep_idx:端点号
  • buf:buf指针
  • size:buf的大小

返回值:

  • 0:成功
  • 负数:失败

USB Gadget 写字符串

函数原型

int usb_gadget_function_string_set(char *name, char *str, unsigned int idx);

参数:

  • name:名称
  • str:字符串指针
  • idx:端点号

返回值:

  • 0:成功
  • 负数:失败

模块使用范例

测试用例公共头文件

usb_test.h

#ifndef USB_TEST_H
#define USB_TEST_H

#include <sunxi_hal_usb.h>
#include <usb_os_platform.h>

#ifdef __cplusplus
extern "C" {
#endif

int usb_test_cmd_hci(int argc, const char **argv);
int usb_test_cmd_udc(int argc, const char **argv);
int usb_test_cmd_phy_range(int argc, const char **argv);
int usb_test_cmd_ed_test(int argc, const char **argv);
int usb_test_cmd_debug(int argc, const char **argv);
int usb_test_cmd_uvc(int argc, const char **argv);


int usb_test_is_otg(int port);
int usb_test_get_port(const char *buf, int *port);
void usb_test_show_help(void);

unsigned char usb_core_is_enabled(void);
unsigned char sunxi_ehci_status_get(void);

#ifdef __cplusplus
}
#endif

#endif // USB_TEST_H

HCI 测试实现实现

#include "usb_test.h"

int usb_test_cmd_hci(int argc, const char **argv)
{
    int c;
    int hci_num = 0;
    unsigned int port = 0;

    if (argc == 4) {
        // insmod/rmmod indicate host driver
        if (usb_test_get_port(argv[3], &port))
            return 0;
    } else if (argc == 3) {
        // insmod/rmmod all host driver
        port = 0xffff;
    } else
        return -1;

    while ((c = getopt(argc, (char *const *)argv, "ir")) != -1) {
        switch (c) {
        case 'i':
#ifdef CONFIG_HAL_TEST_UDC
            /*otg mode rmmod device driver before insmod hci*/
            if (usb_test_is_otg(port)) {
                printf("[usb0] rmmod device driver!\n");
                hal_gadget_exit();
            }
#endif
            if (!usb_core_is_enabled())
                hal_usb_core_init();

            if (port == 0xffff)
                for (hci_num = 0; hci_num < USB_MAX_CONTROLLER_COUNT; hci_num++)
                    hal_usb_hcd_init(hci_num);
            else
                hal_usb_hcd_init(port);
            break;
        case 'r':
            if (port == 0xffff)
                for (hci_num = 0; hci_num < USB_MAX_CONTROLLER_COUNT; hci_num++)
                    hal_usb_hcd_deinit(hci_num);
            else
                hal_usb_hcd_deinit(port);

            if (usb_core_is_enabled() && !sunxi_ehci_status_get())
                hal_usb_core_exit();
            break;
        default:
            printf("ERR: insmod/rmmod error!\n");
            usb_test_show_help();
            break;
        }
    }

    return 0;
}

int usb_test_cmd_debug(int argc, const char **argv)
{
    int enable = 0;

    if (argc != 3)
        return -1;

    enable = atoi(argv[2]);
    if (enable == 1 || enable == 0) {
        hal_usb_hcd_debug_set(enable);
        printf("USB debug %s!\n", hal_usb_hcd_debug_get() ? "open" : "close");
        return 0;
    }

    return -1;
}

MSG 测试用例实现

#include <inttypes.h>

#include "usb.h"
#include "ch9.h"
#include "storage.h"
#include "usb_os_platform.h"


struct usb_msg_dev {
    uint8_t max_lun;
    uint8_t cbw[32];
};

static struct usb_device_descriptor demo_device_desc = {
    .bLength = USB_DT_DEVICE_SIZE,
    .bDescriptorType = USB_DT_DEVICE,
    .bcdUSB = 0x0200,
    .bDeviceClass = 0,
    .bDeviceSubClass = 0,
    .bDeviceProtocol = 0,
    .bMaxPacketSize0 = 64,
    .idVendor = 0x18d1,
    .idProduct = 0x0001,
    .bcdDevice = 0x0001,
    .iManufacturer = 0x01,
    .iProduct = 0x02,
    .iSerialNumber = 0x03,
    .bNumConfigurations = 1
};

static struct usb_config_descriptor demo_config_desc = {
    .bLength = USB_DT_CONFIG_SIZE,
    .bDescriptorType = USB_DT_CONFIG,
    .wTotalLength = 32, /* FIXME */
    .bNumInterfaces = 1,
    .bConfigurationValue = 1,
    .iConfiguration = 0,
    .bmAttributes = 0x80,
    .bMaxPower = 0x64 /* 200mA */
};

static struct usb_interface_descriptor demo_intf_desc = {
    .bLength = USB_DT_INTERFACE_SIZE,
    .bDescriptorType = USB_DT_INTERFACE,
    .bInterfaceNumber = 0x0,
    .bAlternateSetting = 0x0,
    .bNumEndpoints = 2,
    .bInterfaceClass = 0x08, /* Mass Storage class */
    .bInterfaceSubClass = 0x06, /* SCSI Transparent Subclass */
    .bInterfaceProtocol = 0x50, /* Bulk-Only Protocol */
    .iInterface = 0
};

static struct usb_endpoint_descriptor demo_ep_bulk_out = {
    .bLength = USB_DT_ENDPOINT_SIZE,
    .bDescriptorType = USB_DT_ENDPOINT,
    .bEndpointAddress = 0x1 | USB_DIR_OUT,
    .bmAttributes = USB_ENDPOINT_XFER_BULK,
    .wMaxPacketSize = 0x0200, /* 512 Bytes */
    .bInterval = 0
};

static struct usb_endpoint_descriptor demo_ep_bulk_in = {
    .bLength = USB_DT_ENDPOINT_SIZE,
    .bDescriptorType = USB_DT_ENDPOINT,
    .bEndpointAddress = 0x1 | USB_DIR_IN,
    .bmAttributes = USB_ENDPOINT_XFER_BULK,
    .wMaxPacketSize = 0x0200, /* 512 Bytes */
    .bInterval = 0
};

/*
 * String descriptors
 */
static const uint16_t g_str_lang_id[] = {
    0x0304, 0x0409
};

static const uint16_t g_str_manufacturer[] = {
    0x030e, 'G', 'o', 'o', 'g', 'l', 'e'
};

static const uint16_t g_str_product[] = {
    0x0308, 'M', 's', 'g'
};

static const uint16_t g_str_serialnumber[] = {
    0x0314, '2', '0', '0', '8', '0', '4', '1', '1'
};

struct usb_msg_dev g_msg_dev = {
    .max_lun = 0,
};

static void *g_config_desc = NULL;

void usb_msg_desc_init(void)
{
    uint32_t config_desc_len;
    void *buf;

    config_desc_len = demo_config_desc.bLength + demo_intf_desc.bLength
            + demo_ep_bulk_out.bLength + demo_ep_bulk_in.bLength;

    g_config_desc = malloc(config_desc_len);

    /* compose configuation, interface and endpoint descriptors */
    buf = g_config_desc;
    memcpy(buf, &demo_config_desc, demo_config_desc.bLength);
    buf += demo_config_desc.bLength;
    memcpy(buf, &demo_intf_desc, demo_intf_desc.bLength);
    buf += demo_intf_desc.bLength;
    memcpy(buf, &demo_ep_bulk_out, demo_ep_bulk_out.bLength);
    buf += demo_ep_bulk_out.bLength;
    memcpy(buf, &demo_ep_bulk_in, demo_ep_bulk_in.bLength);

    hal_udc_device_desc_init(&demo_device_desc);
    hal_udc_config_desc_init(g_config_desc, config_desc_len);
    /* FIXME: string descriptors must be initialized in the following order now */
    hal_udc_string_desc_init(g_str_lang_id);
    hal_udc_string_desc_init(g_str_manufacturer);
    hal_udc_string_desc_init(g_str_product);
    hal_udc_string_desc_init(g_str_serialnumber);
}

static void usb_msg_ep_init(void)
{
    hal_log_info("usb demo ep init...\n");

    /* init bulk-in ep */
    hal_udc_ep_enable(demo_ep_bulk_in.bEndpointAddress,
            demo_ep_bulk_in.wMaxPacketSize,
            demo_ep_bulk_in.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);

    /* initialise bulk-out ep buf in order to get the first CBW */
    hal_udc_ep_set_buf(demo_ep_bulk_out.bEndpointAddress,
            g_msg_dev.cbw,
            sizeof(g_msg_dev.cbw));

    /* init bulk-out ep */
    hal_udc_ep_enable(demo_ep_bulk_out.bEndpointAddress,
            demo_ep_bulk_out.wMaxPacketSize,
            demo_ep_bulk_out.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
}

static udc_errno_t usb_msg_class_request_handler(struct usb_ctrlrequest *crq)
{
    udc_errno_t ret = UDC_ERRNO_SUCCESS;

    switch(crq->bRequest) {
    case US_BULK_RESET_REQUEST:
        /* TODO */
        break;
    case US_BULK_GET_MAX_LUN:
        hal_log_info("get MAX_LUN\r\n");

        if (crq->bRequestType !=
                (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) {
            ret = UDC_ERRNO_CMD_INVALID;
            break;
        }
        /* FIXME: a fake response for demo */
        hal_udc_ep_write(0, &g_msg_dev.max_lun, sizeof(g_msg_dev.max_lun));
        break;
    default:
        ret = UDC_ERRNO_CMD_INVALID;
        break;
    }

    return ret;
}

static udc_errno_t usb_msg_standard_request_handler(struct usb_ctrlrequest *crq)
{
    udc_errno_t ret = UDC_ERRNO_SUCCESS;

    switch (crq->bRequest) {
    case USB_REQ_SET_CONFIGURATION:
        /* FIXME: usb msg driver should be independent of demo code */
        usb_msg_ep_init();
        break;
    default:
        ret = UDC_ERRNO_CMD_INVALID;
        break;
    }

    return ret;
}

static udc_errno_t usb_msg_scsi_cmd_handler(struct bulk_cb_wrap *cbw)
{
    udc_errno_t ret = UDC_ERRNO_SUCCESS;
    uint8_t opcode = cbw->CDB[0];
    uint8_t fake_rsp[36] = {0x00, 0x80, 0x02, 0x02, 0x1F, 0x00, 0x00,
            0x00, 0x54, 0x69, 0x6e, 0x61, 0x20, 0x20, 0x20,
            0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
            0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
            0x20, 0x20, 0x20, 0x20, 0x20};

    hal_log_info("scsi cmd opcode: 0x%x\n", opcode);

    switch (opcode) {
    case 0x12: /* INQUIRY */
        /* FIXME: a fake response for demo */
        hal_udc_ep_write(demo_ep_bulk_in.bEndpointAddress, fake_rsp, sizeof(fake_rsp));
        break;
    default:
        ret = UDC_ERRNO_CMD_INVALID;
        break;
    }

    return ret;
}

udc_errno_t usb_msg_callback(uint8_t ep_addr, udc_callback_event_t event, void *data, uint32_t len)
{
    uint8_t ep_idx;
    uint8_t is_in;
    udc_errno_t ret = UDC_ERRNO_SUCCESS;
    struct usb_ctrlrequest *crq;
    struct bulk_cb_wrap *cbw;

    hal_log_info("usb_msg_callback event: %"PRIu32", len: %"PRIu32"\n", event, len);

    ep_idx = ep_addr & 0x7f;
    is_in = ep_addr & USB_DIR_IN;

    if (ep_idx == 0) { /* handle ep0 */
        crq = (struct usb_ctrlrequest *)data;

        switch (event) {
        case UDC_EVENT_RX_STANDARD_REQUEST:
            ret = usb_msg_standard_request_handler(crq);
            break;
        case UDC_EVENT_RX_CLASS_REQUEST:
            ret = usb_msg_class_request_handler(crq);
            break;
        default:
            ret = UDC_ERRNO_CMD_NOT_SUPPORTED;
            break;
        }
    } else { /* handle ep1~4 */
        if (is_in) {
            /* TODO: maybe useless? */
        } else {
            cbw = (struct bulk_cb_wrap *)data;

            switch (event) {
            case UDC_EVENT_RX_DATA:
                usb_msg_scsi_cmd_handler(cbw);
                break;
            default:
                ret = UDC_ERRNO_CMD_NOT_SUPPORTED;
                break;
            }
        }
    }

    return ret;
}

PHY 驱动测试实现

#include "usb_test.h"

static void __usb_ed_test(int port, const char *buf)
{
    if (usb_test_is_otg(port)) { /*otg mode*/
#ifdef CONFIG_HAL_TEST_UDC
        hal_udc_ed_test(buf, strlen(buf));
#else
        printf("ERR: udc config not find!\n");
#endif

    } else {
#ifdef CONFIG_HAL_TEST_HCI
        hal_hci_ed_test(port, buf, strlen(buf));
#else
        printf("ERR: hci config not find!\n");
#endif
    }
}

static void __phy_range_set(int port, int val)
{
    if (usb_test_is_otg(port)) { /*otg mode*/
#ifdef CONFIG_HAL_TEST_UDC
        hal_udc_phy_range_set(port, val);
#else
        printf("ERR: udc config not find!\n");
#endif
    } else {
#ifdef CONFIG_HAL_TEST_HCI
        hal_hci_phy_range_set(port, val);
#else
        printf("ERR: hci config not find!\n");
#endif
    }
}

static void __phy_range_show(int port)
{
    if (usb_test_is_otg(port)) { /*otg mode*/
#ifdef CONFIG_HAL_TEST_UDC
        hal_udc_phy_range_show(port);
#else
        printf("ERR: udc config not find!\n");
#endif
    } else {
#ifdef CONFIG_HAL_TEST_HCI
        hal_hci_phy_range_show(port);
#else
        printf("ERR: hci config not find!\n");
#endif
    }
}


int usb_test_cmd_ed_test(int argc, const char **argv)
{
    int port = 0;

    if (argc != 4)
        return -1;

    if (usb_test_get_port(argv[2], &port))
        return 0;

    __usb_ed_test(port, argv[3]);
    return 0;
}

int usb_test_cmd_phy_range(int argc, const char **argv)
{
    int c;
    int val;
    int port;

    if ((argc != 4) && (argc != 5))
        return -1;

    if (usb_test_get_port(argv[3], &port))
        return 0;

    if (usb_test_is_otg(port)) {
        printf("\nOTG%d phy range\n", port);
    } else
        printf("\nEHCI%d phy range\n", port);

    while ((c = getopt(argc, (char *const *)argv, "sg")) != -1) {
        switch (c) {
        case 's':
            if(argc == 5)
                val = strtol(argv[4], NULL, 16);
            else
                return -1;

            __phy_range_set(port, val);
            break;
        case 'g':
            __phy_range_show(port);
            break;
        default:
            printf("ERR: phy_range cmd error!\n");
            usb_test_show_help();
            break;
        }
    }
    return 0;
}

USB UDC 测试用例实现

#include "usb_test.h"

int usb_test_cmd_udc(int argc, const char **argv)
{
    int c;
    if ((argc != 3) && (argc != 4))
        return -1;

    while ((c = getopt(argc, (char *const *)argv, "ir")) != -1) {
        switch (c) {
        case 'i':
#ifdef CONFIG_HAL_TEST_HCI
            // rmmod host driver before insmod otg
            if (usb_test_is_otg(0) == 0) /*hci mode*/
                hal_usb_hcd_deinit(0);
#endif
            printf("[usb0] insmod device driver!\n");
            hal_gadget_init();
            break;
        case 'r':
            printf("[usb0] rmmod device driver!\n");
            hal_gadget_exit();
            break;
        default:
            printf("err: insmod/rmmod error!\n");
            usb_test_show_help();
            break;
        }
    }

    return 0;
}

### USB UVC 测试用例实现

#include <sys/ioctl.h>
#include <fcntl.h>

#include "usb_test.h"
#include "uvcvideo.h"

static int save_frame_to_file(void *str, void *start, int length)
{
    FILE *fp = NULL;

    fp = fopen(str, "wb+"); //save more frames
    if (!fp) {
        printf(" Open %s error\n", (char *)str);

        return -1;
    }

    if (fwrite(start, length, 1, fp)) {
        fclose(fp);

        return 0;
    } else {
        printf(" Write file fail (%s)\n", strerror(errno));
        fclose(fp);

        return -1;
    }

    return 0;
}

int usb_test_cmd_uvc(int argc, const char **argv)
{
    int fd;
    struct v4l2_capability cap;      /* Query device capabilities */
    struct v4l2_streamparm parms;    /* set streaming parameters */
    struct v4l2_format fmt;          /* try a format */
    struct v4l2_requestbuffers req;  /* Initiate Memory Mapping or User Pointer I/O */
    struct v4l2_buffer buf;          /* Query the status of a buffer */
    enum v4l2_buf_type type;
    int n_buffers;
    char source_data_path[64];
    int np;

    /* 1.open /dev/videoX node */
    fd = open("/dev/video", O_RDWR);

    /* 2.Query device capabilities */
    memset(&cap, 0, sizeof(cap));
    if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
        printf(" Query device capabilities fail!!!\n");
    } else {
        printf(" Querey device capabilities succeed\n");
        printf(" cap.driver=%s\n", cap.driver);
        printf(" cap.card=%s\n", cap.card);
        printf(" cap.bus_info=%s\n", cap.bus_info);
        printf(" cap.version=0x%08x\n", cap.version);
        printf(" cap.capabilities=0x%08x\n", cap.capabilities);
    }

    /* 7.set streaming parameters */
    memset(&parms, 0, sizeof(struct v4l2_streamparm));
    parms.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    parms.parm.capture.timeperframe.numerator = 1;
    parms.parm.capture.timeperframe.denominator = 30;
    if (ioctl(fd, VIDIOC_S_PARM, &parms) < 0) {
        printf(" Setting streaming parameters failed, numerator:%d denominator:%d\n",
               parms.parm.capture.timeperframe.numerator,
               parms.parm.capture.timeperframe.denominator);
        close(fd);
        return -1;
    }

    /* 9.set the data format */
    memset(&fmt, 0, sizeof(struct v4l2_format));
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width = 1280;
    fmt.fmt.pix.height = 720;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
    fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;

    if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) {
        printf(" setting the data format failed!\n");
        close(fd);
        return -1;
    }

    /* 10.Initiate Memory Mapping or User Pointer I/O */
    memset(&req, 0, sizeof(struct v4l2_requestbuffers));
    req.count = 3;
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;
    if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0) {
        printf(" VIDIOC_REQBUFS failed\n");
        close(fd);
        return -1;
    }

    /* 11.Exchange a buffer with the driver */
    for (n_buffers = 0; n_buffers < req.count; n_buffers++) {
        memset(&buf, 0, sizeof(struct v4l2_buffer));

        buf.index = n_buffers;
        if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
            printf(" VIDIOC_QBUF error\n");

            close(fd);
            return -1;
        }
    }

    /* streamon */
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) {
        printf(" VIDIOC_STREAMON error! %s\n", strerror(errno));
    } else
        printf(" stream on succeed\n");

    np = 0;
    while (np < 5) {
        printf(" camera capture num is [%d]\n", np);

        /* wait uvc frame */
        memset(&buf, 0, sizeof(struct v4l2_buffer));

        if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1) {
            printf(" VIDIOC_DQBUF error\n");

            goto EXIT;
        } else
            printf("*****DQBUF[%d] FINISH*****\n", buf.index);

        sprintf(source_data_path, "/data/source_frame_%d.jpg", np);
        save_frame_to_file(source_data_path, (void *)buf.mem_buf, buf.length);

        if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
            printf(" VIDIOC_QBUF error\n");

            goto EXIT;
        } else
            printf("************QBUF[%d] FINISH**************\n\n", buf.index);

        np++;
    }

    printf("\n\n Capture thread finish\n");

EXIT:
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ioctl(fd, VIDIOC_STREAMOFF, &type);

    memset(&req, 0, sizeof(struct v4l2_requestbuffers));
    req.count = 0;
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;
    ioctl(fd, VIDIOC_REQBUFS, &req);

    close(fd);

    return 0;
}

USB 测试用例

#include "usb_test.h"

void usb_test_show_help(void)
{
    printf("\nUsage:\n"\
        "\tusb hci {-i|-r} [<port>]\n"\
        "\tusb udc {-i|-r} [<port>]\n"\
        "\tusb phy_range {-s|-g} {<port>} [<phyrange>]\n"\
        "\tusb ed_test {<port>} {<type>}\n"\
        "\tusb debug {<status>}\n"\
        "\tusb uvc_test\n"\
        "\n\t- - - - - - - - - - - - - - - - - - - - -\n"\
        "Meaning:\n"\
        "\t-i:insmod, -r:rmmod, -s:set, -g:get\n"\
        "\n"\
        "\tport     : [0-%d],port number\n"\
        "\tphyrange : [0x0-0x1f],phy range\n"\
        "\tstatus   : [0-disable,1-enable],hci debug status\n"\
        "\ttype     : [test_j_state/test_k_state/test_se0_nak/test_pack]--hci & otg\n"\
        "\t           [test_not_operating/test_force_enable/test_mask]--hci only\n"\
        "\n\t==>> More information refer to spec <<==\n",
        USB_MAX_CONTROLLER_COUNT - 1);
}

int usb_test_is_otg(int port)
{
#if defined(CONFIG_HAL_TEST_HCI)
    if (port == 0 && !(sunxi_ehci_status_get() & 0x1)) /*otg mode*/
#else
    if (port == 0)
#endif
        return 1;
    else
        return 0;
}

int usb_test_get_port(const char *buf, int *port)
{
    *port = atoi(buf);
    if (*port > USB_MAX_CONTROLLER_COUNT - 1) {
        printf("ERR: port(%d) choose error! Port range [0-%d]\n", *port,
            USB_MAX_CONTROLLER_COUNT - 1);
        return -1;
    }

    return 0;
}

static int usb_test_command_hci(int argc, const char **argv)
{
#if defined(CONFIG_HAL_TEST_HCI)
    return usb_test_cmd_hci(argc, argv);
#else
    printf("ERR: Can't find command config!\n");
    return -1;
#endif
}

static int usb_test_command_udc(int argc, const char **argv)
{
#if defined(CONFIG_HAL_TEST_UDC)
    return usb_test_cmd_udc(argc, argv);
#else
    printf("ERR: Can't find command config!\n");
    return -1;
#endif
}

static int usb_test_command_phy_range(int argc, const char **argv)
{
    return usb_test_cmd_phy_range(argc, argv);
}

static int usb_test_command_ed_test(int argc, const char **argv)
{
    return usb_test_cmd_ed_test(argc, argv);
}

static int usb_test_command_debug(int argc, const char **argv)
{
#if defined(CONFIG_HAL_TEST_HCI)
    return usb_test_cmd_debug(argc, argv);
#else
    printf("ERR: Can't find command config!\n");
    return -1;
#endif
}

static int usb_test_command_uvc(int argc, const char **argv)
{
#if defined(CONFIG_HAL_TEST_UVC)
    // return usb_test_cmd_uvc(argc, argv);
    usb_test_cmd_uvc(argc, argv);/* -1 has other meaning in this case*/
    return 0;
#else
    printf("ERR: Can't find command config!\n");
    return -1;
#endif
}

static int usb_test_command(int argc, const char **argv)
{
    int ret = -1;
    if (argc < 2) {
        printf("ERR: command error\n");
        usb_test_show_help();
        return -1;
    }

    if (!strcmp(argv[1], "hci"))
        ret = usb_test_command_hci(argc, argv);
    else if (!strcmp(argv[1], "udc"))
        ret = usb_test_command_udc(argc, argv);
    else if (!strcmp(argv[1], "phy_range"))
        ret = usb_test_command_phy_range(argc, argv);
    else if (!strcmp(argv[1], "ed_test"))
        ret = usb_test_command_ed_test(argc, argv);
    else if (!strcmp(argv[1], "debug"))
        ret = usb_test_command_debug(argc, argv);
    else if (!strcmp(argv[1], "uvc_test"))
        ret = usb_test_command_uvc(argc, argv);


    if (ret == 0)
        return 0;

    usb_test_show_help();
    return -1;
}

FINSH_FUNCTION_EXPORT_CMD(usb_test_command, usb, usb tests)