This commit is contained in:
2024-08-05 20:57:09 +08:00
commit 46d9ee7795
3020 changed files with 1725767 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,344 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbd_core.h"
#include "usbd_audio.h"
struct audio_entity_param {
uint32_t wCur;
uint32_t wMin;
uint32_t wMax;
uint32_t wRes;
};
struct usbd_audio_priv {
struct audio_entity_info *table;
uint8_t num;
uint16_t uac_version;
} g_usbd_audio[CONFIG_USBDEV_MAX_BUS];
static int audio_class_endpoint_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
{
uint8_t control_selector;
uint32_t sampling_freq = 0;
uint8_t ep;
control_selector = HI_BYTE(setup->wValue);
ep = LO_BYTE(setup->wIndex);
switch (control_selector) {
case AUDIO_EP_CONTROL_SAMPLING_FEQ:
switch (setup->bRequest) {
case AUDIO_REQUEST_SET_CUR:
memcpy((uint8_t *)&sampling_freq, *data, *len);
USB_LOG_DBG("Set ep:0x%02x %d Hz\r\n", ep, (int)sampling_freq);
usbd_audio_set_sampling_freq(busid, ep, sampling_freq);
break;
case AUDIO_REQUEST_GET_CUR:
case AUDIO_REQUEST_GET_MIN:
case AUDIO_REQUEST_GET_MAX:
case AUDIO_REQUEST_GET_RES:
sampling_freq = usbd_audio_get_sampling_freq(busid, ep);
memcpy(*data, &sampling_freq, 3);
USB_LOG_DBG("Get ep:0x%02x %d Hz\r\n", ep, (int)sampling_freq);
*len = 3;
break;
}
break;
default:
USB_LOG_WRN("Unhandled Audio Class control selector 0x%02x\r\n", control_selector);
return -1;
}
return 0;
}
static int audio_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
{
USB_LOG_DBG("Audio Class request: "
"bRequest 0x%02x\r\n",
setup->bRequest);
uint8_t entity_id;
uint8_t ep;
uint8_t subtype = 0x01;
uint8_t control_selector;
uint8_t ch;
uint8_t mute;
uint16_t volume;
int volume_db = 0;
uint32_t sampling_freq = 0;
const char *mute_string[2] = { "off", "on" };
entity_id = HI_BYTE(setup->wIndex);
control_selector = HI_BYTE(setup->wValue);
ch = LO_BYTE(setup->wValue);
ARG_UNUSED(mute_string);
for (uint8_t i = 0; i < g_usbd_audio[busid].num; i++) {
if (g_usbd_audio[busid].table[i].bEntityId == entity_id) {
subtype = g_usbd_audio[busid].table[i].bDescriptorSubtype;
ep = g_usbd_audio[busid].table[i].ep;
break;
}
}
if (subtype == 0x01) {
USB_LOG_ERR("Do not find subtype for 0x%02x\r\n", entity_id);
return -1;
}
USB_LOG_DBG("Audio entity_id:%02x, subtype:%02x, cs:%02x\r\n", entity_id, subtype, control_selector);
switch (subtype) {
case AUDIO_CONTROL_FEATURE_UNIT:
switch (control_selector) {
case AUDIO_FU_CONTROL_MUTE:
if (g_usbd_audio[busid].uac_version < 0x0200) {
switch (setup->bRequest) {
case AUDIO_REQUEST_SET_CUR:
mute = (*data)[0];
usbd_audio_set_mute(busid, ep, ch, mute);
break;
case AUDIO_REQUEST_GET_CUR:
(*data)[0] = usbd_audio_get_mute(busid, ep, ch);
*len = 1;
break;
default:
USB_LOG_WRN("Unhandled Audio Class bRequest 0x%02x in cs 0x%02x\r\n", setup->bRequest, control_selector);
return -1;
}
} else {
switch (setup->bRequest) {
case AUDIO_REQUEST_CUR:
if (setup->bmRequestType & USB_REQUEST_DIR_MASK) {
(*data)[0] = usbd_audio_get_mute(busid, ep, ch);
*len = 1;
} else {
mute = (*data)[0];
usbd_audio_set_mute(busid, ep, ch, mute);
}
break;
default:
//USB_LOG_WRN("Unhandled Audio Class bRequest 0x%02x in cs 0x%02x\r\n", setup->bRequest, control_selector);
return -1;
}
}
break;
case AUDIO_FU_CONTROL_VOLUME:
if (g_usbd_audio[busid].uac_version < 0x0200) {
switch (setup->bRequest) {
case AUDIO_REQUEST_SET_CUR:
memcpy(&volume, *data, *len);
if (volume < 0x8000) {
volume_db = volume / 256;
} else if (volume > 0x8000) {
volume_db = (0xffff - volume + 1) / -256;
}
volume_db += 128; /* 0 ~ 255 */
USB_LOG_DBG("Set ep:0x%02x ch:%d volume:0x%04x\r\n", ep, ch, volume);
usbd_audio_set_volume(busid, ep, ch, volume_db);
break;
case AUDIO_REQUEST_GET_CUR:
volume_db = usbd_audio_get_volume(busid, ep, ch);
volume_db -= 128;
if (volume_db >= 0) {
volume = volume_db * 256;
} else {
volume = volume_db * 256 + 0xffff + 1;
}
memcpy(*data, &volume, 2);
*len = 2;
break;
case AUDIO_REQUEST_GET_MIN:
(*data)[0] = 0x00; /* -2560/256 dB */
(*data)[1] = 0xdb;
*len = 2;
break;
case AUDIO_REQUEST_GET_MAX:
(*data)[0] = 0x00; /* 0 dB */
(*data)[1] = 0x00;
*len = 2;
break;
case AUDIO_REQUEST_GET_RES:
(*data)[0] = 0x00; /* -256/256 dB */
(*data)[1] = 0x01;
*len = 2;
break;
default:
USB_LOG_WRN("Unhandled Audio Class bRequest 0x%02x in cs 0x%02x\r\n", setup->bRequest, control_selector);
return -1;
}
} else {
switch (setup->bRequest) {
case AUDIO_REQUEST_CUR:
if (setup->bmRequestType & USB_REQUEST_DIR_MASK) {
volume_db = usbd_audio_get_volume(busid, ep, ch);
volume = volume_db;
memcpy(*data, &volume, 2);
*len = 2;
} else {
memcpy(&volume, *data, *len);
volume_db = volume;
USB_LOG_DBG("Set ep:0x%02x ch:%d volume:0x%02x\r\n", ep, ch, volume);
usbd_audio_set_volume(busid, ep, ch, volume_db);
}
break;
case AUDIO_REQUEST_RANGE:
if (setup->bmRequestType & USB_REQUEST_DIR_MASK) {
*((uint16_t *)(*data + 0)) = 1;
*((uint16_t *)(*data + 2)) = 0;
*((uint16_t *)(*data + 4)) = 100;
*((uint16_t *)(*data + 6)) = 1;
*len = 8;
} else {
}
break;
default:
//USB_LOG_WRN("Unhandled Audio Class bRequest 0x%02x in cs 0x%02x\r\n", setup->bRequest, control_selector);
return -1;
}
}
break;
default:
USB_LOG_WRN("Unhandled Audio Class cs 0x%02x \r\n", control_selector);
return -1;
}
break;
case AUDIO_CONTROL_CLOCK_SOURCE:
switch (control_selector) {
case AUDIO_CS_CONTROL_SAM_FREQ:
switch (setup->bRequest) {
case AUDIO_REQUEST_CUR:
if (setup->bmRequestType & USB_REQUEST_DIR_MASK) {
sampling_freq = usbd_audio_get_sampling_freq(busid, ep);
memcpy(*data, &sampling_freq, 4);
USB_LOG_DBG("Get ep:0x%02x %d Hz\r\n", ep, (int)sampling_freq);
*len = 4;
} else {
memcpy(&sampling_freq, *data, setup->wLength);
USB_LOG_DBG("Set ep:0x%02x %d Hz\r\n", ep, (int)sampling_freq);
usbd_audio_set_sampling_freq(busid, ep, sampling_freq);
}
break;
case AUDIO_REQUEST_RANGE:
if (setup->bmRequestType & USB_REQUEST_DIR_MASK) {
uint8_t *sampling_freq_table = NULL;
uint16_t num;
usbd_audio_get_sampling_freq_table(busid, ep, &sampling_freq_table);
num = (uint16_t)((uint16_t)(sampling_freq_table[1] << 8) | ((uint16_t)sampling_freq_table[0]));
memcpy(*data, sampling_freq_table, (12 * num + 2));
*len = (12 * num + 2);
} else {
}
break;
default:
//USB_LOG_WRN("Unhandled Audio Class bRequest 0x%02x in cs 0x%02x\r\n", setup->bRequest, control_selector);
return -1;
}
break;
case AUDIO_CS_CONTROL_CLOCK_VALID:
if (setup->bmRequestType & USB_REQUEST_DIR_MASK) {
(*data)[0] = 1;
*len = 1;
} else {
return -1;
}
break;
default:
//USB_LOG_WRN("Unhandled Audio Class cs 0x%02x \r\n", control_selector);
return -1;
}
break;
default:
break;
}
return 0;
}
static void audio_notify_handler(uint8_t busid, uint8_t event, void *arg)
{
switch (event) {
case USBD_EVENT_RESET:
break;
case USBD_EVENT_SET_INTERFACE: {
struct usb_interface_descriptor *intf = (struct usb_interface_descriptor *)arg;
if (intf->bAlternateSetting) {
usbd_audio_open(busid, intf->bInterfaceNumber);
} else {
usbd_audio_close(busid, intf->bInterfaceNumber);
}
}
break;
default:
break;
}
}
struct usbd_interface *usbd_audio_init_intf(uint8_t busid,
struct usbd_interface *intf,
uint16_t uac_version,
struct audio_entity_info *table,
uint8_t num)
{
if (uac_version < 0x0200) {
intf->class_interface_handler = audio_class_interface_request_handler;
intf->class_endpoint_handler = audio_class_endpoint_request_handler;
intf->vendor_handler = NULL;
intf->notify_handler = audio_notify_handler;
} else {
intf->class_interface_handler = audio_class_interface_request_handler;
intf->class_endpoint_handler = NULL;
intf->vendor_handler = NULL;
intf->notify_handler = audio_notify_handler;
}
g_usbd_audio[busid].uac_version = uac_version;
g_usbd_audio[busid].table = table;
g_usbd_audio[busid].num = num;
return intf;
}
__WEAK void usbd_audio_set_volume(uint8_t busid, uint8_t ep, uint8_t ch, int volume)
{
}
__WEAK int usbd_audio_get_volume(uint8_t busid, uint8_t ep, uint8_t ch)
{
return 0;
}
__WEAK void usbd_audio_set_mute(uint8_t busid, uint8_t ep, uint8_t ch, bool mute)
{
}
__WEAK bool usbd_audio_get_mute(uint8_t busid, uint8_t ep, uint8_t ch)
{
return 0;
}
__WEAK void usbd_audio_set_sampling_freq(uint8_t busid, uint8_t ep, uint32_t sampling_freq)
{
}
__WEAK uint32_t usbd_audio_get_sampling_freq(uint8_t busid, uint8_t ep)
{
return 0;
}
__WEAK void usbd_audio_get_sampling_freq_table(uint8_t busid, uint8_t ep, uint8_t **sampling_freq_table)
{
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBD_AUDIO_H
#define USBD_AUDIO_H
#include "usb_audio.h"
#ifdef __cplusplus
extern "C" {
#endif
struct audio_entity_info {
uint8_t bDescriptorSubtype;
uint8_t bEntityId;
uint8_t ep;
};
/* Init audio interface driver */
struct usbd_interface *usbd_audio_init_intf(uint8_t busid, struct usbd_interface *intf,
uint16_t uac_version,
struct audio_entity_info *table,
uint8_t num);
void usbd_audio_open(uint8_t busid, uint8_t intf);
void usbd_audio_close(uint8_t busid, uint8_t intf);
void usbd_audio_set_volume(uint8_t busid, uint8_t ep, uint8_t ch, int volume);
int usbd_audio_get_volume(uint8_t busid, uint8_t ep, uint8_t ch);
void usbd_audio_set_mute(uint8_t busid, uint8_t ep, uint8_t ch, bool mute);
bool usbd_audio_get_mute(uint8_t busid, uint8_t ep, uint8_t ch);
void usbd_audio_set_sampling_freq(uint8_t busid, uint8_t ep, uint32_t sampling_freq);
uint32_t usbd_audio_get_sampling_freq(uint8_t busid, uint8_t ep);
void usbd_audio_get_sampling_freq_table(uint8_t busid, uint8_t ep, uint8_t **sampling_freq_table);
#ifdef __cplusplus
}
#endif
#endif /* USBD_AUDIO_H */

View File

@@ -0,0 +1,503 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbh_core.h"
#include "usbh_audio.h"
#undef USB_DBG_TAG
#define USB_DBG_TAG "usbh_audio"
#include "usb_log.h"
#define DEV_FORMAT "/dev/audio%d"
/* general descriptor field offsets */
#define DESC_bLength 0 /** Length offset */
#define DESC_bDescriptorType 1 /** Descriptor type offset */
#define DESC_bDescriptorSubType 2 /** Descriptor subtype offset */
/* interface descriptor field offsets */
#define INTF_DESC_bInterfaceNumber 2 /** Interface number offset */
#define INTF_DESC_bAlternateSetting 3 /** Alternate setting offset */
#ifndef CONFIG_USBHOST_MAX_AUDIO_CLASS
#define CONFIG_USBHOST_MAX_AUDIO_CLASS 4
#endif
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_audio_buf[128];
static struct usbh_audio g_audio_class[CONFIG_USBHOST_MAX_AUDIO_CLASS];
static uint32_t g_devinuse = 0;
static struct usbh_audio *usbh_audio_class_alloc(void)
{
int devno;
for (devno = 0; devno < CONFIG_USBHOST_MAX_AUDIO_CLASS; devno++) {
if ((g_devinuse & (1 << devno)) == 0) {
g_devinuse |= (1 << devno);
memset(&g_audio_class[devno], 0, sizeof(struct usbh_audio));
g_audio_class[devno].minor = devno;
return &g_audio_class[devno];
}
}
return NULL;
}
static void usbh_audio_class_free(struct usbh_audio *audio_class)
{
int devno = audio_class->minor;
if (devno >= 0 && devno < 32) {
g_devinuse &= ~(1 << devno);
}
memset(audio_class, 0, sizeof(struct usbh_audio));
}
int usbh_audio_open(struct usbh_audio *audio_class, const char *name, uint32_t samp_freq)
{
struct usb_setup_packet *setup;
struct usb_endpoint_descriptor *ep_desc;
uint8_t mult;
uint16_t mps;
int ret;
uint8_t intf = 0xff;
uint8_t altsetting = 1;
if (!audio_class || !audio_class->hport) {
return -USB_ERR_INVAL;
}
setup = audio_class->hport->setup;
if (audio_class->is_opened) {
return 0;
}
for (uint8_t i = 0; i < audio_class->module_num; i++) {
if (strcmp(name, audio_class->module[i].name) == 0) {
for (uint8_t j = 0; j < audio_class->num_of_intf_altsettings; j++) {
for (uint8_t k = 0; k < audio_class->module[i].altsetting[j].sampfreq_num; k++) {
if (audio_class->module[i].altsetting[j].sampfreq[k] == samp_freq) {
intf = audio_class->module[i].data_intf;
altsetting = j;
goto freq_found;
}
}
}
}
}
return -USB_ERR_NODEV;
freq_found:
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = USB_REQUEST_SET_INTERFACE;
setup->wValue = altsetting;
setup->wIndex = intf;
setup->wLength = 0;
ret = usbh_control_transfer(audio_class->hport, setup, NULL);
if (ret < 0) {
return ret;
}
ep_desc = &audio_class->hport->config.intf[intf].altsetting[altsetting].ep[0].ep_desc;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_ENDPOINT;
setup->bRequest = AUDIO_REQUEST_SET_CUR;
setup->wValue = (AUDIO_EP_CONTROL_SAMPLING_FEQ << 8) | 0x00;
setup->wIndex = ep_desc->bEndpointAddress;
setup->wLength = 3;
memcpy(g_audio_buf, &samp_freq, 3);
ret = usbh_control_transfer(audio_class->hport, setup, g_audio_buf);
if (ret < 0) {
return ret;
}
mult = (ep_desc->wMaxPacketSize & USB_MAXPACKETSIZE_ADDITIONAL_TRANSCATION_MASK) >> USB_MAXPACKETSIZE_ADDITIONAL_TRANSCATION_SHIFT;
mps = ep_desc->wMaxPacketSize & USB_MAXPACKETSIZE_MASK;
if (ep_desc->bEndpointAddress & 0x80) {
audio_class->isoin_mps = mps * (mult + 1);
USBH_EP_INIT(audio_class->isoin, ep_desc);
} else {
audio_class->isoout_mps = mps * (mult + 1);
USBH_EP_INIT(audio_class->isoout, ep_desc);
}
USB_LOG_INFO("Open audio module :%s, altsetting: %u\r\n", name, altsetting);
audio_class->is_opened = true;
return ret;
}
int usbh_audio_close(struct usbh_audio *audio_class, const char *name)
{
struct usb_setup_packet *setup;
struct usb_endpoint_descriptor *ep_desc;
int ret;
uint8_t intf = 0xff;
uint8_t altsetting = 1;
if (!audio_class || !audio_class->hport) {
return -USB_ERR_INVAL;
}
setup = audio_class->hport->setup;
for (size_t i = 0; i < audio_class->module_num; i++) {
if (strcmp(name, audio_class->module[i].name) == 0) {
intf = audio_class->module[i].data_intf;
}
}
if (intf == 0xff) {
return -USB_ERR_NODEV;
}
USB_LOG_INFO("Close audio module :%s\r\n", name);
audio_class->is_opened = false;
ep_desc = &audio_class->hport->config.intf[intf].altsetting[altsetting].ep[0].ep_desc;
if (ep_desc->bEndpointAddress & 0x80) {
if (audio_class->isoin) {
audio_class->isoin = NULL;
}
} else {
if (audio_class->isoout) {
audio_class->isoout = NULL;
}
}
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = USB_REQUEST_SET_INTERFACE;
setup->wValue = 0;
setup->wIndex = intf;
setup->wLength = 0;
ret = usbh_control_transfer(audio_class->hport, setup, NULL);
return ret;
}
int usbh_audio_set_volume(struct usbh_audio *audio_class, const char *name, uint8_t ch, uint8_t volume)
{
struct usb_setup_packet *setup;
int ret;
uint8_t intf = 0xff;
uint8_t feature_id = 0xff;
uint16_t volume_hex;
if (!audio_class || !audio_class->hport) {
return -USB_ERR_INVAL;
}
setup = audio_class->hport->setup;
for (size_t i = 0; i < audio_class->module_num; i++) {
if (strcmp(name, audio_class->module[i].name) == 0) {
intf = audio_class->ctrl_intf;
feature_id = audio_class->module[i].feature_unit_id;
}
}
if (intf == 0xff) {
return -USB_ERR_NODEV;
}
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = AUDIO_REQUEST_SET_CUR;
setup->wValue = (AUDIO_FU_CONTROL_VOLUME << 8) | ch;
setup->wIndex = (feature_id << 8) | intf;
setup->wLength = 2;
volume_hex = -0xDB00 / 100 * volume + 0xdb00;
memcpy(g_audio_buf, &volume_hex, 2);
ret = usbh_control_transfer(audio_class->hport, setup, NULL);
return ret;
}
int usbh_audio_set_mute(struct usbh_audio *audio_class, const char *name, uint8_t ch, bool mute)
{
struct usb_setup_packet *setup;
int ret;
uint8_t intf = 0xff;
uint8_t feature_id = 0xff;
if (!audio_class || !audio_class->hport) {
return -USB_ERR_INVAL;
}
setup = audio_class->hport->setup;
for (size_t i = 0; i < audio_class->module_num; i++) {
if (strcmp(name, audio_class->module[i].name) == 0) {
intf = audio_class->ctrl_intf;
feature_id = audio_class->module[i].feature_unit_id;
}
}
if (intf == 0xff) {
return -USB_ERR_NODEV;
}
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = AUDIO_REQUEST_SET_CUR;
setup->wValue = (AUDIO_FU_CONTROL_MUTE << 8) | ch;
setup->wIndex = (feature_id << 8) | intf;
setup->wLength = 1;
memcpy(g_audio_buf, &mute, 1);
ret = usbh_control_transfer(audio_class->hport, setup, g_audio_buf);
return ret;
}
void usbh_audio_list_module(struct usbh_audio *audio_class)
{
USB_LOG_INFO("============= Audio module information ===================\r\n");
USB_LOG_RAW("bcdADC :%04x\r\n", audio_class->bcdADC);
USB_LOG_RAW("Num of modules :%u\r\n", audio_class->module_num);
USB_LOG_RAW("Num of altsettings:%u\r\n", audio_class->num_of_intf_altsettings);
for (uint8_t i = 0; i < audio_class->module_num; i++) {
USB_LOG_RAW(" module name :%s\r\n", audio_class->module[i].name);
USB_LOG_RAW(" module feature unit id :%d\r\n", audio_class->module[i].feature_unit_id);
for (uint8_t j = 0; j < audio_class->num_of_intf_altsettings; j++) {
if (j == 0) {
USB_LOG_RAW(" Ingore altsetting 0\r\n");
continue;
}
USB_LOG_RAW(" Altsetting %u\r\n", j);
USB_LOG_RAW(" module channels :%u\r\n", audio_class->module[i].altsetting[j].channels);
//USB_LOG_RAW(" module format_type :%u\r\n",audio_class->module[i].altsetting[j].format_type);
USB_LOG_RAW(" module bitresolution :%u\r\n", audio_class->module[i].altsetting[j].bitresolution);
USB_LOG_RAW(" module sampfreq num :%u\r\n", audio_class->module[i].altsetting[j].sampfreq_num);
for (uint8_t k = 0; k < audio_class->module[i].altsetting[j].sampfreq_num; k++) {
USB_LOG_RAW(" module sampfreq :%d hz\r\n", audio_class->module[i].altsetting[j].sampfreq[k]);
}
}
}
USB_LOG_INFO("============= Audio module information ===================\r\n");
}
static int usbh_audio_ctrl_connect(struct usbh_hubport *hport, uint8_t intf)
{
int ret;
uint8_t cur_iface = 0xff;
uint8_t cur_iface_count = 0xff;
uint8_t cur_alt_setting = 0xff;
uint8_t input_offset = 0;
uint8_t output_offset = 0;
uint8_t feature_unit_offset = 0;
uint8_t format_offset = 0;
uint8_t *p;
struct usbh_audio *audio_class = usbh_audio_class_alloc();
if (audio_class == NULL) {
USB_LOG_ERR("Fail to alloc audio_class\r\n");
return -USB_ERR_NOMEM;
}
audio_class->hport = hport;
audio_class->ctrl_intf = intf;
audio_class->num_of_intf_altsettings = hport->config.intf[intf + 1].altsetting_num;
hport->config.intf[intf].priv = audio_class;
p = hport->raw_config_desc;
while (p[DESC_bLength]) {
switch (p[DESC_bDescriptorType]) {
case USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION:
cur_iface_count = p[3];
break;
case USB_DESCRIPTOR_TYPE_INTERFACE:
cur_iface = p[INTF_DESC_bInterfaceNumber];
cur_alt_setting = p[INTF_DESC_bAlternateSetting];
break;
case USB_DESCRIPTOR_TYPE_ENDPOINT:
break;
case AUDIO_INTERFACE_DESCRIPTOR_TYPE:
if (cur_iface == audio_class->ctrl_intf) {
switch (p[DESC_bDescriptorSubType]) {
case AUDIO_CONTROL_HEADER: {
struct audio_cs_if_ac_header_descriptor *desc = (struct audio_cs_if_ac_header_descriptor *)p;
audio_class->bcdADC = desc->bcdADC;
audio_class->bInCollection = desc->bInCollection;
} break;
case AUDIO_CONTROL_INPUT_TERMINAL: {
struct audio_cs_if_ac_input_terminal_descriptor *desc = (struct audio_cs_if_ac_input_terminal_descriptor *)p;
audio_class->module[input_offset].input_terminal_id = desc->bTerminalID;
audio_class->module[input_offset].input_terminal_type = desc->wTerminalType;
audio_class->module[input_offset].input_channel_config = desc->wChannelConfig;
if (desc->wTerminalType == AUDIO_TERMINAL_STREAMING) {
audio_class->module[input_offset].terminal_link_id = desc->bTerminalID;
}
if (desc->wTerminalType == AUDIO_INTERM_MIC) {
audio_class->module[input_offset].name = "mic";
}
input_offset++;
} break;
break;
case AUDIO_CONTROL_OUTPUT_TERMINAL: {
struct audio_cs_if_ac_output_terminal_descriptor *desc = (struct audio_cs_if_ac_output_terminal_descriptor *)p;
audio_class->module[output_offset].output_terminal_id = desc->bTerminalID;
audio_class->module[output_offset].output_terminal_type = desc->wTerminalType;
if (desc->wTerminalType == AUDIO_TERMINAL_STREAMING) {
audio_class->module[output_offset].terminal_link_id = desc->bTerminalID;
}
if (desc->wTerminalType == AUDIO_OUTTERM_SPEAKER) {
audio_class->module[output_offset].name = "speaker";
}
output_offset++;
} break;
case AUDIO_CONTROL_FEATURE_UNIT: {
struct audio_cs_if_ac_feature_unit_descriptor *desc = (struct audio_cs_if_ac_feature_unit_descriptor *)p;
audio_class->module[feature_unit_offset].feature_unit_id = desc->bUnitID;
audio_class->module[feature_unit_offset].feature_unit_controlsize = desc->bControlSize;
for (uint8_t j = 0; j < desc->bControlSize; j++) {
audio_class->module[feature_unit_offset].feature_unit_controls[j] = p[6 + j];
}
feature_unit_offset++;
} break;
case AUDIO_CONTROL_PROCESSING_UNIT:
break;
default:
break;
}
} else if ((cur_iface < (audio_class->ctrl_intf + cur_iface_count)) && (cur_iface > audio_class->ctrl_intf)) {
switch (p[DESC_bDescriptorSubType]) {
case AUDIO_STREAMING_GENERAL:
break;
case AUDIO_STREAMING_FORMAT_TYPE: {
struct audio_cs_if_as_format_type_descriptor *desc = (struct audio_cs_if_as_format_type_descriptor *)p;
audio_class->module[format_offset].data_intf = cur_iface;
audio_class->module[format_offset].altsetting[cur_alt_setting].channels = desc->bNrChannels;
audio_class->module[format_offset].altsetting[cur_alt_setting].format_type = desc->bFormatType;
audio_class->module[format_offset].altsetting[cur_alt_setting].bitresolution = desc->bBitResolution;
audio_class->module[format_offset].altsetting[cur_alt_setting].sampfreq_num = desc->bSamFreqType;
for (uint8_t j = 0; j < desc->bSamFreqType; j++) {
audio_class->module[format_offset].altsetting[cur_alt_setting].sampfreq[j] = (uint32_t)(p[10 + j * 3] << 16) |
(uint32_t)(p[9 + j * 3] << 8) |
(uint32_t)(p[8 + j * 3] << 0);
}
if (cur_alt_setting == (hport->config.intf[intf + 1].altsetting_num - 1)) {
format_offset++;
}
} break;
default:
break;
}
}
break;
default:
break;
}
/* skip to next descriptor */
p += p[DESC_bLength];
}
if ((input_offset != output_offset) && (input_offset != feature_unit_offset) && (input_offset != format_offset)) {
return -USB_ERR_INVAL;
}
audio_class->module_num = input_offset;
for (size_t i = 0; i < audio_class->module_num; i++) {
ret = usbh_audio_close(audio_class, audio_class->module[i].name);
if (ret < 0) {
USB_LOG_ERR("Fail to close audio module :%s\r\n", audio_class->module[i].name);
return ret;
}
}
usbh_audio_list_module(audio_class);
snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, audio_class->minor);
USB_LOG_INFO("Register Audio Class:%s\r\n", hport->config.intf[intf].devname);
usbh_audio_run(audio_class);
return 0;
}
static int usbh_audio_ctrl_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
int ret = 0;
struct usbh_audio *audio_class = (struct usbh_audio *)hport->config.intf[intf].priv;
if (audio_class) {
if (audio_class->isoin) {
}
if (audio_class->isoout) {
}
if (hport->config.intf[intf].devname[0] != '\0') {
USB_LOG_INFO("Unregister Audio Class:%s\r\n", hport->config.intf[intf].devname);
usbh_audio_stop(audio_class);
}
usbh_audio_class_free(audio_class);
}
return ret;
}
static int usbh_audio_data_connect(struct usbh_hubport *hport, uint8_t intf)
{
return 0;
}
static int usbh_audio_data_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
return 0;
}
__WEAK void usbh_audio_run(struct usbh_audio *audio_class)
{
}
__WEAK void usbh_audio_stop(struct usbh_audio *audio_class)
{
}
const struct usbh_class_driver audio_ctrl_class_driver = {
.driver_name = "audio_ctrl",
.connect = usbh_audio_ctrl_connect,
.disconnect = usbh_audio_ctrl_disconnect
};
const struct usbh_class_driver audio_streaming_class_driver = {
.driver_name = "audio_streaming",
.connect = usbh_audio_data_connect,
.disconnect = usbh_audio_data_disconnect
};
CLASS_INFO_DEFINE const struct usbh_class_info audio_ctrl_intf_class_info = {
.match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS,
.class = USB_DEVICE_CLASS_AUDIO,
.subclass = AUDIO_SUBCLASS_AUDIOCONTROL,
.protocol = 0x00,
.id_table = NULL,
.class_driver = &audio_ctrl_class_driver
};
CLASS_INFO_DEFINE const struct usbh_class_info audio_streaming_intf_class_info = {
.match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS,
.class = USB_DEVICE_CLASS_AUDIO,
.subclass = AUDIO_SUBCLASS_AUDIOSTREAMING,
.protocol = 0x00,
.id_table = NULL,
.class_driver = &audio_streaming_class_driver
};

View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_AUDIO_H
#define USBH_AUDIO_H
#include "usb_audio.h"
struct usbh_audio_format_type {
uint8_t channels;
uint8_t format_type;
uint8_t bitresolution;
uint8_t sampfreq_num;
uint32_t sampfreq[3];
};
/**
* bSourceID in feature_unit = input_terminal_id
* bSourceID in output_terminal = feature_unit_id
* terminal_link_id = input_terminal_id or output_terminal_id (if input_terminal_type or output_terminal_type is 0x0101)
*
*
*/
struct usbh_audio_module {
const char *name;
uint8_t data_intf;
uint8_t input_terminal_id;
uint16_t input_terminal_type;
uint16_t input_channel_config;
uint8_t output_terminal_id;
uint16_t output_terminal_type;
uint8_t feature_unit_id;
uint8_t feature_unit_controlsize;
uint8_t feature_unit_controls[8];
uint8_t terminal_link_id;
struct usbh_audio_format_type altsetting[CONFIG_USBHOST_MAX_INTF_ALTSETTINGS];
};
struct usbh_audio {
struct usbh_hubport *hport;
struct usb_endpoint_descriptor *isoin; /* ISO IN endpoint */
struct usb_endpoint_descriptor *isoout; /* ISO OUT endpoint */
uint8_t ctrl_intf; /* interface number */
uint8_t minor;
uint16_t isoin_mps;
uint16_t isoout_mps;
bool is_opened;
uint16_t bcdADC;
uint8_t bInCollection;
uint8_t num_of_intf_altsettings;
struct usbh_audio_module module[2];
uint8_t module_num;
void *user_data;
};
#ifdef __cplusplus
extern "C" {
#endif
int usbh_audio_open(struct usbh_audio *audio_class, const char *name, uint32_t samp_freq);
int usbh_audio_close(struct usbh_audio *audio_class, const char *name);
int usbh_audio_set_volume(struct usbh_audio *audio_class, const char *name, uint8_t ch, uint8_t volume);
int usbh_audio_set_mute(struct usbh_audio *audio_class, const char *name, uint8_t ch, bool mute);
void usbh_audio_run(struct usbh_audio *audio_class);
void usbh_audio_stop(struct usbh_audio *audio_class);
#ifdef __cplusplus
}
#endif
#endif /* USBH_AUDIO_H */

View File

@@ -0,0 +1,698 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USB_CDC_H
#define USB_CDC_H
/*------------------------------------------------------------------------------
* Definitions based on usbcdc11.pdf (www.usb.org)
*----------------------------------------------------------------------------*/
/* Communication device class specification version 1.10 */
#define CDC_V1_10 0x0110U
// Communication device class specification version 1.2
#define CDC_V1_2_0 0x0120U
/* Communication interface class code */
/* (usbcdc11.pdf, 4.2, Table 15) */
#define CDC_COMMUNICATION_INTERFACE_CLASS 0x02U
/* Communication interface class subclass codes */
/* (usbcdc11.pdf, 4.3, Table 16) */
#define CDC_SUBCLASS_NONE 0x00 /* Reserved */
#define CDC_SUBCLASS_DLC 0x01 /* Direct Line Control Model */
#define CDC_SUBCLASS_ACM 0x02 /* Abstract Control Model */
#define CDC_SUBCLASS_TCM 0x03 /* Telephone Control Model */
#define CDC_SUBCLASS_MCM 0x04 /* Multi-Channel Control Model */
#define CDC_SUBCLASS_CAPI 0x05 /* CAPI Control Model */
#define CDC_SUBCLASS_ECM 0x06 /* Ethernet Networking Control Model */
#define CDC_SUBCLASS_ATM 0x07 /* ATM Networking Control Model */
/* 0x08-0x0d Reserved (future use) */
#define CDC_SUBCLASS_MBIM 0x0e /* MBIM Control Model */
/* 0x0f-0x7f Reserved (future use) */
/* 0x80-0xfe Reserved (vendor specific) */
#define CDC_DIRECT_LINE_CONTROL_MODEL 0x01U
#define CDC_ABSTRACT_CONTROL_MODEL 0x02U
#define CDC_TELEPHONE_CONTROL_MODEL 0x03U
#define CDC_MULTI_CHANNEL_CONTROL_MODEL 0x04U
#define CDC_CAPI_CONTROL_MODEL 0x05U
#define CDC_ETHERNET_NETWORKING_CONTROL_MODEL 0x06U
#define CDC_ATM_NETWORKING_CONTROL_MODEL 0x07U
#define CDC_WIRELESS_HANDSET_CONTROL_MODEL 0x08U
#define CDC_DEVICE_MANAGEMENT 0x09U
#define CDC_MOBILE_DIRECT_LINE_MODEL 0x0AU
#define CDC_OBEX 0x0BU
#define CDC_ETHERNET_EMULATION_MODEL 0x0CU
#define CDC_NETWORK_CONTROL_MODEL 0x0DU
/* Communication interface class control protocol codes */
/* (usbcdc11.pdf, 4.4, Table 17) */
#define CDC_COMMON_PROTOCOL_NONE 0x00U
#define CDC_COMMON_PROTOCOL_AT_COMMANDS 0x01U
#define CDC_COMMON_PROTOCOL_AT_COMMANDS_PCCA_101 0x02U
#define CDC_COMMON_PROTOCOL_AT_COMMANDS_PCCA_101_AND_ANNEXO 0x03U
#define CDC_COMMON_PROTOCOL_AT_COMMANDS_GSM_707 0x04U
#define CDC_COMMON_PROTOCOL_AT_COMMANDS_3GPP_27007 0x05U
#define CDC_COMMON_PROTOCOL_AT_COMMANDS_CDMA 0x06U
#define CDC_COMMON_PROTOCOL_ETHERNET_EMULATION_MODEL 0x07U
// NCM Communication Interface Protocol Codes
// (usbncm10.pdf, 4.2, Table 4-2)
#define CDC_NCM_PROTOCOL_NONE 0x00U
#define CDC_NCM_PROTOCOL_OEM 0xFEU
/* Data interface class code */
/* (usbcdc11.pdf, 4.5, Table 18) */
#define CDC_DATA_INTERFACE_CLASS 0x0A
/* Data Interface Sub-Class Codes ********************************************/
#define CDC_DATA_SUBCLASS_NONE 0x00
/* Data interface class protocol codes */
/* (usbcdc11.pdf, 4.7, Table 19) */
#define CDC_DATA_PROTOCOL_ISDN_BRI 0x30
#define CDC_DATA_PROTOCOL_HDLC 0x31
#define CDC_DATA_PROTOCOL_TRANSPARENT 0x32
#define CDC_DATA_PROTOCOL_Q921_MANAGEMENT 0x50
#define CDC_DATA_PROTOCOL_Q921_DATA_LINK 0x51
#define CDC_DATA_PROTOCOL_Q921_MULTIPLEXOR 0x52
#define CDC_DATA_PROTOCOL_V42 0x90
#define CDC_DATA_PROTOCOL_EURO_ISDN 0x91
#define CDC_DATA_PROTOCOL_V24_RATE_ADAPTATION 0x92
#define CDC_DATA_PROTOCOL_CAPI 0x93
#define CDC_DATA_PROTOCOL_HOST_BASED_DRIVER 0xFD
#define CDC_DATA_PROTOCOL_DESCRIBED_IN_PUFD 0xFE
/* Type values for bDescriptorType field of functional descriptors */
/* (usbcdc11.pdf, 5.2.3, Table 24) */
#define CDC_CS_INTERFACE 0x24
#define CDC_CS_ENDPOINT 0x25
/* Type values for bDescriptorSubtype field of functional descriptors */
/* (usbcdc11.pdf, 5.2.3, Table 25) */
#define CDC_FUNC_DESC_HEADER 0x00
#define CDC_FUNC_DESC_CALL_MANAGEMENT 0x01
#define CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT 0x02
#define CDC_FUNC_DESC_DIRECT_LINE_MANAGEMENT 0x03
#define CDC_FUNC_DESC_TELEPHONE_RINGER 0x04
#define CDC_FUNC_DESC_REPORTING_CAPABILITIES 0x05
#define CDC_FUNC_DESC_UNION 0x06
#define CDC_FUNC_DESC_COUNTRY_SELECTION 0x07
#define CDC_FUNC_DESC_TELEPHONE_OPERATIONAL_MODES 0x08
#define CDC_FUNC_DESC_USB_TERMINAL 0x09
#define CDC_FUNC_DESC_NETWORK_CHANNEL 0x0A
#define CDC_FUNC_DESC_PROTOCOL_UNIT 0x0B
#define CDC_FUNC_DESC_EXTENSION_UNIT 0x0C
#define CDC_FUNC_DESC_MULTI_CHANNEL_MANAGEMENT 0x0D
#define CDC_FUNC_DESC_CAPI_CONTROL_MANAGEMENT 0x0E
#define CDC_FUNC_DESC_ETHERNET_NETWORKING 0x0F
#define CDC_FUNC_DESC_ATM_NETWORKING 0x10
#define CDC_FUNC_DESC_WIRELESS_HANDSET_CONTROL_MODEL 0x11
#define CDC_FUNC_DESC_MOBILE_DIRECT_LINE_MODEL 0x12
#define CDC_FUNC_DESC_MOBILE_DIRECT_LINE_MODEL_DETAIL 0x13
#define CDC_FUNC_DESC_DEVICE_MANAGEMENT_MODEL 0x14
#define CDC_FUNC_DESC_OBEX 0x15
#define CDC_FUNC_DESC_COMMAND_SET 0x16
#define CDC_FUNC_DESC_COMMAND_SET_DETAIL 0x17
#define CDC_FUNC_DESC_TELEPHONE_CONTROL_MODEL 0x18
#define CDC_FUNC_DESC_OBEX_SERVICE_IDENTIFIER 0x19
#define CDC_FUNC_DESC_NCM 0x1A
/* CDC class-specific request codes */
/* (usbcdc11.pdf, 6.2, Table 46) */
/* see Table 45 for info about the specific requests. */
#define CDC_REQUEST_SEND_ENCAPSULATED_COMMAND 0x00
#define CDC_REQUEST_GET_ENCAPSULATED_RESPONSE 0x01
#define CDC_REQUEST_SET_COMM_FEATURE 0x02
#define CDC_REQUEST_GET_COMM_FEATURE 0x03
#define CDC_REQUEST_CLEAR_COMM_FEATURE 0x04
#define CDC_REQUEST_SET_AUX_LINE_STATE 0x10
#define CDC_REQUEST_SET_HOOK_STATE 0x11
#define CDC_REQUEST_PULSE_SETUP 0x12
#define CDC_REQUEST_SEND_PULSE 0x13
#define CDC_REQUEST_SET_PULSE_TIME 0x14
#define CDC_REQUEST_RING_AUX_JACK 0x15
#define CDC_REQUEST_SET_LINE_CODING 0x20
#define CDC_REQUEST_GET_LINE_CODING 0x21
#define CDC_REQUEST_SET_CONTROL_LINE_STATE 0x22
#define CDC_REQUEST_SEND_BREAK 0x23
#define CDC_REQUEST_SET_RINGER_PARMS 0x30
#define CDC_REQUEST_GET_RINGER_PARMS 0x31
#define CDC_REQUEST_SET_OPERATION_PARMS 0x32
#define CDC_REQUEST_GET_OPERATION_PARMS 0x33
#define CDC_REQUEST_SET_LINE_PARMS 0x34
#define CDC_REQUEST_GET_LINE_PARMS 0x35
#define CDC_REQUEST_DIAL_DIGITS 0x36
#define CDC_REQUEST_SET_UNIT_PARAMETER 0x37
#define CDC_REQUEST_GET_UNIT_PARAMETER 0x38
#define CDC_REQUEST_CLEAR_UNIT_PARAMETER 0x39
#define CDC_REQUEST_GET_PROFILE 0x3A
#define CDC_REQUEST_SET_ETHERNET_MULTICAST_FILTERS 0x40
#define CDC_REQUEST_SET_ETHERNET_PMP_FILTER 0x41
#define CDC_REQUEST_GET_ETHERNET_PMP_FILTER 0x42
#define CDC_REQUEST_SET_ETHERNET_PACKET_FILTER 0x43
#define CDC_REQUEST_GET_ETHERNET_STATISTIC 0x44
#define CDC_REQUEST_SET_ATM_DATA_FORMAT 0x50
#define CDC_REQUEST_GET_ATM_DEVICE_STATISTICS 0x51
#define CDC_REQUEST_SET_ATM_DEFAULT_VC 0x52
#define CDC_REQUEST_GET_ATM_VC_STATISTICS 0x53
#define CDC_REQUEST_GET_NTB_PARAMETERS 0x80
#define CDC_REQUEST_GET_NET_ADDRESS 0x81
#define CDC_REQUEST_SET_NET_ADDRESS 0x82
#define CDC_REQUEST_GET_NTB_FORMAT 0x83
#define CDC_REQUEST_SET_NTB_FORMAT 0x84
#define CDC_REQUEST_GET_NTB_INPUT_SIZE 0x85
#define CDC_REQUEST_SET_NTB_INPUT_SIZE 0x86
#define CDC_REQUEST_GET_MAX_DATAGRAM_SIZE 0x87
#define CDC_REQUEST_SET_MAX_DATAGRAM_SIZE 0x88
#define CDC_REQUEST_GET_CRC_MODE 0x89
#define CDC_REQUEST_SET_CRC_MODE 0x90
/* Communication feature selector codes */
/* (usbcdc11.pdf, 6.2.2..6.2.4, Table 47) */
#define CDC_ABSTRACT_STATE 0x01
#define CDC_COUNTRY_SETTING 0x02
/** Control Signal Bitmap Values for SetControlLineState */
#define SET_CONTROL_LINE_STATE_RTS 0x02
#define SET_CONTROL_LINE_STATE_DTR 0x01
/* Feature Status returned for ABSTRACT_STATE Selector */
/* (usbcdc11.pdf, 6.2.3, Table 48) */
#define CDC_IDLE_SETTING (1 << 0)
#define CDC_DATA_MULTPLEXED_STATE (1 << 1)
/* Control signal bitmap values for the SetControlLineState request */
/* (usbcdc11.pdf, 6.2.14, Table 51) */
#define CDC_DTE_PRESENT (1 << 0)
#define CDC_ACTIVATE_CARRIER (1 << 1)
/* CDC class-specific notification codes */
/* (usbcdc11.pdf, 6.3, Table 68) */
/* see Table 67 for Info about class-specific notifications */
#define CDC_NOTIFICATION_NETWORK_CONNECTION 0x00
#define CDC_RESPONSE_AVAILABLE 0x01
#define CDC_AUX_JACK_HOOK_STATE 0x08
#define CDC_RING_DETECT 0x09
#define CDC_NOTIFICATION_SERIAL_STATE 0x20
#define CDC_CALL_STATE_CHANGE 0x28
#define CDC_LINE_STATE_CHANGE 0x29
#define CDC_CONNECTION_SPEED_CHANGE 0x2A
/* UART state bitmap values (Serial state notification). */
/* (usbcdc11.pdf, 6.3.5, Table 69) */
#define CDC_SERIAL_STATE_OVERRUN (1 << 6) /* receive data overrun error has occurred */
#define CDC_SERIAL_STATE_OVERRUN_Pos (6)
#define CDC_SERIAL_STATE_OVERRUN_Msk (1 << CDC_SERIAL_STATE_OVERRUN_Pos)
#define CDC_SERIAL_STATE_PARITY (1 << 5) /* parity error has occurred */
#define CDC_SERIAL_STATE_PARITY_Pos (5)
#define CDC_SERIAL_STATE_PARITY_Msk (1 << CDC_SERIAL_STATE_PARITY_Pos)
#define CDC_SERIAL_STATE_FRAMING (1 << 4) /* framing error has occurred */
#define CDC_SERIAL_STATE_FRAMING_Pos (4)
#define CDC_SERIAL_STATE_FRAMING_Msk (1 << CDC_SERIAL_STATE_FRAMING_Pos)
#define CDC_SERIAL_STATE_RING (1 << 3) /* state of ring signal detection */
#define CDC_SERIAL_STATE_RING_Pos (3)
#define CDC_SERIAL_STATE_RING_Msk (1 << CDC_SERIAL_STATE_RING_Pos)
#define CDC_SERIAL_STATE_BREAK (1 << 2) /* state of break detection */
#define CDC_SERIAL_STATE_BREAK_Pos (2)
#define CDC_SERIAL_STATE_BREAK_Msk (1 << CDC_SERIAL_STATE_BREAK_Pos)
#define CDC_SERIAL_STATE_TX_CARRIER (1 << 1) /* state of transmission carrier */
#define CDC_SERIAL_STATE_TX_CARRIER_Pos (1)
#define CDC_SERIAL_STATE_TX_CARRIER_Msk (1 << CDC_SERIAL_STATE_TX_CARRIER_Pos)
#define CDC_SERIAL_STATE_RX_CARRIER (1 << 0) /* state of receiver carrier */
#define CDC_SERIAL_STATE_RX_CARRIER_Pos (0)
#define CDC_SERIAL_STATE_RX_CARRIER_Msk (1 << CDC_SERIAL_STATE_RX_CARRIER_Pos)
#define CDC_ECM_XMIT_OK (1 << 0)
#define CDC_ECM_RVC_OK (1 << 1)
#define CDC_ECM_XMIT_ERROR (1 << 2)
#define CDC_ECM_RCV_ERROR (1 << 3)
#define CDC_ECM_RCV_NO_BUFFER (1 << 4)
#define CDC_ECM_DIRECTED_BYTES_XMIT (1 << 5)
#define CDC_ECM_DIRECTED_FRAMES_XMIT (1 << 6)
#define CDC_ECM_MULTICAST_BYTES_XMIT (1 << 7)
#define CDC_ECM_MULTICAST_FRAMES_XMIT (1 << 8)
#define CDC_ECM_BROADCAST_BYTES_XMIT (1 << 9)
#define CDC_ECM_BROADCAST_FRAMES_XMIT (1 << 10)
#define CDC_ECM_DIRECTED_BYTES_RCV (1 << 11)
#define CDC_ECM_DIRECTED_FRAMES_RCV (1 << 12)
#define CDC_ECM_MULTICAST_BYTES_RCV (1 << 13)
#define CDC_ECM_MULTICAST_FRAMES_RCV (1 << 14)
#define CDC_ECM_BROADCAST_BYTES_RCV (1 << 15)
#define CDC_ECM_BROADCAST_FRAMES_RCV (1 << 16)
#define CDC_ECM_RCV_CRC_ERROR (1 << 17)
#define CDC_ECM_TRANSMIT_QUEUE_LENGTH (1 << 18)
#define CDC_ECM_RCV_ERROR_ALIGNMENT (1 << 19)
#define CDC_ECM_XMIT_ONE_COLLISION (1 << 20)
#define CDC_ECM_XMIT_MORE_COLLISIONS (1 << 21)
#define CDC_ECM_XMIT_DEFERRED (1 << 22)
#define CDC_ECM_XMIT_MAX_COLLISIONS (1 << 23)
#define CDC_ECM_RCV_OVERRUN (1 << 24)
#define CDC_ECM_XMIT_UNDERRUN (1 << 25)
#define CDC_ECM_XMIT_HEARTBEAT_FAILURE (1 << 26)
#define CDC_ECM_XMIT_TIMES_CRS_LOST (1 << 27)
#define CDC_ECM_XMIT_LATE_COLLISIONS (1 << 28)
#define CDC_ECM_MAC_STR_DESC (uint8_t *)"010202030000"
#define CDC_ECM_MAC_ADDR0 0x00U /* 01 */
#define CDC_ECM_MAC_ADDR1 0x02U /* 02 */
#define CDC_ECM_MAC_ADDR2 0x02U /* 03 */
#define CDC_ECM_MAC_ADDR3 0x03U /* 00 */
#define CDC_ECM_MAC_ADDR4 0x00U /* 00 */
#define CDC_ECM_MAC_ADDR5 0x00U /* 00 */
#define CDC_ECM_NET_DISCONNECTED 0x00U
#define CDC_ECM_NET_CONNECTED 0x01U
#define CDC_ECM_ETH_STATS_RESERVED 0xE0U
#define CDC_ECM_BMREQUEST_TYPE_ECM 0xA1U
#define CDC_ECM_CONNECT_SPEED_UPSTREAM 0x004C4B40U /* 5Mbps */
#define CDC_ECM_CONNECT_SPEED_DOWNSTREAM 0x004C4B40U /* 5Mbps */
#define CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION 0x00
#define CDC_ECM_NOTIFY_CODE_RESPONSE_AVAILABLE 0x01
#define CDC_ECM_NOTIFY_CODE_CONNECTION_SPEED_CHANGE 0x2A
#define CDC_NCM_NTH16_SIGNATURE 0x484D434E
#define CDC_NCM_NDP16_SIGNATURE_NCM0 0x304D434E
#define CDC_NCM_NDP16_SIGNATURE_NCM1 0x314D434E
/*------------------------------------------------------------------------------
* Structures based on usbcdc11.pdf (www.usb.org)
*----------------------------------------------------------------------------*/
/* Header functional descriptor */
/* (usbcdc11.pdf, 5.2.3.1) */
/* This header must precede any list of class-specific descriptors. */
struct cdc_header_descriptor {
uint8_t bFunctionLength; /* size of this descriptor in bytes */
uint8_t bDescriptorType; /* CS_INTERFACE descriptor type */
uint8_t bDescriptorSubtype; /* Header functional descriptor subtype */
uint16_t bcdCDC; /* USB CDC specification release version */
} __PACKED;
/* Call management functional descriptor */
/* (usbcdc11.pdf, 5.2.3.2) */
/* Describes the processing of calls for the communication class interface. */
struct cdc_call_management_descriptor {
uint8_t bFunctionLength; /* size of this descriptor in bytes */
uint8_t bDescriptorType; /* CS_INTERFACE descriptor type */
uint8_t bDescriptorSubtype; /* call management functional descriptor subtype */
uint8_t bmCapabilities; /* capabilities that this configuration supports */
uint8_t bDataInterface; /* interface number of the data class interface used for call management (optional) */
} __PACKED;
/* Abstract control management functional descriptor */
/* (usbcdc11.pdf, 5.2.3.3) */
/* Describes the command supported by the communication interface class with the Abstract Control Model subclass code. */
struct cdc_abstract_control_management_descriptor {
uint8_t bFunctionLength; /* size of this descriptor in bytes */
uint8_t bDescriptorType; /* CS_INTERFACE descriptor type */
uint8_t bDescriptorSubtype; /* abstract control management functional descriptor subtype */
uint8_t bmCapabilities; /* capabilities supported by this configuration */
} __PACKED;
/* Union functional descriptors */
/* (usbcdc11.pdf, 5.2.3.8) */
/* Describes the relationship between a group of interfaces that can be considered to form a functional unit. */
struct cdc_union_descriptor {
uint8_t bFunctionLength; /* size of this descriptor in bytes */
uint8_t bDescriptorType; /* CS_INTERFACE descriptor type */
uint8_t bDescriptorSubtype; /* union functional descriptor subtype */
uint8_t bMasterInterface; /* interface number designated as master */
} __PACKED;
/* Union functional descriptors with one slave interface */
/* (usbcdc11.pdf, 5.2.3.8) */
struct cdc_union_1slave_descriptor {
uint8_t bFunctionLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bControlInterface;
uint8_t bSubordinateInterface0;
} __PACKED;
/* Line coding structure for GET_LINE_CODING / SET_LINE_CODING class requests*/
/* Format of the data returned when a GetLineCoding request is received */
/* (usbcdc11.pdf, 6.2.13) */
struct cdc_line_coding {
uint32_t dwDTERate; /* Data terminal rate in bits per second */
uint8_t bCharFormat; /* Number of stop bits */
uint8_t bParityType; /* Parity bit type */
uint8_t bDataBits; /* Number of data bits */
} __PACKED;
/** Data structure for the notification about SerialState */
struct cdc_acm_notification {
uint8_t bmRequestType;
uint8_t bNotificationType;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
uint16_t data;
} __PACKED;
/** Ethernet Networking Functional Descriptor */
struct cdc_eth_descriptor {
uint8_t bFunctionLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t iMACAddress;
uint32_t bmEthernetStatistics;
uint16_t wMaxSegmentSize;
uint16_t wNumberMCFilters;
uint8_t bNumberPowerFilters;
} __PACKED;
struct cdc_eth_notification {
uint8_t bmRequestType;
uint8_t bNotificationType;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
uint8_t data[8];
} __PACKED;
struct cdc_ncm_ntb_parameters {
uint16_t wLength;
uint16_t bmNtbFormatsSupported;
uint32_t dwNtbInMaxSize;
uint16_t wNdbInDivisor;
uint16_t wNdbInPayloadRemainder;
uint16_t wNdbInAlignment;
uint16_t wReserved;
uint32_t dwNtbOutMaxSize;
uint16_t wNdbOutDivisor;
uint16_t wNdbOutPayloadRemainder;
uint16_t wNdbOutAlignment;
uint16_t wNtbOutMaxDatagrams;
};
struct cdc_ncm_nth16 {
uint32_t dwSignature;
uint16_t wHeaderLength;
uint16_t wSequence;
uint16_t wBlockLength;
uint16_t wNdpIndex;
};
struct cdc_ncm_ndp16_datagram {
uint16_t wDatagramIndex;
uint16_t wDatagramLength;
};
struct cdc_ncm_ndp16 {
uint32_t dwSignature;
uint16_t wLength;
uint16_t wNextNdpIndex;
struct cdc_ncm_ndp16_datagram datagram[];
};
/*Length of template descriptor: 66 bytes*/
#define CDC_ACM_DESCRIPTOR_LEN (8 + 9 + 5 + 5 + 4 + 5 + 7 + 9 + 7 + 7)
// clang-format off
#define CDC_ACM_DESCRIPTOR_INIT(bFirstInterface, int_ep, out_ep, in_ep, wMaxPacketSize, str_idx) \
/* Interface Associate */ \
0x08, /* bLength */ \
USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, /* bDescriptorType */ \
bFirstInterface, /* bFirstInterface */ \
0x02, /* bInterfaceCount */ \
USB_DEVICE_CLASS_CDC, /* bFunctionClass */ \
CDC_ABSTRACT_CONTROL_MODEL, /* bFunctionSubClass */ \
CDC_COMMON_PROTOCOL_AT_COMMANDS, /* bFunctionProtocol */ \
0x00, /* iFunction */ \
0x09, /* bLength */ \
USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
bFirstInterface, /* bInterfaceNumber */ \
0x00, /* bAlternateSetting */ \
0x01, /* bNumEndpoints */ \
USB_DEVICE_CLASS_CDC, /* bInterfaceClass */ \
CDC_ABSTRACT_CONTROL_MODEL, /* bInterfaceSubClass */ \
CDC_COMMON_PROTOCOL_AT_COMMANDS, /* bInterfaceProtocol */ \
str_idx, /* iInterface */ \
0x05, /* bLength */ \
CDC_CS_INTERFACE, /* bDescriptorType */ \
CDC_FUNC_DESC_HEADER, /* bDescriptorSubtype */ \
WBVAL(CDC_V1_10), /* bcdCDC */ \
0x05, /* bLength */ \
CDC_CS_INTERFACE, /* bDescriptorType */ \
CDC_FUNC_DESC_CALL_MANAGEMENT, /* bDescriptorSubtype */ \
0x00, /* bmCapabilities */ \
(uint8_t)(bFirstInterface + 1), /* bDataInterface */ \
0x04, /* bLength */ \
CDC_CS_INTERFACE, /* bDescriptorType */ \
CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, /* bDescriptorSubtype */ \
0x02, /* bmCapabilities */ \
0x05, /* bLength */ \
CDC_CS_INTERFACE, /* bDescriptorType */ \
CDC_FUNC_DESC_UNION, /* bDescriptorSubtype */ \
bFirstInterface, /* bMasterInterface */ \
(uint8_t)(bFirstInterface + 1), /* bSlaveInterface0 */ \
0x07, /* bLength */ \
USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
int_ep, /* bEndpointAddress */ \
0x03, /* bmAttributes */ \
0x08, 0x00, /* wMaxPacketSize */ \
0x0a, /* bInterval */ \
0x09, /* bLength */ \
USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
(uint8_t)(bFirstInterface + 1), /* bInterfaceNumber */ \
0x00, /* bAlternateSetting */ \
0x02, /* bNumEndpoints */ \
CDC_DATA_INTERFACE_CLASS, /* bInterfaceClass */ \
0x00, /* bInterfaceSubClass */ \
0x00, /* bInterfaceProtocol */ \
0x00, /* iInterface */ \
0x07, /* bLength */ \
USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
out_ep, /* bEndpointAddress */ \
0x02, /* bmAttributes */ \
WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
0x00, /* bInterval */ \
0x07, /* bLength */ \
USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
in_ep, /* bEndpointAddress */ \
0x02, /* bmAttributes */ \
WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
0x00 /* bInterval */
// clang-format on
/*Length of template descriptor: 66 bytes*/
#define CDC_RNDIS_DESCRIPTOR_LEN (8 + 9 + 5 + 5 + 4 + 5 + 7 + 9 + 7 + 7)
// clang-format off
#define CDC_RNDIS_DESCRIPTOR_INIT(bFirstInterface, int_ep, out_ep, in_ep, wMaxPacketSize, str_idx) \
/* Interface Associate */ \
0x08, /* bLength */ \
USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, /* bDescriptorType */ \
bFirstInterface, /* bFirstInterface */ \
0x02, /* bInterfaceCount */ \
USB_DEVICE_CLASS_WIRELESS, /* bFunctionClass */ \
CDC_DIRECT_LINE_CONTROL_MODEL, /* bFunctionSubClass */ \
CDC_COMMON_PROTOCOL_AT_COMMANDS_PCCA_101_AND_ANNEXO, /* bFunctionProtocol */ \
0x00, /* iFunction */ \
0x09, /* bLength */ \
USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
bFirstInterface, /* bInterfaceNumber */ \
0x00, /* bAlternateSetting */ \
0x01, /* bNumEndpoints */ \
USB_DEVICE_CLASS_WIRELESS, /* bInterfaceClass */ \
CDC_DIRECT_LINE_CONTROL_MODEL, /* bInterfaceSubClass */ \
CDC_COMMON_PROTOCOL_AT_COMMANDS_PCCA_101_AND_ANNEXO, /* bInterfaceProtocol */ \
str_idx, /* iInterface */ \
0x05, /* bLength */ \
CDC_CS_INTERFACE, /* bDescriptorType */ \
CDC_FUNC_DESC_HEADER, /* bDescriptorSubtype */ \
WBVAL(CDC_V1_10), /* bcdCDC */ \
0x05, /* bLength */ \
CDC_CS_INTERFACE, /* bDescriptorType */ \
CDC_FUNC_DESC_CALL_MANAGEMENT, /* bDescriptorSubtype */ \
0x00, /* bmCapabilities */ \
(uint8_t)(bFirstInterface + 1), /* bDataInterface */ \
0x04, /* bLength */ \
CDC_CS_INTERFACE, /* bDescriptorType */ \
CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, /* bDescriptorSubtype */ \
0x00, /* bmCapabilities */ \
0x05, /* bLength */ \
CDC_CS_INTERFACE, /* bDescriptorType */ \
CDC_FUNC_DESC_UNION, /* bDescriptorSubtype */ \
bFirstInterface, /* bMasterInterface */ \
(uint8_t)(bFirstInterface + 1), /* bSlaveInterface0 */ \
0x07, /* bLength */ \
USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
int_ep, /* bEndpointAddress */ \
0x03, /* bmAttributes */ \
0x08, 0x00, /* wMaxPacketSize */ \
0x10, /* bInterval */ \
0x09, /* bLength */ \
USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
(uint8_t)(bFirstInterface + 1), /* bInterfaceNumber */ \
0x00, /* bAlternateSetting */ \
0x02, /* bNumEndpoints */ \
CDC_DATA_INTERFACE_CLASS, /* bInterfaceClass */ \
0x00, /* bInterfaceSubClass */ \
0x00, /* bInterfaceProtocol */ \
0x00, /* iInterface */ \
0x07, /* bLength */ \
USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
out_ep, /* bEndpointAddress */ \
0x02, /* bmAttributes */ \
WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
0x00, /* bInterval */ \
0x07, /* bLength */ \
USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
in_ep, /* bEndpointAddress */ \
0x02, /* bmAttributes */ \
WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
0x00 /* bInterval */
// clang-format on
#define DBVAL_BE(x) ((x >> 24) & 0xFF), ((x >> 16) & 0xFF), ((x >> 8) & 0xFF), (x & 0xFF)
/*Length of template descriptor: 71 bytes*/
#define CDC_ECM_DESCRIPTOR_LEN (8 + 9 + 5 + 5 + 13 + 7 + 9 + 7 + 7)
// clang-format off
#define CDC_ECM_DESCRIPTOR_INIT(bFirstInterface, int_ep, out_ep, in_ep, wMaxPacketSize, \
eth_statistics, wMaxSegmentSize, wNumberMCFilters, bNumberPowerFilters, str_idx) \
/* Interface Associate */ \
0x08, /* bLength */ \
USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, /* bDescriptorType */ \
bFirstInterface, /* bFirstInterface */ \
0x02, /* bInterfaceCount */ \
USB_DEVICE_CLASS_CDC, /* bFunctionClass */ \
CDC_ETHERNET_NETWORKING_CONTROL_MODEL, /* bFunctionSubClass */ \
CDC_COMMON_PROTOCOL_NONE, /* bFunctionProtocol */ \
0x00, /* iFunction */ \
0x09, /* bLength */ \
USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
bFirstInterface, /* bInterfaceNumber */ \
0x00, /* bAlternateSetting */ \
0x01, /* bNumEndpoints */ \
USB_DEVICE_CLASS_CDC, /* bInterfaceClass */ \
CDC_ETHERNET_NETWORKING_CONTROL_MODEL, /* bInterfaceSubClass */ \
CDC_COMMON_PROTOCOL_NONE, /* bInterfaceProtocol */ \
str_idx, /* iInterface */ \
0x05, /* bLength */ \
CDC_CS_INTERFACE, /* bDescriptorType */ \
CDC_FUNC_DESC_HEADER, /* bDescriptorSubtype */ \
WBVAL(CDC_V1_10), /* bcdCDC */ \
0x05, /* bLength */ \
CDC_CS_INTERFACE, /* bDescriptorType */ \
CDC_FUNC_DESC_UNION, /* bDescriptorSubtype */ \
bFirstInterface, /* bMasterInterface */ \
(uint8_t)(bFirstInterface + 1), /* bSlaveInterface0 */ \
/* CDC_ECM Functional Descriptor */ \
0x0D, /* bFunctionLength */\
CDC_CS_INTERFACE, /* bDescriptorType: CS_INTERFACE */\
CDC_FUNC_DESC_ETHERNET_NETWORKING, /* Ethernet Networking functional descriptor subtype */\
str_idx, /* Device's MAC string index */\
DBVAL_BE(eth_statistics), /* Ethernet statistics (bitmap) */\
WBVAL(wMaxSegmentSize),/* wMaxSegmentSize: Ethernet Maximum Segment size, typically 1514 bytes */\
WBVAL(wNumberMCFilters), /* wNumberMCFilters: the number of multicast filters */\
bNumberPowerFilters, /* bNumberPowerFilters: the number of wakeup power filters */\
0x07, /* bLength */ \
USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
int_ep, /* bEndpointAddress */ \
0x03, /* bmAttributes */ \
0x10, 0x00, /* wMaxPacketSize */ \
0x10, /* bInterval */ \
0x09, /* bLength */ \
USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
(uint8_t)(bFirstInterface + 1), /* bInterfaceNumber */ \
0x00, /* bAlternateSetting */ \
0x02, /* bNumEndpoints */ \
CDC_DATA_INTERFACE_CLASS, /* bInterfaceClass */ \
0x00, /* bInterfaceSubClass */ \
0x00, /* bInterfaceProtocol */ \
0x00, /* iInterface */ \
0x07, /* bLength */ \
USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
out_ep, /* bEndpointAddress */ \
0x02, /* bmAttributes */ \
WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
0x00, /* bInterval */ \
0x07, /* bLength */ \
USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
in_ep, /* bEndpointAddress */ \
0x02, /* bmAttributes */ \
WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
0x00 /* bInterval */
// clang-format on
/*Length of template descriptor: 77 bytes*/
#define CDC_NCM_DESCRIPTOR_LEN (8 + 9 + 5 + 5 + 13 + 6 + 7 + 9 + 7 + 7)
// clang-format off
#define CDC_NCM_DESCRIPTOR_INIT(bFirstInterface, int_ep, out_ep, in_ep, wMaxPacketSize, \
eth_statistics, wMaxSegmentSize, wNumberMCFilters, bNumberPowerFilters, str_idx) \
/* Interface Associate */ \
0x08, /* bLength */ \
USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, /* bDescriptorType */ \
bFirstInterface, /* bFirstInterface */ \
0x02, /* bInterfaceCount */ \
USB_DEVICE_CLASS_CDC, /* bFunctionClass */ \
CDC_NETWORK_CONTROL_MODEL, /* bFunctionSubClass */ \
CDC_COMMON_PROTOCOL_NONE, /* bFunctionProtocol */ \
0x00, /* iFunction */ \
0x09, /* bLength */ \
USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
bFirstInterface, /* bInterfaceNumber */ \
0x00, /* bAlternateSetting */ \
0x01, /* bNumEndpoints */ \
USB_DEVICE_CLASS_CDC, /* bInterfaceClass */ \
CDC_NETWORK_CONTROL_MODEL, /* bInterfaceSubClass */ \
CDC_COMMON_PROTOCOL_NONE, /* bInterfaceProtocol */ \
str_idx, /* iInterface */ \
0x05, /* bLength */ \
CDC_CS_INTERFACE, /* bDescriptorType */ \
CDC_FUNC_DESC_HEADER, /* bDescriptorSubtype */ \
WBVAL(CDC_V1_10), /* bcdCDC */ \
0x05, /* bLength */ \
CDC_CS_INTERFACE, /* bDescriptorType */ \
CDC_FUNC_DESC_UNION, /* bDescriptorSubtype */ \
bFirstInterface, /* bMasterInterface */ \
(uint8_t)(bFirstInterface + 1), /* bSlaveInterface0 */ \
/* CDC ETH Functional Descriptor */ \
0x0D, /* bFunctionLength */\
CDC_CS_INTERFACE, /* bDescriptorType: CS_INTERFACE */\
CDC_FUNC_DESC_ETHERNET_NETWORKING, /* Ethernet Networking functional descriptor subtype */\
str_idx, /* Device's MAC string index */\
DBVAL_BE(eth_statistics), /* Ethernet statistics (bitmap) */\
WBVAL(wMaxPacketSize),/* wMaxSegmentSize: Ethernet Maximum Segment size, typically 1514 bytes */\
WBVAL(wNumberMCFilters), /* wNumberMCFilters: the number of multicast filters */\
bNumberPowerFilters, /* bNumberPowerFilters: the number of wakeup power filters */\
0x06, \
CDC_CS_INTERFACE, \
CDC_FUNC_DESC_NCM, \
0x00, 0x01, \
0x23, \
0x07, /* bLength */ \
USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
int_ep, /* bEndpointAddress */ \
0x03, /* bmAttributes */ \
0x10, 0x00, /* wMaxPacketSize */ \
0x10, /* bInterval */ \
0x09, /* bLength */ \
USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
(uint8_t)(bFirstInterface + 1), /* bInterfaceNumber */ \
0x00, /* bAlternateSetting */ \
0x02, /* bNumEndpoints */ \
CDC_DATA_INTERFACE_CLASS, /* bInterfaceClass */ \
0x00, /* bInterfaceSubClass */ \
0x00, /* bInterfaceProtocol */ \
0x00, /* iInterface */ \
0x07, /* bLength */ \
USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
out_ep, /* bEndpointAddress */ \
0x02, /* bmAttributes */ \
WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
0x00, /* bInterval */ \
0x07, /* bLength */ \
USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
in_ep, /* bEndpointAddress */ \
0x02, /* bmAttributes */ \
WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
0x00 /* bInterval */
// clang-format on
#endif /* USB_CDC_H */

View File

@@ -0,0 +1,118 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbd_core.h"
#include "usbd_cdc.h"
const char *stop_name[] = { "1", "1.5", "2" };
const char *parity_name[] = { "N", "O", "E", "M", "S" };
static int cdc_acm_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
{
USB_LOG_DBG("CDC Class request: "
"bRequest 0x%02x\r\n",
setup->bRequest);
struct cdc_line_coding line_coding;
bool dtr, rts;
uint8_t intf_num = LO_BYTE(setup->wIndex);
switch (setup->bRequest) {
case CDC_REQUEST_SET_LINE_CODING:
/*******************************************************************************/
/* Line Coding Structure */
/*-----------------------------------------------------------------------------*/
/* Offset | Field | Size | Value | Description */
/* 0 | dwDTERate | 4 | Number |Data terminal rate, in bits per second*/
/* 4 | bCharFormat | 1 | Number | Stop bits */
/* 0 - 1 Stop bit */
/* 1 - 1.5 Stop bits */
/* 2 - 2 Stop bits */
/* 5 | bParityType | 1 | Number | Parity */
/* 0 - None */
/* 1 - Odd */
/* 2 - Even */
/* 3 - Mark */
/* 4 - Space */
/* 6 | bDataBits | 1 | Number Data bits (5, 6, 7, 8 or 16). */
/*******************************************************************************/
memcpy(&line_coding, *data, setup->wLength);
USB_LOG_DBG("Set intf:%d linecoding <%d %d %s %s>\r\n",
intf_num,
line_coding.dwDTERate,
line_coding.bDataBits,
parity_name[line_coding.bParityType],
stop_name[line_coding.bCharFormat]);
usbd_cdc_acm_set_line_coding(busid, intf_num, &line_coding);
break;
case CDC_REQUEST_SET_CONTROL_LINE_STATE:
dtr = (setup->wValue & 0x0001);
rts = (setup->wValue & 0x0002);
USB_LOG_DBG("Set intf:%d DTR 0x%x,RTS 0x%x\r\n",
intf_num,
dtr,
rts);
usbd_cdc_acm_set_dtr(busid, intf_num, dtr);
usbd_cdc_acm_set_rts(busid, intf_num, rts);
break;
case CDC_REQUEST_GET_LINE_CODING:
usbd_cdc_acm_get_line_coding(busid, intf_num, &line_coding);
memcpy(*data, &line_coding, 7);
*len = 7;
USB_LOG_DBG("Get intf:%d linecoding %d %d %d %d\r\n",
intf_num,
line_coding.dwDTERate,
line_coding.bCharFormat,
line_coding.bParityType,
line_coding.bDataBits);
break;
case CDC_REQUEST_SEND_BREAK:
usbd_cdc_acm_send_break(busid, intf_num);
break;
default:
USB_LOG_WRN("Unhandled CDC Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
return 0;
}
struct usbd_interface *usbd_cdc_acm_init_intf(uint8_t busid, struct usbd_interface *intf)
{
intf->class_interface_handler = cdc_acm_class_interface_request_handler;
intf->class_endpoint_handler = NULL;
intf->vendor_handler = NULL;
intf->notify_handler = NULL;
return intf;
}
__WEAK void usbd_cdc_acm_set_line_coding(uint8_t busid, uint8_t intf, struct cdc_line_coding *line_coding)
{
}
__WEAK void usbd_cdc_acm_get_line_coding(uint8_t busid, uint8_t intf, struct cdc_line_coding *line_coding)
{
line_coding->dwDTERate = 2000000;
line_coding->bDataBits = 8;
line_coding->bParityType = 0;
line_coding->bCharFormat = 0;
}
__WEAK void usbd_cdc_acm_set_dtr(uint8_t busid, uint8_t intf, bool dtr)
{
}
__WEAK void usbd_cdc_acm_set_rts(uint8_t busid, uint8_t intf, bool rts)
{
}
__WEAK void usbd_cdc_acm_send_break(uint8_t busid, uint8_t intf)
{
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBD_CDC_H
#define USBD_CDC_H
#include "usb_cdc.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Init cdc acm interface driver */
struct usbd_interface *usbd_cdc_acm_init_intf(uint8_t busid, struct usbd_interface *intf);
/* Setup request command callback api */
void usbd_cdc_acm_set_line_coding(uint8_t busid, uint8_t intf, struct cdc_line_coding *line_coding);
void usbd_cdc_acm_get_line_coding(uint8_t busid, uint8_t intf, struct cdc_line_coding *line_coding);
void usbd_cdc_acm_set_dtr(uint8_t busid, uint8_t intf, bool dtr);
void usbd_cdc_acm_set_rts(uint8_t busid, uint8_t intf, bool rts);
void usbd_cdc_acm_send_break(uint8_t busid, uint8_t intf);
#ifdef __cplusplus
}
#endif
#endif /* USBD_CDC_H */

View File

@@ -0,0 +1,245 @@
/*
* Copyright (c) 2023, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbd_core.h"
#include "usbd_cdc_ecm.h"
#define CDC_ECM_OUT_EP_IDX 0
#define CDC_ECM_IN_EP_IDX 1
#define CDC_ECM_INT_EP_IDX 2
/* Describe EndPoints configuration */
static struct usbd_endpoint cdc_ecm_ep_data[3];
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_rx_buffer[CONFIG_CDC_ECM_ETH_MAX_SEGSZE];
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_tx_buffer[CONFIG_CDC_ECM_ETH_MAX_SEGSZE];
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_notify_buf[16];
volatile uint8_t *g_cdc_ecm_rx_data_buffer = NULL;
volatile uint32_t g_cdc_ecm_rx_data_length = 0;
volatile uint32_t g_cdc_ecm_tx_data_length = 0;
static volatile uint8_t g_current_net_status = 0;
static volatile uint8_t g_cmd_intf = 0;
static uint32_t g_connect_speed_table[2] = { CDC_ECM_CONNECT_SPEED_UPSTREAM,
CDC_ECM_CONNECT_SPEED_DOWNSTREAM };
void usbd_cdc_ecm_send_notify(uint8_t notifycode, uint8_t value, uint32_t *speed)
{
struct cdc_eth_notification *notify = (struct cdc_eth_notification *)g_cdc_ecm_notify_buf;
uint8_t bytes2send = 0;
notify->bmRequestType = CDC_ECM_BMREQUEST_TYPE_ECM;
notify->bNotificationType = notifycode;
switch (notifycode) {
case CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION:
notify->wValue = value;
notify->wIndex = g_cmd_intf;
notify->wLength = 0U;
for (uint8_t i = 0U; i < 8U; i++) {
notify->data[i] = 0U;
}
bytes2send = 8U;
break;
case CDC_ECM_NOTIFY_CODE_RESPONSE_AVAILABLE:
notify->wValue = 0U;
notify->wIndex = g_cmd_intf;
notify->wLength = 0U;
for (uint8_t i = 0U; i < 8U; i++) {
notify->data[i] = 0U;
}
bytes2send = 8U;
break;
case CDC_ECM_NOTIFY_CODE_CONNECTION_SPEED_CHANGE:
notify->wValue = 0U;
notify->wIndex = g_cmd_intf;
notify->wLength = 0x0008U;
bytes2send = 16U;
memcpy(notify->data, speed, 8);
break;
default:
break;
}
if (bytes2send) {
usbd_ep_start_write(0, cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX].ep_addr, g_cdc_ecm_notify_buf, bytes2send);
}
}
static int cdc_ecm_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
{
USB_LOG_DBG("CDC ECM Class request: "
"bRequest 0x%02x\r\n",
setup->bRequest);
g_cmd_intf = LO_BYTE(setup->wIndex);
switch (setup->bRequest) {
case CDC_REQUEST_SET_ETHERNET_PACKET_FILTER:
/* bit0 Promiscuous
* bit1 ALL Multicast
* bit2 Directed
* bit3 Broadcast
* bit4 Multicast
*/
if (g_current_net_status == 0) {
g_current_net_status = 1;
usbd_cdc_ecm_send_notify(CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION, CDC_ECM_NET_CONNECTED, NULL);
}
break;
default:
USB_LOG_WRN("Unhandled CDC ECM Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
return 0;
}
void cdc_ecm_notify_handler(uint8_t busid, uint8_t event, void *arg)
{
switch (event) {
case USBD_EVENT_RESET:
g_current_net_status = 0;
g_cdc_ecm_rx_data_length = 0;
g_cdc_ecm_tx_data_length = 0;
g_cdc_ecm_rx_data_buffer = NULL;
break;
case USBD_EVENT_CONFIGURED:
usbd_ep_start_read(0, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr, &g_cdc_ecm_rx_buffer[g_cdc_ecm_rx_data_length], usbd_get_ep_mps(busid, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr));
break;
default:
break;
}
}
void cdc_ecm_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
g_cdc_ecm_rx_data_length += nbytes;
if (nbytes < usbd_get_ep_mps(busid, ep)) {
g_cdc_ecm_rx_data_buffer = g_cdc_ecm_rx_buffer;
usbd_cdc_ecm_data_recv_done(g_cdc_ecm_rx_buffer, g_cdc_ecm_rx_data_length);
} else {
usbd_ep_start_read(0, ep, &g_cdc_ecm_rx_buffer[g_cdc_ecm_rx_data_length], usbd_get_ep_mps(busid, ep));
}
}
void cdc_ecm_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
if ((nbytes % usbd_get_ep_mps(busid, ep)) == 0 && nbytes) {
/* send zlp */
usbd_ep_start_write(0, ep, NULL, 0);
} else {
g_cdc_ecm_tx_data_length = 0;
}
}
void cdc_ecm_int_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
if (g_current_net_status == 1) {
g_current_net_status = 2;
usbd_cdc_ecm_send_notify(CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION, CDC_ECM_NET_CONNECTED, g_connect_speed_table);
}
}
int usbd_cdc_ecm_start_write(uint8_t *buf, uint32_t len)
{
if (g_cdc_ecm_tx_data_length > 0) {
return -USB_ERR_BUSY;
}
g_cdc_ecm_tx_data_length = len;
USB_LOG_DBG("txlen:%d\r\n", g_cdc_ecm_tx_data_length);
return usbd_ep_start_write(0, cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_addr, buf, g_cdc_ecm_tx_data_length);
}
void usbd_cdc_ecm_start_read_next(void)
{
g_cdc_ecm_rx_data_length = 0;
g_cdc_ecm_rx_data_buffer = NULL;
usbd_ep_start_read(0, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr, g_cdc_ecm_rx_buffer, usbd_get_ep_mps(0, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr));
}
#ifdef CONFIG_USBDEV_CDC_ECM_USING_LWIP
struct pbuf *usbd_cdc_ecm_eth_rx(void)
{
struct pbuf *p;
if (g_cdc_ecm_rx_data_buffer == NULL) {
return NULL;
}
p = pbuf_alloc(PBUF_RAW, g_cdc_ecm_rx_data_length, PBUF_POOL);
if (p == NULL) {
usbd_cdc_ecm_start_read_next();
return NULL;
}
usb_memcpy(p->payload, (uint8_t *)g_cdc_ecm_rx_buffer, g_cdc_ecm_rx_data_length);
p->len = g_cdc_ecm_rx_data_length;
USB_LOG_DBG("rxlen:%d\r\n", g_cdc_ecm_rx_data_length);
usbd_cdc_ecm_start_read_next();
return p;
}
int usbd_cdc_ecm_eth_tx(struct pbuf *p)
{
struct pbuf *q;
uint8_t *buffer;
if (g_cdc_ecm_tx_data_length > 0) {
return -USB_ERR_BUSY;
}
if (p->tot_len > sizeof(g_cdc_ecm_tx_buffer)) {
p->tot_len = sizeof(g_cdc_ecm_tx_buffer);
}
buffer = g_cdc_ecm_tx_buffer;
for (q = p; q != NULL; q = q->next) {
usb_memcpy(buffer, q->payload, q->len);
buffer += q->len;
}
return usbd_cdc_ecm_start_write(g_cdc_ecm_tx_buffer, p->tot_len);
}
#endif
struct usbd_interface *usbd_cdc_ecm_init_intf(struct usbd_interface *intf, const uint8_t int_ep, const uint8_t out_ep, const uint8_t in_ep)
{
intf->class_interface_handler = cdc_ecm_class_interface_request_handler;
intf->class_endpoint_handler = NULL;
intf->vendor_handler = NULL;
intf->notify_handler = cdc_ecm_notify_handler;
cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr = out_ep;
cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_cb = cdc_ecm_bulk_out;
cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_addr = in_ep;
cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_cb = cdc_ecm_bulk_in;
cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX].ep_addr = int_ep;
cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX].ep_cb = cdc_ecm_int_in;
usbd_add_endpoint(0, &cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX]);
usbd_add_endpoint(0, &cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX]);
usbd_add_endpoint(0, &cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX]);
return intf;
}
void usbd_cdc_ecm_set_connect_speed(uint32_t speed[2])
{
memcpy(g_connect_speed_table, speed, 8);
}
__WEAK void usbd_cdc_ecm_data_recv_done(uint8_t *buf, uint32_t len)
{
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBD_CDC_ECM_H
#define USBD_CDC_ECM_H
#include "usb_cdc.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Ethernet Maximum Segment size, typically 1514 bytes */
#define CONFIG_CDC_ECM_ETH_MAX_SEGSZE 1514U
/* Init cdc ecm interface driver */
struct usbd_interface *usbd_cdc_ecm_init_intf(struct usbd_interface *intf, const uint8_t int_ep, const uint8_t out_ep, const uint8_t in_ep);
/* Setup request command callback api */
void usbd_cdc_ecm_set_connect_speed(uint32_t speed[2]);
/* Api for eth only without any net stack */
uint8_t *usbd_cdc_ecm_get_tx_buffer(void);
void usbd_cdc_ecm_send_done(void);
int usbd_cdc_ecm_start_write(uint8_t *buf, uint32_t len);
void usbd_cdc_ecm_data_recv_done(uint8_t *buf, uint32_t len);
void usbd_cdc_ecm_start_read_next(void);
#ifdef CONFIG_USBDEV_CDC_ECM_USING_LWIP
#include "lwip/netif.h"
#include "lwip/pbuf.h"
struct pbuf *usbd_cdc_ecm_eth_rx(void);
int usbd_cdc_ecm_eth_tx(struct pbuf *p);
#endif
#ifdef __cplusplus
}
#endif
#endif /* USBD_CDC_ECM_H */

View File

@@ -0,0 +1,278 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbh_core.h"
#include "usbh_cdc_acm.h"
#undef USB_DBG_TAG
#define USB_DBG_TAG "usbh_cdc_acm"
#include "usb_log.h"
#define DEV_FORMAT "/dev/ttyACM%d"
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_acm_buf[64];
static struct usbh_cdc_acm g_cdc_acm_class[CONFIG_USBHOST_MAX_CDC_ACM_CLASS];
static uint32_t g_devinuse = 0;
static struct usbh_cdc_acm *usbh_cdc_acm_class_alloc(void)
{
int devno;
for (devno = 0; devno < CONFIG_USBHOST_MAX_CDC_ACM_CLASS; devno++) {
if ((g_devinuse & (1 << devno)) == 0) {
g_devinuse |= (1 << devno);
memset(&g_cdc_acm_class[devno], 0, sizeof(struct usbh_cdc_acm));
g_cdc_acm_class[devno].minor = devno;
return &g_cdc_acm_class[devno];
}
}
return NULL;
}
static void usbh_cdc_acm_class_free(struct usbh_cdc_acm *cdc_acm_class)
{
int devno = cdc_acm_class->minor;
if (devno >= 0 && devno < 32) {
g_devinuse &= ~(1 << devno);
}
memset(cdc_acm_class, 0, sizeof(struct usbh_cdc_acm));
}
int usbh_cdc_acm_set_line_coding(struct usbh_cdc_acm *cdc_acm_class, struct cdc_line_coding *line_coding)
{
struct usb_setup_packet *setup;
if (!cdc_acm_class || !cdc_acm_class->hport) {
return -USB_ERR_INVAL;
}
setup = cdc_acm_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CDC_REQUEST_SET_LINE_CODING;
setup->wValue = 0;
setup->wIndex = cdc_acm_class->intf;
setup->wLength = 7;
memcpy(g_cdc_acm_buf, line_coding, sizeof(struct cdc_line_coding));
return usbh_control_transfer(cdc_acm_class->hport, setup, g_cdc_acm_buf);
}
int usbh_cdc_acm_get_line_coding(struct usbh_cdc_acm *cdc_acm_class, struct cdc_line_coding *line_coding)
{
struct usb_setup_packet *setup;
int ret;
if (!cdc_acm_class || !cdc_acm_class->hport) {
return -USB_ERR_INVAL;
}
setup = cdc_acm_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CDC_REQUEST_GET_LINE_CODING;
setup->wValue = 0;
setup->wIndex = cdc_acm_class->intf;
setup->wLength = 7;
ret = usbh_control_transfer(cdc_acm_class->hport, setup, g_cdc_acm_buf);
if (ret < 0) {
return ret;
}
memcpy(line_coding, g_cdc_acm_buf, sizeof(struct cdc_line_coding));
return ret;
}
int usbh_cdc_acm_set_line_state(struct usbh_cdc_acm *cdc_acm_class, bool dtr, bool rts)
{
struct usb_setup_packet *setup;
if (!cdc_acm_class || !cdc_acm_class->hport) {
return -USB_ERR_INVAL;
}
setup = cdc_acm_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE;
setup->wValue = (dtr << 0) | (rts << 1);
setup->wIndex = cdc_acm_class->intf;
setup->wLength = 0;
return usbh_control_transfer(cdc_acm_class->hport, setup, NULL);
}
static int usbh_cdc_acm_connect(struct usbh_hubport *hport, uint8_t intf)
{
struct usb_endpoint_descriptor *ep_desc;
int ret = 0;
struct usbh_cdc_acm *cdc_acm_class = usbh_cdc_acm_class_alloc();
if (cdc_acm_class == NULL) {
USB_LOG_ERR("Fail to alloc cdc_acm_class\r\n");
return -USB_ERR_NOMEM;
}
cdc_acm_class->hport = hport;
cdc_acm_class->intf = intf;
hport->config.intf[intf].priv = cdc_acm_class;
hport->config.intf[intf + 1].priv = NULL;
#ifdef CONFIG_USBHOST_CDC_ACM_NOTIFY
ep_desc = &hport->config.intf[intf].altsetting[0].ep[0].ep_desc;
USBH_EP_INIT(cdc_acm_class->intin, ep_desc);
#endif
for (uint8_t i = 0; i < hport->config.intf[intf + 1].altsetting[0].intf_desc.bNumEndpoints; i++) {
ep_desc = &hport->config.intf[intf + 1].altsetting[0].ep[i].ep_desc;
if (ep_desc->bEndpointAddress & 0x80) {
USBH_EP_INIT(cdc_acm_class->bulkin, ep_desc);
} else {
USBH_EP_INIT(cdc_acm_class->bulkout, ep_desc);
}
}
snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, cdc_acm_class->minor);
USB_LOG_INFO("Register CDC ACM Class:%s\r\n", hport->config.intf[intf].devname);
#if 0
USB_LOG_INFO("Test cdc acm rx and tx and rx for 5 times, baudrate is 115200\r\n");
struct cdc_line_coding linecoding;
uint8_t count = 5;
linecoding.dwDTERate = 115200;
linecoding.bDataBits = 8;
linecoding.bParityType = 0;
linecoding.bCharFormat = 0;
usbh_cdc_acm_set_line_coding(cdc_acm_class, &linecoding);
usbh_cdc_acm_set_line_state(cdc_acm_class, true, false);
memset(g_cdc_acm_buf, 'a', sizeof(g_cdc_acm_buf));
ret = usbh_cdc_acm_bulk_out_transfer(cdc_acm_class, g_cdc_acm_buf, sizeof(g_cdc_acm_buf), 0xfffffff);
USB_LOG_RAW("out ret:%d\r\n", ret);
while (count--) {
ret = usbh_cdc_acm_bulk_in_transfer(cdc_acm_class, g_cdc_acm_buf, sizeof(g_cdc_acm_buf), 0xfffffff);
USB_LOG_RAW("in ret:%d\r\n", ret);
if (ret > 0) {
for (uint32_t i = 0; i < ret; i++) {
USB_LOG_RAW("%02x ", g_cdc_acm_buf[i]);
}
}
USB_LOG_RAW("\r\n");
}
#endif
usbh_cdc_acm_run(cdc_acm_class);
return ret;
}
static int usbh_cdc_acm_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
int ret = 0;
struct usbh_cdc_acm *cdc_acm_class = (struct usbh_cdc_acm *)hport->config.intf[intf].priv;
if (cdc_acm_class) {
if (cdc_acm_class->bulkin) {
usbh_kill_urb(&cdc_acm_class->bulkin_urb);
}
if (cdc_acm_class->bulkout) {
usbh_kill_urb(&cdc_acm_class->bulkout_urb);
}
#ifdef CONFIG_USBHOST_CDC_ACM_NOTIFY
if (cdc_acm_class->intin) {
usbh_kill_urb(&cdc_acm_class->intin_urb);
}
#endif
if (hport->config.intf[intf].devname[0] != '\0') {
USB_LOG_INFO("Unregister CDC ACM Class:%s\r\n", hport->config.intf[intf].devname);
usbh_cdc_acm_stop(cdc_acm_class);
}
usbh_cdc_acm_class_free(cdc_acm_class);
}
return ret;
}
int usbh_cdc_acm_bulk_in_transfer(struct usbh_cdc_acm *cdc_acm_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
{
int ret;
struct usbh_urb *urb = &cdc_acm_class->bulkin_urb;
usbh_bulk_urb_fill(urb, cdc_acm_class->hport, cdc_acm_class->bulkin, buffer, buflen, timeout, NULL, NULL);
ret = usbh_submit_urb(urb);
if (ret == 0) {
ret = urb->actual_length;
}
return ret;
}
int usbh_cdc_acm_bulk_out_transfer(struct usbh_cdc_acm *cdc_acm_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
{
int ret;
struct usbh_urb *urb = &cdc_acm_class->bulkout_urb;
usbh_bulk_urb_fill(urb, cdc_acm_class->hport, cdc_acm_class->bulkout, buffer, buflen, timeout, NULL, NULL);
ret = usbh_submit_urb(urb);
if (ret == 0) {
ret = urb->actual_length;
}
return ret;
}
static int usbh_cdc_data_connect(struct usbh_hubport *hport, uint8_t intf)
{
return 0;
}
static int usbh_cdc_data_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
return 0;
}
__WEAK void usbh_cdc_acm_run(struct usbh_cdc_acm *cdc_acm_class)
{
}
__WEAK void usbh_cdc_acm_stop(struct usbh_cdc_acm *cdc_acm_class)
{
}
const struct usbh_class_driver cdc_acm_class_driver = {
.driver_name = "cdc_acm",
.connect = usbh_cdc_acm_connect,
.disconnect = usbh_cdc_acm_disconnect
};
const struct usbh_class_driver cdc_data_class_driver = {
.driver_name = "cdc_data",
.connect = usbh_cdc_data_connect,
.disconnect = usbh_cdc_data_disconnect
};
CLASS_INFO_DEFINE const struct usbh_class_info cdc_acm_class_info = {
.match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
.class = USB_DEVICE_CLASS_CDC,
.subclass = CDC_ABSTRACT_CONTROL_MODEL,
.protocol = CDC_COMMON_PROTOCOL_AT_COMMANDS,
.id_table = NULL,
.class_driver = &cdc_acm_class_driver
};
CLASS_INFO_DEFINE const struct usbh_class_info cdc_data_class_info = {
.match_flags = USB_CLASS_MATCH_INTF_CLASS,
.class = USB_DEVICE_CLASS_CDC_DATA,
.subclass = 0x00,
.protocol = 0x00,
.id_table = NULL,
.class_driver = &cdc_data_class_driver
};

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_CDC_ACM_H
#define USBH_CDC_ACM_H
#include "usb_cdc.h"
struct usbh_cdc_acm {
struct usbh_hubport *hport;
struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
#ifdef CONFIG_USBHOST_CDC_ACM_NOTIFY
struct usb_endpoint_descriptor *intin; /* INTR IN endpoint (optional) */
#endif
struct usbh_urb bulkout_urb;
struct usbh_urb bulkin_urb;
#ifdef CONFIG_USBHOST_CDC_ACM_NOTIFY
struct usbh_urb intin_urb;
#endif
struct cdc_line_coding linecoding;
uint8_t intf;
uint8_t minor;
void *user_data;
};
#ifdef __cplusplus
extern "C" {
#endif
int usbh_cdc_acm_set_line_coding(struct usbh_cdc_acm *cdc_acm_class, struct cdc_line_coding *line_coding);
int usbh_cdc_acm_get_line_coding(struct usbh_cdc_acm *cdc_acm_class, struct cdc_line_coding *line_coding);
int usbh_cdc_acm_set_line_state(struct usbh_cdc_acm *cdc_acm_class, bool dtr, bool rts);
int usbh_cdc_acm_bulk_in_transfer(struct usbh_cdc_acm *cdc_acm_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
int usbh_cdc_acm_bulk_out_transfer(struct usbh_cdc_acm *cdc_acm_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
void usbh_cdc_acm_run(struct usbh_cdc_acm *cdc_acm_class);
void usbh_cdc_acm_stop(struct usbh_cdc_acm *cdc_acm_class);
#ifdef __cplusplus
}
#endif
#endif /* USBH_CDC_ACM_H */

View File

@@ -0,0 +1,328 @@
/*
* Copyright (c) 2024, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbh_core.h"
#include "usbh_cdc_ecm.h"
#undef USB_DBG_TAG
#define USB_DBG_TAG "usbh_cdc_ecm"
#include "usb_log.h"
#define DEV_FORMAT "/dev/cdc_ether"
/* general descriptor field offsets */
#define DESC_bLength 0 /** Length offset */
#define DESC_bDescriptorType 1 /** Descriptor type offset */
#define DESC_bDescriptorSubType 2 /** Descriptor subtype offset */
/* interface descriptor field offsets */
#define INTF_DESC_bInterfaceNumber 2 /** Interface number offset */
#define INTF_DESC_bAlternateSetting 3 /** Alternate setting offset */
#define CONFIG_USBHOST_CDC_ECM_PKT_FILTER 0x000C
#define CONFIG_USBHOST_CDC_ECM_ETH_MAX_SIZE 1514U
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_rx_buffer[CONFIG_USBHOST_CDC_ECM_ETH_MAX_SIZE];
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_tx_buffer[CONFIG_USBHOST_CDC_ECM_ETH_MAX_SIZE];
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_inttx_buffer[16];
static struct usbh_cdc_ecm g_cdc_ecm_class;
static int usbh_cdc_ecm_set_eth_packet_filter(struct usbh_cdc_ecm *cdc_ecm_class, uint16_t filter_value)
{
struct usb_setup_packet *setup;
if (!cdc_ecm_class || !cdc_ecm_class->hport) {
return -USB_ERR_INVAL;
}
setup = cdc_ecm_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CDC_REQUEST_SET_ETHERNET_PACKET_FILTER;
setup->wValue = filter_value;
setup->wIndex = cdc_ecm_class->ctrl_intf;
setup->wLength = 0;
return usbh_control_transfer(cdc_ecm_class->hport, setup, NULL);
}
int usbh_cdc_ecm_get_connect_status(struct usbh_cdc_ecm *cdc_ecm_class)
{
int ret;
usbh_int_urb_fill(&cdc_ecm_class->intin_urb, cdc_ecm_class->hport, cdc_ecm_class->intin, g_cdc_ecm_inttx_buffer, 16, USB_OSAL_WAITING_FOREVER, NULL, NULL);
ret = usbh_submit_urb(&cdc_ecm_class->intin_urb);
if (ret < 0) {
return ret;
}
if (g_cdc_ecm_inttx_buffer[1] == CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION) {
if (g_cdc_ecm_inttx_buffer[2] == CDC_ECM_NET_CONNECTED) {
cdc_ecm_class->connect_status = true;
} else {
cdc_ecm_class->connect_status = false;
}
} else if (g_cdc_ecm_inttx_buffer[1] == CDC_ECM_NOTIFY_CODE_CONNECTION_SPEED_CHANGE) {
memcpy(cdc_ecm_class->speed, &g_cdc_ecm_inttx_buffer[8], 8);
}
return 0;
}
static int usbh_cdc_ecm_connect(struct usbh_hubport *hport, uint8_t intf)
{
struct usb_endpoint_descriptor *ep_desc;
int ret;
uint8_t altsetting = 0;
char mac_buffer[12];
uint8_t *p;
uint8_t cur_iface = 0xff;
uint8_t mac_str_idx = 0xff;
struct usbh_cdc_ecm *cdc_ecm_class = &g_cdc_ecm_class;
memset(cdc_ecm_class, 0, sizeof(struct usbh_cdc_ecm));
cdc_ecm_class->hport = hport;
cdc_ecm_class->ctrl_intf = intf;
cdc_ecm_class->data_intf = intf + 1;
hport->config.intf[intf].priv = cdc_ecm_class;
hport->config.intf[intf + 1].priv = NULL;
p = hport->raw_config_desc;
while (p[DESC_bLength]) {
switch (p[DESC_bDescriptorType]) {
case USB_DESCRIPTOR_TYPE_INTERFACE:
cur_iface = p[INTF_DESC_bInterfaceNumber];
//cur_alt_setting = p[INTF_DESC_bAlternateSetting];
break;
case CDC_CS_INTERFACE:
if ((cur_iface == cdc_ecm_class->ctrl_intf) && p[DESC_bDescriptorSubType] == CDC_FUNC_DESC_ETHERNET_NETWORKING) {
struct cdc_eth_descriptor *desc = (struct cdc_eth_descriptor *)p;
mac_str_idx = desc->iMACAddress;
cdc_ecm_class->max_segment_size = desc->wMaxSegmentSize;
goto get_mac;
}
break;
default:
break;
}
/* skip to next descriptor */
p += p[DESC_bLength];
}
get_mac:
if (mac_str_idx == 0xff) {
USB_LOG_ERR("Do not find cdc ecm mac string\r\n");
return -1;
}
memset(mac_buffer, 0, 12);
ret = usbh_get_string_desc(cdc_ecm_class->hport, mac_str_idx, (uint8_t *)mac_buffer);
if (ret < 0) {
return ret;
}
for (int i = 0, j = 0; i < 12; i += 2, j++) {
char byte_str[3];
byte_str[0] = mac_buffer[i];
byte_str[1] = mac_buffer[i + 1];
byte_str[2] = '\0';
uint32_t byte = strtoul(byte_str, NULL, 16);
cdc_ecm_class->mac[j] = (unsigned char)byte;
}
USB_LOG_INFO("CDC ECM MAC address %02x:%02x:%02x:%02x:%02x:%02x\r\n",
cdc_ecm_class->mac[0],
cdc_ecm_class->mac[1],
cdc_ecm_class->mac[2],
cdc_ecm_class->mac[3],
cdc_ecm_class->mac[4],
cdc_ecm_class->mac[5]);
if (cdc_ecm_class->max_segment_size > CONFIG_USBHOST_CDC_ECM_ETH_MAX_SIZE) {
USB_LOG_ERR("CDC ECM Max Segment Size is overflow, default is %u, but now %u\r\n", CONFIG_USBHOST_CDC_ECM_ETH_MAX_SIZE, cdc_ecm_class->max_segment_size);
} else {
USB_LOG_INFO("CDC ECM Max Segment Size:%u\r\n", cdc_ecm_class->max_segment_size);
}
/* enable int ep */
ep_desc = &hport->config.intf[intf].altsetting[0].ep[0].ep_desc;
USBH_EP_INIT(cdc_ecm_class->intin, ep_desc);
if (hport->config.intf[intf + 1].altsetting_num > 1) {
altsetting = hport->config.intf[intf + 1].altsetting_num - 1;
for (uint8_t i = 0; i < hport->config.intf[intf + 1].altsetting[altsetting].intf_desc.bNumEndpoints; i++) {
ep_desc = &hport->config.intf[intf + 1].altsetting[altsetting].ep[i].ep_desc;
if (ep_desc->bEndpointAddress & 0x80) {
USBH_EP_INIT(cdc_ecm_class->bulkin, ep_desc);
} else {
USBH_EP_INIT(cdc_ecm_class->bulkout, ep_desc);
}
}
USB_LOG_INFO("Select cdc ecm altsetting: %d\r\n", altsetting);
usbh_set_interface(cdc_ecm_class->hport, cdc_ecm_class->data_intf, altsetting);
} else {
for (uint8_t i = 0; i < hport->config.intf[intf + 1].altsetting[0].intf_desc.bNumEndpoints; i++) {
ep_desc = &hport->config.intf[intf + 1].altsetting[0].ep[i].ep_desc;
if (ep_desc->bEndpointAddress & 0x80) {
USBH_EP_INIT(cdc_ecm_class->bulkin, ep_desc);
} else {
USBH_EP_INIT(cdc_ecm_class->bulkout, ep_desc);
}
}
}
/* bit0 Promiscuous
* bit1 ALL Multicast
* bit2 Directed
* bit3 Broadcast
* bit4 Multicast
*/
ret = usbh_cdc_ecm_set_eth_packet_filter(cdc_ecm_class, CONFIG_USBHOST_CDC_ECM_PKT_FILTER);
if (ret < 0) {
return ret;
}
USB_LOG_INFO("Set CDC ECM packet filter:%04x\r\n", CONFIG_USBHOST_CDC_ECM_PKT_FILTER);
strncpy(hport->config.intf[intf].devname, DEV_FORMAT, CONFIG_USBHOST_DEV_NAMELEN);
USB_LOG_INFO("Register CDC ECM Class:%s\r\n", hport->config.intf[intf].devname);
usbh_cdc_ecm_run(cdc_ecm_class);
return ret;
}
static int usbh_cdc_ecm_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
int ret = 0;
struct usbh_cdc_ecm *cdc_ecm_class = (struct usbh_cdc_ecm *)hport->config.intf[intf].priv;
if (cdc_ecm_class) {
if (cdc_ecm_class->bulkin) {
usbh_kill_urb(&cdc_ecm_class->bulkin_urb);
}
if (cdc_ecm_class->bulkout) {
usbh_kill_urb(&cdc_ecm_class->bulkout_urb);
}
if (cdc_ecm_class->intin) {
usbh_kill_urb(&cdc_ecm_class->intin_urb);
}
if (hport->config.intf[intf].devname[0] != '\0') {
USB_LOG_INFO("Unregister CDC ECM Class:%s\r\n", hport->config.intf[intf].devname);
usbh_cdc_ecm_stop(cdc_ecm_class);
}
memset(cdc_ecm_class, 0, sizeof(struct usbh_cdc_ecm));
}
return ret;
}
void usbh_cdc_ecm_rx_thread(void *argument)
{
uint32_t g_cdc_ecm_rx_length;
int ret;
USB_LOG_INFO("Create cdc ecm rx thread\r\n");
// clang-format off
find_class:
// clang-format on
g_cdc_ecm_class.connect_status = false;
if (usbh_find_class_instance("/dev/cdc_ether") == NULL) {
goto delete;
}
while (g_cdc_ecm_class.connect_status == false) {
ret = usbh_cdc_ecm_get_connect_status(&g_cdc_ecm_class);
if (ret < 0) {
usb_osal_msleep(100);
goto find_class;
}
usb_osal_msleep(128);
}
g_cdc_ecm_rx_length = 0;
while (1) {
usbh_bulk_urb_fill(&g_cdc_ecm_class.bulkin_urb, g_cdc_ecm_class.hport, g_cdc_ecm_class.bulkin, g_cdc_ecm_rx_buffer, CONFIG_USBHOST_CDC_ECM_ETH_MAX_SIZE, USB_OSAL_WAITING_FOREVER, NULL, NULL);
ret = usbh_submit_urb(&g_cdc_ecm_class.bulkin_urb);
if (ret < 0) {
goto find_class;
}
g_cdc_ecm_rx_length = g_cdc_ecm_class.bulkin_urb.actual_length;
/* A transfer is complete because last packet is a short packet.
* Short packet is not zero, match g_cdc_ecm_rx_length % USB_GET_MAXPACKETSIZE(g_cdc_ecm_class.bulkin->wMaxPacketSize).
* Short packet is zero, check if g_cdc_ecm_class.bulkin_urb.actual_length < transfer_size, for example transfer is complete with size is 512 < 1514.
* This case is always true
*/
if (g_cdc_ecm_rx_length % USB_GET_MAXPACKETSIZE(g_cdc_ecm_class.bulkin->wMaxPacketSize) ||
(g_cdc_ecm_class.bulkin_urb.actual_length < CONFIG_USBHOST_CDC_ECM_ETH_MAX_SIZE)) {
USB_LOG_DBG("rxlen:%d\r\n", g_cdc_ecm_rx_length);
usbh_cdc_ecm_eth_input(g_cdc_ecm_rx_buffer, g_cdc_ecm_rx_length);
g_cdc_ecm_rx_length = 0;
} else {
/* There's no way to run here. */
}
}
// clang-format off
delete:
USB_LOG_INFO("Delete cdc ecm rx thread\r\n");
usb_osal_thread_delete(NULL);
// clang-format on
}
uint8_t *usbh_cdc_ecm_get_eth_txbuf(void)
{
return g_cdc_ecm_tx_buffer;
}
int usbh_cdc_ecm_eth_output(uint32_t buflen)
{
if (g_cdc_ecm_class.connect_status == false) {
return -USB_ERR_NOTCONN;
}
USB_LOG_DBG("txlen:%d\r\n", buflen);
usbh_bulk_urb_fill(&g_cdc_ecm_class.bulkout_urb, g_cdc_ecm_class.hport, g_cdc_ecm_class.bulkout, g_cdc_ecm_tx_buffer, buflen, USB_OSAL_WAITING_FOREVER, NULL, NULL);
return usbh_submit_urb(&g_cdc_ecm_class.bulkout_urb);
}
__WEAK void usbh_cdc_ecm_run(struct usbh_cdc_ecm *cdc_ecm_class)
{
}
__WEAK void usbh_cdc_ecm_stop(struct usbh_cdc_ecm *cdc_ecm_class)
{
}
const struct usbh_class_driver cdc_ecm_class_driver = {
.driver_name = "cdc_ecm",
.connect = usbh_cdc_ecm_connect,
.disconnect = usbh_cdc_ecm_disconnect
};
CLASS_INFO_DEFINE const struct usbh_class_info cdc_ecm_class_info = {
.match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
.class = USB_DEVICE_CLASS_CDC,
.subclass = CDC_ETHERNET_NETWORKING_CONTROL_MODEL,
.protocol = CDC_COMMON_PROTOCOL_NONE,
.id_table = NULL,
.class_driver = &cdc_ecm_class_driver
};

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2024, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_CDC_ECM_H
#define USBH_CDC_ECM_H
#include "usb_cdc.h"
struct usbh_cdc_ecm {
struct usbh_hubport *hport;
struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
struct usb_endpoint_descriptor *intin; /* Interrupt IN endpoint */
struct usbh_urb bulkout_urb; /* Bulk out endpoint */
struct usbh_urb bulkin_urb; /* Bulk IN endpoint */
struct usbh_urb intin_urb; /* Interrupt IN endpoint */
uint8_t ctrl_intf; /* Control interface number */
uint8_t data_intf; /* Data interface number */
uint8_t minor;
uint8_t mac[6];
bool connect_status;
uint16_t max_segment_size;
uint32_t speed[2];
void *user_data;
};
#ifdef __cplusplus
extern "C" {
#endif
int usbh_cdc_ecm_get_connect_status(struct usbh_cdc_ecm *cdc_ecm_class);
void usbh_cdc_ecm_run(struct usbh_cdc_ecm *cdc_ecm_class);
void usbh_cdc_ecm_stop(struct usbh_cdc_ecm *cdc_ecm_class);
uint8_t *usbh_cdc_ecm_get_eth_txbuf(void);
int usbh_cdc_ecm_eth_output(uint32_t buflen);
void usbh_cdc_ecm_eth_input(uint8_t *buf, uint32_t buflen);
void usbh_cdc_ecm_rx_thread(void *argument);
#ifdef __cplusplus
}
#endif
#endif /* USBH_CDC_ECM_H */

View File

@@ -0,0 +1,408 @@
/*
* Copyright (c) 2024, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbh_core.h"
#include "usbh_cdc_ncm.h"
#undef USB_DBG_TAG
#define USB_DBG_TAG "usbh_cdc_ncm"
#include "usb_log.h"
#define DEV_FORMAT "/dev/cdc_ncm"
/* general descriptor field offsets */
#define DESC_bLength 0 /** Length offset */
#define DESC_bDescriptorType 1 /** Descriptor type offset */
#define DESC_bDescriptorSubType 2 /** Descriptor subtype offset */
/* interface descriptor field offsets */
#define INTF_DESC_bInterfaceNumber 2 /** Interface number offset */
#define INTF_DESC_bAlternateSetting 3 /** Alternate setting offset */
#define CONFIG_USBHOST_CDC_NCM_ETH_MAX_SEGSZE 1514U
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ncm_rx_buffer[CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE];
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ncm_tx_buffer[CONFIG_USBHOST_CDC_NCM_ETH_MAX_TX_SIZE];
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ncm_inttx_buffer[16];
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ncm_buf[32];
static struct usbh_cdc_ncm g_cdc_ncm_class;
static int usbh_cdc_ncm_get_ntb_parameters(struct usbh_cdc_ncm *cdc_ncm_class, struct cdc_ncm_ntb_parameters *param)
{
struct usb_setup_packet *setup;
int ret;
if (!cdc_ncm_class || !cdc_ncm_class->hport) {
return -USB_ERR_INVAL;
}
setup = cdc_ncm_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CDC_REQUEST_GET_NTB_PARAMETERS;
setup->wValue = 0;
setup->wIndex = cdc_ncm_class->ctrl_intf;
setup->wLength = 28;
ret = usbh_control_transfer(cdc_ncm_class->hport, setup, g_cdc_ncm_buf);
if (ret < 0) {
return ret;
}
memcpy((uint8_t *)param, g_cdc_ncm_buf, ret - 8);
return 0;
}
static void print_ntb_parameters(struct cdc_ncm_ntb_parameters *param)
{
USB_LOG_RAW("CDC NCM ntb parameters:\r\n");
USB_LOG_RAW("wLength: 0x%02x \r\n", param->wLength);
USB_LOG_RAW("bmNtbFormatsSupported: %s \r\n", param->bmNtbFormatsSupported ? "NTB16" : "NTB32");
USB_LOG_RAW("dwNtbInMaxSize: 0x%04x \r\n", param->dwNtbInMaxSize);
USB_LOG_RAW("wNdbInDivisor: 0x%02x \r\n", param->wNdbInDivisor);
USB_LOG_RAW("wNdbInPayloadRemainder: 0x%02x \r\n", param->wNdbInPayloadRemainder);
USB_LOG_RAW("wNdbInAlignment: 0x%02x \r\n", param->wNdbInAlignment);
USB_LOG_RAW("dwNtbOutMaxSize: 0x%04x \r\n", param->dwNtbOutMaxSize);
USB_LOG_RAW("wNdbOutDivisor: 0x%02x \r\n", param->wNdbOutDivisor);
USB_LOG_RAW("wNdbOutPayloadRemainder: 0x%02x \r\n", param->wNdbOutPayloadRemainder);
USB_LOG_RAW("wNdbOutAlignment: 0x%02x \r\n", param->wNdbOutAlignment);
USB_LOG_RAW("wNtbOutMaxDatagrams: 0x%02x \r\n", param->wNtbOutMaxDatagrams);
}
int usbh_cdc_ncm_get_connect_status(struct usbh_cdc_ncm *cdc_ncm_class)
{
int ret;
usbh_int_urb_fill(&cdc_ncm_class->intin_urb, cdc_ncm_class->hport, cdc_ncm_class->intin, g_cdc_ncm_inttx_buffer, 16, USB_OSAL_WAITING_FOREVER, NULL, NULL);
ret = usbh_submit_urb(&cdc_ncm_class->intin_urb);
if (ret < 0) {
return ret;
}
if (g_cdc_ncm_inttx_buffer[1] == CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION) {
if (g_cdc_ncm_inttx_buffer[2] == CDC_ECM_NET_CONNECTED) {
cdc_ncm_class->connect_status = true;
} else {
cdc_ncm_class->connect_status = false;
}
} else if (g_cdc_ncm_inttx_buffer[1] == CDC_ECM_NOTIFY_CODE_CONNECTION_SPEED_CHANGE) {
memcpy(cdc_ncm_class->speed, &g_cdc_ncm_inttx_buffer[8], 8);
}
return 0;
}
static int usbh_cdc_ncm_connect(struct usbh_hubport *hport, uint8_t intf)
{
struct usb_endpoint_descriptor *ep_desc;
int ret;
uint8_t altsetting = 0;
char mac_buffer[12];
uint8_t *p;
uint8_t cur_iface = 0xff;
uint8_t mac_str_idx = 0xff;
struct usbh_cdc_ncm *cdc_ncm_class = &g_cdc_ncm_class;
memset(cdc_ncm_class, 0, sizeof(struct usbh_cdc_ncm));
cdc_ncm_class->hport = hport;
cdc_ncm_class->ctrl_intf = intf;
cdc_ncm_class->data_intf = intf + 1;
hport->config.intf[intf].priv = cdc_ncm_class;
hport->config.intf[intf + 1].priv = NULL;
p = hport->raw_config_desc;
while (p[DESC_bLength]) {
switch (p[DESC_bDescriptorType]) {
case USB_DESCRIPTOR_TYPE_INTERFACE:
cur_iface = p[INTF_DESC_bInterfaceNumber];
//cur_alt_setting = p[INTF_DESC_bAlternateSetting];
break;
case CDC_CS_INTERFACE:
if ((cur_iface == cdc_ncm_class->ctrl_intf) && p[DESC_bDescriptorSubType] == CDC_FUNC_DESC_ETHERNET_NETWORKING) {
struct cdc_eth_descriptor *desc = (struct cdc_eth_descriptor *)p;
mac_str_idx = desc->iMACAddress;
cdc_ncm_class->max_segment_size = desc->wMaxSegmentSize;
goto get_mac;
}
break;
default:
break;
}
/* skip to next descriptor */
p += p[DESC_bLength];
}
get_mac:
if (mac_str_idx == 0xff) {
USB_LOG_ERR("Do not find cdc ncm mac string\r\n");
return -1;
}
memset(mac_buffer, 0, 12);
ret = usbh_get_string_desc(cdc_ncm_class->hport, mac_str_idx, (uint8_t *)mac_buffer);
if (ret < 0) {
return ret;
}
for (int i = 0, j = 0; i < 12; i += 2, j++) {
char byte_str[3];
byte_str[0] = mac_buffer[i];
byte_str[1] = mac_buffer[i + 1];
byte_str[2] = '\0';
uint32_t byte = strtoul(byte_str, NULL, 16);
cdc_ncm_class->mac[j] = (unsigned char)byte;
}
USB_LOG_INFO("CDC NCM MAC address %02x:%02x:%02x:%02x:%02x:%02x\r\n",
cdc_ncm_class->mac[0],
cdc_ncm_class->mac[1],
cdc_ncm_class->mac[2],
cdc_ncm_class->mac[3],
cdc_ncm_class->mac[4],
cdc_ncm_class->mac[5]);
if (cdc_ncm_class->max_segment_size > CONFIG_USBHOST_CDC_NCM_ETH_MAX_SEGSZE) {
USB_LOG_ERR("CDC NCM Max Segment Size is overflow, default is %u, but now %u\r\n", CONFIG_USBHOST_CDC_NCM_ETH_MAX_SEGSZE, cdc_ncm_class->max_segment_size);
} else {
USB_LOG_INFO("CDC NCM Max Segment Size:%u\r\n", cdc_ncm_class->max_segment_size);
}
usbh_cdc_ncm_get_ntb_parameters(cdc_ncm_class, &cdc_ncm_class->ntb_param);
print_ntb_parameters(&cdc_ncm_class->ntb_param);
/* enable int ep */
ep_desc = &hport->config.intf[intf].altsetting[0].ep[0].ep_desc;
USBH_EP_INIT(cdc_ncm_class->intin, ep_desc);
if (hport->config.intf[intf + 1].altsetting_num > 1) {
altsetting = hport->config.intf[intf + 1].altsetting_num - 1;
for (uint8_t i = 0; i < hport->config.intf[intf + 1].altsetting[altsetting].intf_desc.bNumEndpoints; i++) {
ep_desc = &hport->config.intf[intf + 1].altsetting[altsetting].ep[i].ep_desc;
if (ep_desc->bEndpointAddress & 0x80) {
USBH_EP_INIT(cdc_ncm_class->bulkin, ep_desc);
} else {
USBH_EP_INIT(cdc_ncm_class->bulkout, ep_desc);
}
}
USB_LOG_INFO("Select cdc ncm altsetting: %d\r\n", altsetting);
usbh_set_interface(cdc_ncm_class->hport, cdc_ncm_class->data_intf, altsetting);
} else {
for (uint8_t i = 0; i < hport->config.intf[intf + 1].altsetting[0].intf_desc.bNumEndpoints; i++) {
ep_desc = &hport->config.intf[intf + 1].altsetting[0].ep[i].ep_desc;
if (ep_desc->bEndpointAddress & 0x80) {
USBH_EP_INIT(cdc_ncm_class->bulkin, ep_desc);
} else {
USBH_EP_INIT(cdc_ncm_class->bulkout, ep_desc);
}
}
}
strncpy(hport->config.intf[intf].devname, DEV_FORMAT, CONFIG_USBHOST_DEV_NAMELEN);
USB_LOG_INFO("Register CDC NCM Class:%s\r\n", hport->config.intf[intf].devname);
usbh_cdc_ncm_run(cdc_ncm_class);
return ret;
}
static int usbh_cdc_ncm_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
int ret = 0;
struct usbh_cdc_ncm *cdc_ncm_class = (struct usbh_cdc_ncm *)hport->config.intf[intf].priv;
if (cdc_ncm_class) {
if (cdc_ncm_class->bulkin) {
usbh_kill_urb(&cdc_ncm_class->bulkin_urb);
}
if (cdc_ncm_class->bulkout) {
usbh_kill_urb(&cdc_ncm_class->bulkout_urb);
}
if (cdc_ncm_class->intin) {
usbh_kill_urb(&cdc_ncm_class->intin_urb);
}
if (hport->config.intf[intf].devname[0] != '\0') {
USB_LOG_INFO("Unregister CDC NCM Class:%s\r\n", hport->config.intf[intf].devname);
usbh_cdc_ncm_stop(cdc_ncm_class);
}
memset(cdc_ncm_class, 0, sizeof(struct usbh_cdc_ncm));
}
return ret;
}
void usbh_cdc_ncm_rx_thread(void *argument)
{
uint32_t g_cdc_ncm_rx_length;
int ret;
#if CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE <= (16 * 1024)
uint32_t transfer_size = CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE;
#else
uint32_t transfer_size = (16 * 1024);
#endif
USB_LOG_INFO("Create cdc ncm rx thread\r\n");
// clang-format off
find_class:
// clang-format on
g_cdc_ncm_class.connect_status = false;
if (usbh_find_class_instance("/dev/cdc_ncm") == NULL) {
goto delete;
}
while (g_cdc_ncm_class.connect_status == false) {
ret = usbh_cdc_ncm_get_connect_status(&g_cdc_ncm_class);
if (ret < 0) {
usb_osal_msleep(100);
goto find_class;
}
}
g_cdc_ncm_rx_length = 0;
while (1) {
usbh_bulk_urb_fill(&g_cdc_ncm_class.bulkin_urb, g_cdc_ncm_class.hport, g_cdc_ncm_class.bulkin, &g_cdc_ncm_rx_buffer[g_cdc_ncm_rx_length], transfer_size, USB_OSAL_WAITING_FOREVER, NULL, NULL);
ret = usbh_submit_urb(&g_cdc_ncm_class.bulkin_urb);
if (ret < 0) {
goto find_class;
}
g_cdc_ncm_rx_length += g_cdc_ncm_class.bulkin_urb.actual_length;
/* A transfer is complete because last packet is a short packet.
* Short packet is not zero, match g_cdc_ncm_rx_length % USB_GET_MAXPACKETSIZE(g_cdc_ncm_class.bulkin->wMaxPacketSize).
* Short packet is zero, check if g_cdc_ncm_class.bulkin_urb.actual_length < transfer_size, for example transfer is complete with size is 1024 < 2048.
*/
if ((g_cdc_ncm_rx_length % USB_GET_MAXPACKETSIZE(g_cdc_ncm_class.bulkin->wMaxPacketSize)) ||
(g_cdc_ncm_class.bulkin_urb.actual_length < transfer_size)) {
USB_LOG_DBG("rxlen:%d\r\n", g_cdc_ncm_rx_length);
struct cdc_ncm_nth16 *nth16 = (struct cdc_ncm_nth16 *)&g_cdc_ncm_rx_buffer[0];
if ((nth16->dwSignature != CDC_NCM_NTH16_SIGNATURE) ||
(nth16->wHeaderLength != 12) ||
(nth16->wBlockLength != g_cdc_ncm_rx_length)) {
USB_LOG_ERR("invalid rx nth16\r\n");
g_cdc_ncm_rx_length = 0;
continue;
}
struct cdc_ncm_ndp16 *ndp16 = (struct cdc_ncm_ndp16 *)&g_cdc_ncm_rx_buffer[nth16->wNdpIndex];
if ((ndp16->dwSignature != CDC_NCM_NDP16_SIGNATURE_NCM0) && (ndp16->dwSignature != CDC_NCM_NDP16_SIGNATURE_NCM1)) {
USB_LOG_ERR("invalid rx ndp16\r\n");
g_cdc_ncm_rx_length = 0;
continue;
}
uint16_t datagram_num = (ndp16->wLength - 8) / 4;
USB_LOG_DBG("datagram num:%02x\r\n", datagram_num);
for (uint16_t i = 0; i < datagram_num; i++) {
struct cdc_ncm_ndp16_datagram *ndp16_datagram = (struct cdc_ncm_ndp16_datagram *)&g_cdc_ncm_rx_buffer[nth16->wNdpIndex + 8 + 4 * i];
if (ndp16_datagram->wDatagramIndex && ndp16_datagram->wDatagramLength) {
USB_LOG_DBG("ndp16_datagram index:%02x, length:%02x\r\n", ndp16_datagram->wDatagramIndex, ndp16_datagram->wDatagramLength);
uint8_t *buf = (uint8_t *)&g_cdc_ncm_rx_buffer[ndp16_datagram->wDatagramIndex];
usbh_cdc_ncm_eth_input(buf, ndp16_datagram->wDatagramLength);
}
}
g_cdc_ncm_rx_length = 0;
} else {
#if CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE <= (16 * 1024)
if (g_cdc_ncm_rx_length == CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE) {
#else
if ((g_cdc_ncm_rx_length + (16 * 1024)) > CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE) {
#endif
USB_LOG_ERR("Rx packet is overflow, please ruduce tcp window size or increase CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE\r\n");
while (1) {
}
}
}
}
// clang-format off
delete:
USB_LOG_INFO("Delete cdc ncm rx thread\r\n");
usb_osal_thread_delete(NULL);
// clang-format on
}
uint8_t *usbh_cdc_ncm_get_eth_txbuf(void)
{
return &g_cdc_ncm_tx_buffer[16];
}
int usbh_cdc_ncm_eth_output(uint32_t buflen)
{
struct cdc_ncm_ndp16_datagram *ndp16_datagram;
if (g_cdc_ncm_class.connect_status == false) {
return -USB_ERR_NOTCONN;
}
struct cdc_ncm_nth16 *nth16 = (struct cdc_ncm_nth16 *)&g_cdc_ncm_tx_buffer[0];
nth16->dwSignature = CDC_NCM_NTH16_SIGNATURE;
nth16->wHeaderLength = 12;
nth16->wSequence = g_cdc_ncm_class.bulkout_sequence++;
nth16->wBlockLength = 16 + 16 + USB_ALIGN_UP(buflen, 4);
nth16->wNdpIndex = 16 + USB_ALIGN_UP(buflen, 4);
struct cdc_ncm_ndp16 *ndp16 = (struct cdc_ncm_ndp16 *)&g_cdc_ncm_tx_buffer[nth16->wNdpIndex];
ndp16->dwSignature = CDC_NCM_NDP16_SIGNATURE_NCM0;
ndp16->wLength = 16;
ndp16->wNextNdpIndex = 0;
ndp16_datagram = (struct cdc_ncm_ndp16_datagram *)&g_cdc_ncm_tx_buffer[nth16->wNdpIndex + 8 + 4 * 0];
ndp16_datagram->wDatagramIndex = 16;
ndp16_datagram->wDatagramLength = buflen;
ndp16_datagram = (struct cdc_ncm_ndp16_datagram *)&g_cdc_ncm_tx_buffer[nth16->wNdpIndex + 8 + 4 * 1];
ndp16_datagram->wDatagramIndex = 0;
ndp16_datagram->wDatagramLength = 0;
USB_LOG_DBG("txlen:%d\r\n", nth16->wBlockLength);
usbh_bulk_urb_fill(&g_cdc_ncm_class.bulkout_urb, g_cdc_ncm_class.hport, g_cdc_ncm_class.bulkout, g_cdc_ncm_tx_buffer, nth16->wBlockLength, USB_OSAL_WAITING_FOREVER, NULL, NULL);
return usbh_submit_urb(&g_cdc_ncm_class.bulkout_urb);
}
__WEAK void usbh_cdc_ncm_run(struct usbh_cdc_ncm *cdc_ncm_class)
{
}
__WEAK void usbh_cdc_ncm_stop(struct usbh_cdc_ncm *cdc_ncm_class)
{
}
const struct usbh_class_driver cdc_ncm_class_driver = {
.driver_name = "cdc_ncm",
.connect = usbh_cdc_ncm_connect,
.disconnect = usbh_cdc_ncm_disconnect
};
CLASS_INFO_DEFINE const struct usbh_class_info cdc_ncm_class_info = {
.match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
.class = USB_DEVICE_CLASS_CDC,
.subclass = CDC_NETWORK_CONTROL_MODEL,
.protocol = CDC_COMMON_PROTOCOL_NONE,
.id_table = NULL,
.class_driver = &cdc_ncm_class_driver
};

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) 2024, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_CDC_NCM_H
#define USBH_CDC_NCM_H
#include "usb_cdc.h"
struct usbh_cdc_ncm {
struct usbh_hubport *hport;
struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
struct usb_endpoint_descriptor *intin; /* Interrupt IN endpoint */
struct usbh_urb bulkout_urb; /* Bulk out endpoint */
struct usbh_urb bulkin_urb; /* Bulk IN endpoint */
struct usbh_urb intin_urb; /* Interrupt IN endpoint */
uint8_t ctrl_intf; /* Control interface number */
uint8_t data_intf; /* Data interface number */
uint8_t minor;
struct cdc_ncm_ntb_parameters ntb_param;
uint16_t bulkin_sequence;
uint16_t bulkout_sequence;
uint8_t mac[6];
bool connect_status;
uint16_t max_segment_size;
uint32_t speed[2];
void *user_data;
};
#ifdef __cplusplus
extern "C" {
#endif
int usbh_cdc_ncm_get_connect_status(struct usbh_cdc_ncm *cdc_ncm_class);
void usbh_cdc_ncm_run(struct usbh_cdc_ncm *cdc_ncm_class);
void usbh_cdc_ncm_stop(struct usbh_cdc_ncm *cdc_ncm_class);
uint8_t *usbh_cdc_ncm_get_eth_txbuf(void);
int usbh_cdc_ncm_eth_output(uint32_t buflen);
void usbh_cdc_ncm_eth_input(uint8_t *buf, uint32_t buflen);
void usbh_cdc_ncm_rx_thread(void *argument);
#ifdef __cplusplus
}
#endif
#endif /* USBH_CDC_NCM_H */

View File

@@ -0,0 +1,137 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USB_DFU_H
#define USB_DFU_H
/**\addtogroup USB_MODULE_DFU USB DFU class
* \brief This module contains USB Device Firmware Upgrade class definitions.
* \details This module based on
* + [USB Device Firmware Upgrade Specification, Revision 1.1]
* (https://www.usb.org/sites/default/files/DFU_1.1.pdf)
* @{ */
/** DFU Specification release */
#define DFU_VERSION 0x0110
/** DFU Class Subclass */
#define DFU_SUBCLASS_DFU 0x01
/** DFU Class runtime Protocol */
#define DFU_PROTOCOL_RUNTIME 0x01
/** DFU Class DFU mode Protocol */
#define DFU_PROTOCOL_MODE 0x02
/**
* @brief DFU Class Specific Requests
*/
#define DFU_REQUEST_DETACH 0x00
#define DFU_REQUEST_DNLOAD 0x01
#define DFU_REQUEST_UPLOAD 0x02
#define DFU_REQUEST_GETSTATUS 0x03
#define DFU_REQUEST_CLRSTATUS 0x04
#define DFU_REQUEST_GETSTATE 0x05
#define DFU_REQUEST_ABORT 0x06
/** DFU FUNCTIONAL descriptor type */
#define DFU_FUNC_DESC 0x21
/** DFU attributes DFU Functional Descriptor */
#define DFU_ATTR_WILL_DETACH 0x08
#define DFU_ATTR_MANIFESTATION_TOLERANT 0x04
#define DFU_ATTR_CAN_UPLOAD 0x02
#define DFU_ATTR_CAN_DNLOAD 0x01
/** bStatus values for the DFU_GETSTATUS response */
#define DFU_STATUS_OK 0x00U
#define DFU_STATUS_ERR_TARGET 0x01U
#define DFU_STATUS_ERR_FILE 0x02U
#define DFU_STATUS_ERR_WRITE 0x03U
#define DFU_STATUS_ERR_ERASE 0x04U
#define DFU_STATUS_ERR_CHECK_ERASED 0x05U
#define DFU_STATUS_ERR_PROG 0x06U
#define DFU_STATUS_ERR_VERIFY 0x07U
#define DFU_STATUS_ERR_ADDRESS 0x08U
#define DFU_STATUS_ERR_NOTDONE 0x09U
#define DFU_STATUS_ERR_FIRMWARE 0x0AU
#define DFU_STATUS_ERR_VENDOR 0x0BU
#define DFU_STATUS_ERR_USB 0x0CU
#define DFU_STATUS_ERR_POR 0x0DU
#define DFU_STATUS_ERR_UNKNOWN 0x0EU
#define DFU_STATUS_ERR_STALLEDPKT 0x0FU
/** bState values for the DFU_GETSTATUS response */
#define DFU_STATE_APP_IDLE 0U
#define DFU_STATE_APP_DETACH 1U
#define DFU_STATE_DFU_IDLE 2U
#define DFU_STATE_DFU_DNLOAD_SYNC 3U
#define DFU_STATE_DFU_DNLOAD_BUSY 4U
#define DFU_STATE_DFU_DNLOAD_IDLE 5U
#define DFU_STATE_DFU_MANIFEST_SYNC 6U
#define DFU_STATE_DFU_MANIFEST 7U
#define DFU_STATE_DFU_MANIFEST_WAIT_RESET 8U
#define DFU_STATE_DFU_UPLOAD_IDLE 9U
#define DFU_STATE_DFU_ERROR 10U
/** DFU Manifestation State */
#define DFU_MANIFEST_COMPLETE 0U
#define DFU_MANIFEST_IN_PROGRESS 1U
/** Special Commands with Download Request */
#define DFU_CMD_GETCOMMANDS 0U
#define DFU_CMD_SETADDRESSPOINTER 0x21U
#define DFU_CMD_ERASE 0x41U
#define DFU_MEDIA_ERASE 0x00U
#define DFU_MEDIA_PROGRAM 0x01U
/** Other defines */
/* Bit Detach capable = bit 3 in bmAttributes field */
#define DFU_DETACH_MASK (1U << 3)
#define DFU_MANIFEST_MASK (1U << 2)
/** Run-Time Functional Descriptor */
struct dfu_runtime_descriptor {
uint8_t bLength; /**<\brief Descriptor length in bytes.*/
uint8_t bDescriptorType; /**<\brief DFU functional descriptor type.*/
uint8_t bmAttributes; /**<\brief USB DFU capabilities \ref USB_DFU_CAPAB*/
uint16_t wDetachTimeout; /**<\brief USB DFU detach timeout in ms.*/
uint16_t wTransferSize; /**<\brief USB DFU maximum transfer block size in bytes.*/
uint16_t bcdDFUVersion; /**<\brief USB DFU version \ref VERSION_BCD utility macro.*/
} __PACKED;
/**\brief Payload packet to response in DFU_GETSTATUS request */
struct dfu_info {
uint8_t bStatus; /**<\brief An indication of the status resulting from the
* execution of the most recent request.*/
uint8_t bPollTimeout; /**<\brief Minimum time (LSB) in ms, that the host should wait
* before sending a subsequent DFU_GETSTATUS request.*/
uint16_t wPollTimeout; /**<\brief Minimum time (MSB) in ms, that the host should wait
* before sending a subsequent DFU_GETSTATUS request.*/
uint8_t bState; /**<\brief An indication of the state that the device is going
* to enter immediately following transmission of this response.*/
uint8_t iString; /**<\brief Index of the status string descriptor.*/
};
// clang-format off
#define DFU_DESCRIPTOR_INIT() \
0x09, /* bLength */ \
USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
0x00, /* bInterfaceNumber */ \
0x00, /* bAlternateSetting */ \
0x00, /* bNumEndpoints Default Control Pipe only */ \
USB_DEVICE_CLASS_APP_SPECIFIC, /* bInterfaceClass */ \
0x01, /* bInterfaceSubClass Device Firmware Upgrade */ \
0x02, /* bInterfaceProtocol DFU mode */ \
0x04, /* iInterface */ /*!< Device Firmware Update Functional Descriptor */ \
0x09, /* bLength */ \
0x21, /* DFU Functional Descriptor */ \
0x0B, /* bmAttributes */ \
WBVAL(0x00ff), /* wDetachTimeOut */ \
WBVAL(USBD_DFU_XFER_SIZE), /* wTransferSize */ \
WBVAL(0x011a) /* bcdDFUVersion */
// clang-format on
#endif /* USB_DFU_H */

View File

@@ -0,0 +1,505 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbd_core.h"
#include "usbd_dfu.h"
/** Modify the following three parameters according to different platforms */
#ifndef USBD_DFU_XFER_SIZE
#define USBD_DFU_XFER_SIZE 1024
#endif
#ifndef USBD_DFU_APP_DEFAULT_ADD
#define USBD_DFU_APP_DEFAULT_ADD 0x8004000
#endif
#ifndef FLASH_PROGRAM_TIME
#define FLASH_PROGRAM_TIME 50
#endif
#ifndef FLASH_ERASE_TIME
#define FLASH_ERASE_TIME 50
#endif
struct usbd_dfu_priv {
struct dfu_info info;
union {
uint32_t d32[USBD_DFU_XFER_SIZE / 4U];
uint8_t d8[USBD_DFU_XFER_SIZE];
} buffer;
uint32_t wblock_num;
uint32_t wlength;
uint32_t data_ptr;
uint32_t alt_setting;
uint8_t dev_status[6];
uint8_t ReservedForAlign[2];
uint8_t dev_state;
uint8_t manif_state;
uint8_t firmwar_flag;
} g_usbd_dfu;
static void dfu_reset(void)
{
memset(&g_usbd_dfu, 0, sizeof(g_usbd_dfu));
g_usbd_dfu.alt_setting = 0U;
g_usbd_dfu.data_ptr = USBD_DFU_APP_DEFAULT_ADD;
g_usbd_dfu.wblock_num = 0U;
g_usbd_dfu.wlength = 0U;
g_usbd_dfu.manif_state = DFU_MANIFEST_COMPLETE;
g_usbd_dfu.dev_state = DFU_STATE_DFU_IDLE;
g_usbd_dfu.dev_status[0] = DFU_STATUS_OK;
g_usbd_dfu.dev_status[1] = 0U;
g_usbd_dfu.dev_status[2] = 0U;
g_usbd_dfu.dev_status[3] = 0U;
g_usbd_dfu.dev_status[4] = DFU_STATE_DFU_IDLE;
g_usbd_dfu.dev_status[5] = 0U;
}
static uint16_t dfu_getstatus(uint32_t add, uint8_t cmd, uint8_t *buffer)
{
switch (cmd) {
case DFU_MEDIA_PROGRAM:
buffer[1] = (uint8_t)FLASH_PROGRAM_TIME;
buffer[2] = (uint8_t)(FLASH_PROGRAM_TIME << 8);
buffer[3] = 0;
break;
case DFU_MEDIA_ERASE:
buffer[1] = (uint8_t)FLASH_ERASE_TIME;
buffer[2] = (uint8_t)(FLASH_ERASE_TIME << 8);
buffer[3] = 0;
default:
break;
}
return (0);
}
static void dfu_request_detach(void)
{
if ((g_usbd_dfu.dev_state == DFU_STATE_DFU_IDLE) ||
(g_usbd_dfu.dev_state == DFU_STATE_DFU_DNLOAD_SYNC) ||
(g_usbd_dfu.dev_state == DFU_STATE_DFU_DNLOAD_IDLE) ||
(g_usbd_dfu.dev_state == DFU_STATE_DFU_MANIFEST_SYNC) ||
(g_usbd_dfu.dev_state == DFU_STATE_DFU_UPLOAD_IDLE)) {
/* Update the state machine */
g_usbd_dfu.dev_state = DFU_STATE_DFU_IDLE;
g_usbd_dfu.dev_status[0] = DFU_STATUS_OK;
g_usbd_dfu.dev_status[1] = 0U;
g_usbd_dfu.dev_status[2] = 0U;
g_usbd_dfu.dev_status[3] = 0U; /*bwPollTimeout=0ms*/
g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
g_usbd_dfu.dev_status[5] = 0U; /*iString*/
g_usbd_dfu.wblock_num = 0U;
g_usbd_dfu.wlength = 0U;
}
}
static void dfu_request_upload(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
{
struct usb_setup_packet *req = setup;
uint32_t addr;
uint8_t *phaddr;
/* Data setup request */
if (req->wLength > 0U) {
if ((g_usbd_dfu.dev_state == DFU_STATE_DFU_IDLE) || (g_usbd_dfu.dev_state == DFU_STATE_DFU_UPLOAD_IDLE)) {
/* Update the global length and block number */
g_usbd_dfu.wblock_num = req->wValue;
g_usbd_dfu.wlength = MIN(req->wLength, USBD_DFU_XFER_SIZE);
/* DFU Get Command */
if (g_usbd_dfu.wblock_num == 0U) {
/* Update the state machine */
g_usbd_dfu.dev_state = (g_usbd_dfu.wlength > 3U) ? DFU_STATE_DFU_IDLE : DFU_STATE_DFU_UPLOAD_IDLE;
g_usbd_dfu.dev_status[1] = 0U;
g_usbd_dfu.dev_status[2] = 0U;
g_usbd_dfu.dev_status[3] = 0U;
g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
/* Store the values of all supported commands */
g_usbd_dfu.buffer.d8[0] = DFU_CMD_GETCOMMANDS;
g_usbd_dfu.buffer.d8[1] = DFU_CMD_SETADDRESSPOINTER;
g_usbd_dfu.buffer.d8[2] = DFU_CMD_ERASE;
/* Send the status data over EP0 */
memcpy(*data, g_usbd_dfu.buffer.d8, 3);
*len = 3;
} else if (g_usbd_dfu.wblock_num > 1U) {
g_usbd_dfu.dev_state = DFU_STATE_DFU_UPLOAD_IDLE;
g_usbd_dfu.dev_status[1] = 0U;
g_usbd_dfu.dev_status[2] = 0U;
g_usbd_dfu.dev_status[3] = 0U;
g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
addr = ((g_usbd_dfu.wblock_num - 2U) * USBD_DFU_XFER_SIZE) + g_usbd_dfu.data_ptr;
/* Return the physical address where data are stored */
phaddr = dfu_read_flash((uint8_t *)addr, g_usbd_dfu.buffer.d8, g_usbd_dfu.wlength);
/* Send the status data over EP0 */
memcpy(*data, g_usbd_dfu.buffer.d8, g_usbd_dfu.wlength);
*len = g_usbd_dfu.wlength;
} else /* unsupported g_usbd_dfu.wblock_num */
{
g_usbd_dfu.dev_state = DFU_STATUS_ERR_STALLEDPKT;
g_usbd_dfu.dev_status[1] = 0U;
g_usbd_dfu.dev_status[2] = 0U;
g_usbd_dfu.dev_status[3] = 0U;
g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
/* Call the error management function (command will be NAKed */
USB_LOG_ERR("Dfu_request_upload unsupported g_usbd_dfu.wblock_num\r\n");
}
}
/* Unsupported state */
else {
g_usbd_dfu.wlength = 0U;
g_usbd_dfu.wblock_num = 0U;
/* Call the error management function (command will be NAKed */
USB_LOG_ERR("Dfu_request_upload unsupported state\r\n");
}
}
/* No Data setup request */
else {
g_usbd_dfu.dev_state = DFU_STATE_DFU_IDLE;
g_usbd_dfu.dev_status[1] = 0U;
g_usbd_dfu.dev_status[2] = 0U;
g_usbd_dfu.dev_status[3] = 0U;
g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
}
}
static void dfu_request_dnload(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
{
/* Data setup request */
struct usb_setup_packet *req = setup;
if (req->wLength > 0U) {
if ((g_usbd_dfu.dev_state == DFU_STATE_DFU_IDLE) || (g_usbd_dfu.dev_state == DFU_STATE_DFU_DNLOAD_IDLE)) {
/* Update the global length and block number */
g_usbd_dfu.wblock_num = req->wValue;
g_usbd_dfu.wlength = MIN(req->wLength, USBD_DFU_XFER_SIZE);
/* Update the state machine */
g_usbd_dfu.dev_state = DFU_STATE_DFU_DNLOAD_SYNC;
g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
/*!< Data has received complete */
memcpy((uint8_t *)g_usbd_dfu.buffer.d8, (uint8_t *)*data, g_usbd_dfu.wlength);
/*!< Set flag = 1 Write the firmware to the flash in the next dfu_request_getstatus */
g_usbd_dfu.firmwar_flag = 1;
}
/* Unsupported state */
else {
USB_LOG_ERR("Dfu_request_dnload unsupported state\r\n");
}
}
/* 0 Data DNLOAD request */
else {
/* End of DNLOAD operation*/
if ((g_usbd_dfu.dev_state == DFU_STATE_DFU_DNLOAD_IDLE) || (g_usbd_dfu.dev_state == DFU_STATE_DFU_IDLE)) {
g_usbd_dfu.manif_state = DFU_MANIFEST_IN_PROGRESS;
g_usbd_dfu.dev_state = DFU_STATE_DFU_MANIFEST_SYNC;
g_usbd_dfu.dev_status[1] = 0U;
g_usbd_dfu.dev_status[2] = 0U;
g_usbd_dfu.dev_status[3] = 0U;
g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
} else {
/* Call the error management function (command will be NAKed */
USB_LOG_ERR("Dfu_request_dnload End of DNLOAD operation but dev_state %02x \r\n", g_usbd_dfu.dev_state);
}
}
}
static int8_t dfu_getstatus_special_handler(void)
{
uint32_t addr;
if (g_usbd_dfu.dev_state == DFU_STATE_DFU_DNLOAD_BUSY) {
/* Decode the Special Command */
if (g_usbd_dfu.wblock_num == 0U) {
if (g_usbd_dfu.wlength == 1U) {
if (g_usbd_dfu.buffer.d8[0] == DFU_CMD_GETCOMMANDS) {
/* Nothing to do */
}
} else if (g_usbd_dfu.wlength == 5U) {
if (g_usbd_dfu.buffer.d8[0] == DFU_CMD_SETADDRESSPOINTER) {
g_usbd_dfu.data_ptr = g_usbd_dfu.buffer.d8[1];
g_usbd_dfu.data_ptr += (uint32_t)g_usbd_dfu.buffer.d8[2] << 8;
g_usbd_dfu.data_ptr += (uint32_t)g_usbd_dfu.buffer.d8[3] << 16;
g_usbd_dfu.data_ptr += (uint32_t)g_usbd_dfu.buffer.d8[4] << 24;
} else if (g_usbd_dfu.buffer.d8[0] == DFU_CMD_ERASE) {
g_usbd_dfu.data_ptr = g_usbd_dfu.buffer.d8[1];
g_usbd_dfu.data_ptr += (uint32_t)g_usbd_dfu.buffer.d8[2] << 8;
g_usbd_dfu.data_ptr += (uint32_t)g_usbd_dfu.buffer.d8[3] << 16;
g_usbd_dfu.data_ptr += (uint32_t)g_usbd_dfu.buffer.d8[4] << 24;
USB_LOG_DBG("Erase start add %08x \r\n", g_usbd_dfu.data_ptr);
/*!< Erase */
dfu_erase_flash(g_usbd_dfu.data_ptr);
} else {
return -1;
}
} else {
/* Reset the global length and block number */
g_usbd_dfu.wlength = 0U;
g_usbd_dfu.wblock_num = 0U;
/* Call the error management function (command will be NAKed) */
USB_LOG_ERR("Reset the global length and block number\r\n");
}
}
/* Regular Download Command */
else {
if (g_usbd_dfu.wblock_num > 1U) {
/* Decode the required address */
addr = ((g_usbd_dfu.wblock_num - 2U) * USBD_DFU_XFER_SIZE) + g_usbd_dfu.data_ptr;
/* Perform the write operation */
/* Write flash */
USB_LOG_DBG("Write start add %08x length %d\r\n", addr, g_usbd_dfu.wlength);
dfu_write_flash(g_usbd_dfu.buffer.d8, (uint8_t *)addr, g_usbd_dfu.wlength);
}
}
/* Reset the global length and block number */
g_usbd_dfu.wlength = 0U;
g_usbd_dfu.wblock_num = 0U;
/* Update the state machine */
g_usbd_dfu.dev_state = DFU_STATE_DFU_DNLOAD_SYNC;
g_usbd_dfu.dev_status[1] = 0U;
g_usbd_dfu.dev_status[2] = 0U;
g_usbd_dfu.dev_status[3] = 0U;
g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
}
return 0;
}
static void dfu_request_getstatus(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
{
/*!< Determine whether to leave DFU mode */
if (g_usbd_dfu.manif_state == DFU_MANIFEST_IN_PROGRESS &&
g_usbd_dfu.dev_state == DFU_STATE_DFU_MANIFEST_SYNC &&
g_usbd_dfu.dev_status[1] == 0U &&
g_usbd_dfu.dev_status[2] == 0U &&
g_usbd_dfu.dev_status[3] == 0U &&
g_usbd_dfu.dev_status[4] == g_usbd_dfu.dev_state) {
g_usbd_dfu.manif_state = DFU_MANIFEST_COMPLETE;
if ((0x0B & DFU_MANIFEST_MASK) != 0U) {
g_usbd_dfu.dev_state = DFU_STATE_DFU_MANIFEST_SYNC;
g_usbd_dfu.dev_status[1] = 0U;
g_usbd_dfu.dev_status[2] = 0U;
g_usbd_dfu.dev_status[3] = 0U;
g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
return;
} else {
g_usbd_dfu.dev_state = DFU_STATE_DFU_MANIFEST_WAIT_RESET;
g_usbd_dfu.dev_status[1] = 0U;
g_usbd_dfu.dev_status[2] = 0U;
g_usbd_dfu.dev_status[3] = 0U;
g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
/* Generate system reset to allow jumping to the user code */
dfu_leave();
}
}
switch (g_usbd_dfu.dev_state) {
case DFU_STATE_DFU_DNLOAD_SYNC:
if (g_usbd_dfu.wlength != 0U) {
g_usbd_dfu.dev_state = DFU_STATE_DFU_DNLOAD_BUSY;
g_usbd_dfu.dev_status[1] = 0U;
g_usbd_dfu.dev_status[2] = 0U;
g_usbd_dfu.dev_status[3] = 0U;
g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
if ((g_usbd_dfu.wblock_num == 0U) && (g_usbd_dfu.buffer.d8[0] == DFU_CMD_ERASE)) {
dfu_getstatus(g_usbd_dfu.data_ptr, DFU_MEDIA_ERASE, g_usbd_dfu.dev_status);
} else {
dfu_getstatus(g_usbd_dfu.data_ptr, DFU_MEDIA_PROGRAM, g_usbd_dfu.dev_status);
}
} else /* (g_usbd_dfu.wlength==0)*/
{
g_usbd_dfu.dev_state = DFU_STATE_DFU_DNLOAD_IDLE;
g_usbd_dfu.dev_status[1] = 0U;
g_usbd_dfu.dev_status[2] = 0U;
g_usbd_dfu.dev_status[3] = 0U;
g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
}
break;
case DFU_STATE_DFU_MANIFEST_SYNC:
if (g_usbd_dfu.manif_state == DFU_MANIFEST_IN_PROGRESS) {
g_usbd_dfu.dev_state = DFU_STATE_DFU_MANIFEST;
g_usbd_dfu.dev_status[1] = 1U; /*bwPollTimeout = 1ms*/
g_usbd_dfu.dev_status[2] = 0U;
g_usbd_dfu.dev_status[3] = 0U;
g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
} else {
if ((g_usbd_dfu.manif_state == DFU_MANIFEST_COMPLETE) &&
((0x0B & DFU_MANIFEST_MASK) != 0U)) {
g_usbd_dfu.dev_state = DFU_STATE_DFU_IDLE;
g_usbd_dfu.dev_status[1] = 0U;
g_usbd_dfu.dev_status[2] = 0U;
g_usbd_dfu.dev_status[3] = 0U;
g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
}
}
break;
default:
break;
}
/* Send the status data over EP0 */
memcpy(*data, g_usbd_dfu.dev_status, 6);
*len = 6;
if (g_usbd_dfu.firmwar_flag == 1) {
if (dfu_getstatus_special_handler() != 0) {
USB_LOG_ERR("dfu_getstatus_special_handler error \r\n");
}
g_usbd_dfu.firmwar_flag = 0;
}
}
static void dfu_request_clrstatus(void)
{
if (g_usbd_dfu.dev_state == DFU_STATE_DFU_ERROR) {
g_usbd_dfu.dev_state = DFU_STATE_DFU_IDLE;
g_usbd_dfu.dev_status[0] = DFU_STATUS_OK; /* bStatus */
g_usbd_dfu.dev_status[1] = 0U;
g_usbd_dfu.dev_status[2] = 0U;
g_usbd_dfu.dev_status[3] = 0U; /* bwPollTimeout=0ms */
g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state; /* bState */
g_usbd_dfu.dev_status[5] = 0U; /* iString */
} else {
/* State Error */
g_usbd_dfu.dev_state = DFU_STATE_DFU_ERROR;
g_usbd_dfu.dev_status[0] = DFU_STATUS_ERR_UNKNOWN; /* bStatus */
g_usbd_dfu.dev_status[1] = 0U;
g_usbd_dfu.dev_status[2] = 0U;
g_usbd_dfu.dev_status[3] = 0U; /* bwPollTimeout=0ms */
g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state; /* bState */
g_usbd_dfu.dev_status[5] = 0U; /* iString */
}
}
static void dfu_request_getstate(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
{
/* Return the current state of the DFU interface */
(*data)[0] = g_usbd_dfu.dev_state;
*len = 1;
}
void dfu_request_abort(void)
{
if ((g_usbd_dfu.dev_state == DFU_STATE_DFU_IDLE) ||
(g_usbd_dfu.dev_state == DFU_STATE_DFU_DNLOAD_SYNC) ||
(g_usbd_dfu.dev_state == DFU_STATE_DFU_DNLOAD_IDLE) ||
(g_usbd_dfu.dev_state == DFU_STATE_DFU_MANIFEST_SYNC) ||
(g_usbd_dfu.dev_state == DFU_STATE_DFU_UPLOAD_IDLE)) {
g_usbd_dfu.dev_state = DFU_STATE_DFU_IDLE;
g_usbd_dfu.dev_status[0] = DFU_STATUS_OK;
g_usbd_dfu.dev_status[1] = 0U;
g_usbd_dfu.dev_status[2] = 0U;
g_usbd_dfu.dev_status[3] = 0U; /* bwPollTimeout=0ms */
g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
g_usbd_dfu.dev_status[5] = 0U; /* iString */
g_usbd_dfu.wblock_num = 0U;
g_usbd_dfu.wlength = 0U;
}
}
static int dfu_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
{
USB_LOG_DBG("DFU Class request: "
"bRequest 0x%02x\r\n",
setup->bRequest);
switch (setup->bRequest) {
case DFU_REQUEST_DETACH:
dfu_request_detach();
break;
case DFU_REQUEST_DNLOAD:
dfu_request_dnload(setup, data, len);
break;
case DFU_REQUEST_UPLOAD:
dfu_request_upload(setup, data, len);
break;
case DFU_REQUEST_GETSTATUS:
dfu_request_getstatus(setup, data, len);
break;
case DFU_REQUEST_CLRSTATUS:
dfu_request_clrstatus();
break;
case DFU_REQUEST_GETSTATE:
dfu_request_getstate(setup, data, len);
break;
case DFU_REQUEST_ABORT:
dfu_request_abort();
break;
default:
USB_LOG_WRN("Unhandled DFU Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
return 0;
}
static void dfu_notify_handler(uint8_t busid, uint8_t event, void *arg)
{
switch (event) {
case USBD_EVENT_RESET:
dfu_reset();
break;
default:
break;
}
}
struct usbd_interface *usbd_dfu_init_intf(struct usbd_interface *intf)
{
intf->class_interface_handler = dfu_class_interface_request_handler;
intf->class_endpoint_handler = NULL;
intf->vendor_handler = NULL;
intf->notify_handler = dfu_notify_handler;
return intf;
}
__WEAK uint8_t *dfu_read_flash(uint8_t *src, uint8_t *dest, uint32_t len)
{
return dest;
}
__WEAK uint16_t dfu_write_flash(uint8_t *src, uint8_t *dest, uint32_t len)
{
return 0;
}
__WEAK uint16_t dfu_erase_flash(uint32_t add)
{
return 0;
}
__WEAK void dfu_leave(void)
{
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBD_DFU_H
#define USBD_DFU_H
#include "usb_dfu.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Init dfu interface driver */
struct usbd_interface *usbd_dfu_init_intf(struct usbd_interface *intf);
/* Interface functions that need to be implemented by the user */
uint8_t *dfu_read_flash(uint8_t *src, uint8_t *dest, uint32_t len);
uint16_t dfu_write_flash(uint8_t *src, uint8_t *dest, uint32_t len);
uint16_t dfu_erase_flash(uint32_t add);
void dfu_leave(void);
#ifdef __cplusplus
}
#endif
#endif /* USBD_DFU_H */

View File

@@ -0,0 +1,585 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USB_HID_H
#define USB_HID_H
/* Subclass codes (HID 4.2) */
#define HID_SUBCLASS_NONE 0 /* No subclass */
#define HID_SUBCLASS_BOOTIF 1 /* Boot Interface Subclass */
/* HID Protocol Codes (HID 4.3) */
#define HID_PROTOCOL_NONE 0x00
#define HID_PROTOCOL_BOOT 0x00
#define HID_PROTOCOL_KEYBOARD 0x01
#define HID_PROTOCOL_REPORT 0x01
#define HID_PROTOCOL_MOUSE 0x02
/* HID Class Descriptor Types (HID 7.1) */
#define HID_DESCRIPTOR_TYPE_HID 0x21
#define HID_DESCRIPTOR_TYPE_HID_REPORT 0x22
#define HID_DESCRIPTOR_TYPE_HID_PHYSICAL 0x23
/* HID Class Specific Requests (HID 7.2) */
#define HID_REQUEST_GET_REPORT 0x01
#define HID_REQUEST_GET_IDLE 0x02
#define HID_REQUEST_GET_PROTOCOL 0x03
#define HID_REQUEST_SET_REPORT 0x09
#define HID_REQUEST_SET_IDLE 0x0A
#define HID_REQUEST_SET_PROTOCOL 0x0B
/* Report Type (MS byte of wValue for GET_REPORT) (HID 7.2.1) */
#define HID_REPORT_INPUT 0x01
#define HID_REPORT_OUTPUT 0x02
#define HID_REPORT_FEATURE 0x03
/* HID Descriptor ***********************************************************/
#define HID_COUNTRY_NONE 0x00 /* Not Supported */
#define HID_COUNTRY_ARABIC 0x01 /* Arabic */
#define HID_COUNTRY_BELGIAN 0x02 /* Belgian */
#define HID_COUNTRY_CANADA 0x03 /* Canadian-Bilingual */
#define HID_COUNTRY_CANADRFR 0x04 /* Canadian-French */
#define HID_COUNTRY_CZECH 0x05 /* Czech Republic */
#define HID_COUNTRY_DANISH 0x06 /* Danish */
#define HID_COUNTRY_FINNISH 0x07 /* Finnish */
#define HID_COUNTRY_FRENCH 0x08 /* French */
#define HID_COUNTRY_GERMAN 0x09 /* German */
#define HID_COUNTRY_GREEK 0x10 /* Greek */
#define HID_COUNTRY_HEBREW 0x11 /* Hebrew */
#define HID_COUNTRY_HUNGARY 0x12 /* Hungary */
#define HID_COUNTRY_ISO 0x13 /* International (ISO) */
#define HID_COUNTRY_ITALIAN 0x14 /* Italian */
#define HID_COUNTRY_JAPAN 0x15 /* Japan (Katakana) */
#define HID_COUNTRY_KOREAN 0x16 /* Korean */
#define HID_COUNTRY_LATINAM 0x17 /* Latin American */
#define HID_COUNTRY_DUTCH 0x18 /* Netherlands/Dutch */
#define HID_COUNTRY_NORWEGIAN 0x19 /* Norwegian */
#define HID_COUNTRY_PERSIAN 0x20 /* Persian (Farsi) */
#define HID_COUNTRY_POLAND 0x21 /* Poland */
#define HID_COUNTRY_PORTUGUESE 0x22 /* Portuguese */
#define HID_COUNTRY_RUSSIA 0x23 /* Russia */
#define HID_COUNTRY_SLOVAKIA 0x24 /* Slovakia */
#define HID_COUNTRY_SPANISH 0x25 /* Spanish */
#define HID_COUNTRY_SWEDISH 0x26 /* Swedish */
#define HID_COUNTRY_SWISSFR 0x27 /* Swiss/French */
#define HID_COUNTRY_SWISSGR 0x28 /* Swiss/German */
#define HID_COUNTRY_SWITZERLAND 0x29 /* Switzerland */
#define HID_COUNTRY_TAIWAN 0x30 /* Taiwan */
#define HID_COUNTRY_TURKISHQ 0x31 /* Turkish-Q */
#define HID_COUNTRY_UK 0x32 /* UK */
#define HID_COUNTRY_US 0x33 /* US */
#define HID_COUNTRY_YUGOSLAVIA 0x34 /* Yugoslavia */
#define HID_COUNTRY_TURKISHF 0x35 /* Turkish-F */
/* HID report items */
#define HID_REPORT_ITEM_SIZE_MASK 0x03
#define HID_REPORT_ITEM_SIZE_0 0x00 /* No data follows */
#define HID_REPORT_ITEM_SIZE_1 0x01 /* 1 byte of data follows */
#define HID_REPORT_ITEM_SIZE_2 0x02 /* 2 bytes of data follow */
#define HID_REPORT_ITEM_SIZE_4 0x03 /* 4 bytes of data follow */
#define HID_REPORT_ITEM_TYPE_MASK 0x0c
#define HID_REPORT_ITEM_TYPE_MAIN 0x00
#define HID_REPORT_ITEM_TYPE_GLOBAL 0x04
#define HID_REPORT_ITEM_TYPE_LOCAL 0x08
#define HID_REPORT_ITEM_TAG_MASK 0xf0
/* Main Items (HID 6.2.2.4) */
#define HID_MAIN_ITEM_CONSTANT (1 << 0) /* Constant(1) vs Data(0) */
#define HID_MAIN_ITEM_VARIABLE (1 << 1) /* Variable(1) vs Array(0) */
#define HID_MAIN_ITEM_RELATIVE (1 << 2) /* Relative(1) vs Absolute(0) */
#define HID_MAIN_ITEM_WRAP (1 << 3) /* Wrap(1) vs No Wrap(0) */
#define HID_MAIN_ITEM_NONLINEAR (1 << 4) /* Non Linear(1) vs Linear(0) */
#define HID_MAIN_ITEM_NOPREFERRED (1 << 5) /* No Preferred (1) vs Preferred State(0) */
#define HID_MAIN_ITEM_NULLSTATE (1 << 6) /* Null state(1) vs No Null position(0) */
#define HID_MAIN_ITEM_VOLATILE (1 << 7) /* Volatile(1) vs Non volatile(0) */
#define HID_MAIN_ITEM_BUFFEREDBYTES (1 << 8) /* Buffered Bytes(1) vs Bit Field(0) */
#define HID_MAIN_ITEM_SIZE(pfx) ((pfx)&HID_REPORT_ITEM_SIZE_MASK)
#define HID_MAIN_ITEM_INPUT_PREFIX 0x80
#define HID_MAIN_ITEM_INPUT_CONSTANT HID_MAIN_ITEM_CONSTANT
#define HID_MAIN_ITEM_INPUT_VARIABLE HID_MAIN_ITEM_VARIABLE
#define HID_MAIN_ITEM_INPUT_RELATIVE HID_MAIN_ITEM_RELATIVE
#define HID_MAIN_ITEM_INPUT_WRAP HID_MAIN_ITEM_WRAP
#define HID_MAIN_ITEM_INPUT_NONLINEAR HID_MAIN_ITEM_NONLINEAR
#define HID_MAIN_ITEM_INPUT_NOPREFERRED HID_MAIN_ITEM_NOPREFERRED
#define HID_MAIN_ITEM_INPUT_NULLSTATE HID_MAIN_ITEM_NULLSTATE
#define HID_MAIN_ITEM_INPUT_BUFFEREDBYTES HID_MAIN_ITEM_BUFFEREDBYTES
#define HID_MAIN_ITEM_OUTPUT_PREFIX 0x90
#define HID_MAIN_ITEM_OUTPUT_CONSTANT HID_MAIN_ITEM_CONSTANT
#define HID_MAIN_ITEM_OUTPUT_VARIABLE HID_MAIN_ITEM_VARIABLE
#define HID_MAIN_ITEM_OUTPUT_RELATIVE HID_MAIN_ITEM_RELATIVE
#define HID_MAIN_ITEM_OUTPUT_WRAP HID_MAIN_ITEM_WRAP
#define HID_MAIN_ITEM_OUTPUT_NONLINEAR HID_MAIN_ITEM_NONLINEAR
#define HID_MAIN_ITEM_OUTPUT_NOPREFERRED HID_MAIN_ITEM_NOPREFERRED
#define HID_MAIN_ITEM_OUTPUT_NULLSTATE HID_MAIN_ITEM_NULLSTATE
#define HID_MAIN_ITEM_OUTPUT_VOLATILE HID_MAIN_ITEM_VOLATILE
#define HID_MAIN_ITEM_OUTPUT_BUFFEREDBYTES HID_MAIN_ITEM_BUFFEREDBYTES
#define HID_MAIN_ITEM_FEATURE_PREFIX 0xb0
#define HID_MAIN_ITEM_FEATURE_CONSTANT HID_MAIN_ITEM_CONSTANT
#define HID_MAIN_ITEM_FEATURE_VARIABLE HID_MAIN_ITEM_VARIABLE
#define HID_MAIN_ITEM_FEATURE_RELATIVE HID_MAIN_ITEM_RELATIVE
#define HID_MAIN_ITEM_FEATURE_WRAP HID_MAIN_ITEM_WRAP
#define HID_MAIN_ITEM_FEATURE_NONLINEAR HID_MAIN_ITEM_NONLINEAR
#define HID_MAIN_ITEM_FEATURE_NOPREFERRED HID_MAIN_ITEM_NOPREFERRED
#define HID_MAIN_ITEM_FEATURE_NULLSTATE HID_MAIN_ITEM_NULLSTATE
#define HID_MAIN_ITEM_FEATURE_VOLATILE HID_MAIN_ITEM_VOLATILE
#define HID_MAIN_ITEM_FEATURE_BUFFEREDBYTES HID_MAIN_ITEM_BUFFEREDBYTES
#define HID_MAIN_ITEM_COLLECTION_PREFIX 0xa0
#define HID_MAIN_ITEM_COLLECTION_PHYSICAL 0x00 /* Physical (group of axes) */
#define HID_MAIN_ITEM_COLLECTION_APPL 0x01 /* Application (mouse, keyboard) */
#define HID_MAIN_ITEM_COLLECTION_LOGICAL 0x02 /* Logical (interrelated data) */
#define HID_MAIN_ITEM_COLLECTION_REPORT 0x03 /* Report */
#define HID_MAIN_ITEM_COLLECTION_ARRAY 0x04 /* Named Array */
#define HID_MAIN_ITEM_COLLECTION_SWITCH 0x05 /* Usage Switch */
#define HID_MAIN_ITEM_COLLECTION_MODIFIER 0x06 /* Usage Modifier */
#define HID_MAIN_ITEM_ENDCOLLECTION_PREFIX 0xc0
/* Global Items (HID 6.2.2.7) */
#define HID_GLOBAL_ITEM_SIZE(pfx) ((pfx)&HID_REPORT_ITEM_SIZE_MASK)
#define HID_GLOBAL_ITEM_USAGEPAGE_PREFIX 0x04 /* Usage Page */
#define HID_GLOBAL_ITEM_LOGICALMIN_PREFIX 0x14 /* Logical Minimum */
#define HID_GLOBAL_ITEM_LOGICALMAX_PREFIX 0x24 /* Logical Maximum */
#define HID_GLOBAL_ITEM_PHYSICALMIN_PREFIX 0x34 /* Physical Minimum */
#define HID_GLOBAL_ITEM_PHYSMICALAX_PREFIX 0x44 /* Physical Maximum */
#define HID_GLOBAL_ITEM_UNITEXP_PREFIX 0x54 /* Unit Exponent */
#define HID_GLOBAL_ITEM_UNIT_PREFIX 0x64 /* Unit */
#define HID_GLOBAL_ITEM_REPORTSIZE_PREFIX 0x74 /* Report Size */
#define HID_GLOBAL_ITEM_REPORTID_PREFIX 0x84 /* Report ID */
#define HID_GLOBAL_ITEM_REPORTCOUNT_PREFIX 0x94 /* Report Count */
#define HID_GLOBAL_ITEM_PUSH_PREFIX 0xa4 /* Push */
#define HID_GLOBAL_ITEM_POP_PREFIX 0xb4 /* Pop */
/* Local Items (HID 6.2.2.8) */
#define HID_LOCAL_ITEM_SIZE(pfx) ((pfx)&HID_REPORT_ITEM_SIZE_MASK)
#define HID_LOCAL_ITEM_USAGE_PREFIX 0x08 /* Usage */
#define HID_LOCAL_ITEM_USAGEMIN_PREFIX 0x18 /* Usage Minimum */
#define HID_LOCAL_ITEM_USAGEMAX_PREFIX 0x28 /* Usage Maximum */
#define HID_LOCAL_ITEM_DESIGNATORIDX_PREFIX 0x38 /* Designator Index */
#define HID_LOCAL_ITEM_DESIGNATORMIN_PREFIX 0x48 /* Designator Minimum */
#define HID_LOCAL_ITEM_DESIGNATORMAX_PREFIX 0x58 /* Designator Maximum */
#define HID_LOCAL_ITEM_STRINGIDX_PREFIX 0x78 /* String Index */
#define HID_LOCAL_ITEM_STRINGMIN_PREFIX 0x88 /* String Minimum */
#define HID_LOCAL_ITEM_STRINGMAX_PREFIX 0x98 /* xx */
#define HID_LOCAL_ITEM_DELIMITER_PREFIX 0xa8 /* Delimiter */
/* Modifier Keys (HID 8.3) */
#define HID_MODIFER_LCTRL (1 << 0) /* Left Ctrl */
#define HID_MODIFER_LSHIFT (1 << 1) /* Left Shift */
#define HID_MODIFER_LALT (1 << 2) /* Left Alt */
#define HID_MODIFER_LGUI (1 << 3) /* Left GUI */
#define HID_MODIFER_RCTRL (1 << 4) /* Right Ctrl */
#define HID_MODIFER_RSHIFT (1 << 5) /* Right Shift */
#define HID_MODIFER_RALT (1 << 6) /* Right Alt */
#define HID_MODIFER_RGUI (1 << 7) /* Right GUI */
/* Keyboard output report (1 byte) (HID B.1) */
#define HID_KBD_OUTPUT_REPORT_NUMLOCK (1 << 0)
#define HID_KBD_OUTPUT_REPORT_CAPSLOCK (1 << 1)
#define HID_KBD_OUTPUT_REPORT_SCROLLLOCK (1 << 2)
#define HID_KBD_OUTPUT_REPORT_COMPOSE (1 << 3)
#define HID_KBD_OUTPUT_REPORT_KANA (1 << 4)
/* Mouse input report (HID B.2) */
#define HID_MOUSE_INPUT_REPORT_BUTTON1 (1 << 0)
#define HID_MOUSE_INPUT_REPORT_BUTTON2 (1 << 1)
#define HID_MOUSE_INPUT_REPORT_BUTTON3 (1 << 2)
#define HID_MOUSE_INPUT_REPORT_BUTTON_MASK (7)
#define HID_MOUSE_INPUT_BUTTON_LEFT (1 << 0)
#define HID_MOUSE_INPUT_BUTTON_RIGHT (1 << 1)
#define HID_MOUSE_INPUT_BUTTON_MIDDLE (1 << 2)
#define HID_MOUSE_INPUT_BUTTON_BACKWARD (1 << 3)
#define HID_MOUSE_INPUT_BUTTON_FORWARD (1 << 4)
/* Joystick input report (4 bytes) (HID D.1) */
#define HID_JS_INPUT_REPORT_HATSWITCH_SHIFT (0)
#define HID_JS_INPUT_REPORT_HATSWITCH_MASK (15 << HID_JSIN_HATSWITCH_SHIFT)
#define HID_JS_INPUT_REPORT_BUTTON1 (1 << 4)
#define HID_JS_INPUT_REPORT_BUTTON2 (1 << 5)
#define HID_JS_INPUT_REPORT_BUTTON3 (1 << 6)
#define HID_JS_INPUT_REPORT_BUTTON4 (1 << 7)
/* Usage pages (HuT 3) */
#define HID_USAGE_PAGE_UNDEFINED 0x00 /* Undefined */
#define HID_USAGE_PAGE_GENERIC_DCTRL 0x01 /* Generic Desktop Controls */
#define HID_USAGE_PAGE_SIMCTRL 0x02 /* Simulation Controls */
#define HID_USAGE_PAGE_VRCTRL 0x03 /* VR Controls */
#define HID_USAGE_PAGE_SPORTCTRL 0x04 /* Sport Controls */
#define HID_USAGE_PAGE_GAMECTRL 0x05 /* Game Controls */
#define HID_USAGE_PAGE_GENERIC_DEVCTRL 0x06 /* Generic Device Controls */
#define HID_USAGE_PAGE_KBD 0x07 /* Keyboard/Keypad */
#define HID_USAGE_PAGE_LEDS 0x08 /* LEDs */
#define HID_USAGE_PAGE_BUTTON 0x09 /* Button */
#define HID_USAGE_PAGE_ORDINAL 0x0a /* Ordinal */
#define HID_USAGE_PAGE_TELEPHONY 0x0b /* Telephony */
#define HID_USAGE_PAGE_CONSUMER 0x0c /* Consumer */
#define HID_USAGE_PAGE_DIGITIZER 0x0d /* Digitizer */
/* 0x0e Reserved */
#define HID_USAGE_PAGE_PIDPAGE 0x0f /* PID Page Physical Interface Device */
#define HID_USAGE_PAGE_UNICODE 0x10 /* Unicode */
/* 0x11-13 Reserved */
#define HID_USAGE_PAGE_ALPHA_DISPLAY 0x14 /* Alphanumeric Display */
/* 0x15-3f Reserved */
#define HID_USAGE_PAGE_MEDICAL 0x40 /* Medical Instruments */
/* 0x41-7f Reserved */
/* 0x80-83 Monitor Devices */
/* 0x84-87 Power Devices */
/* 0x88-8b Reserved */
#define HID_USAGE_PAGE_BARCODE_SCANNER 0x8c /* Bar Code Scanner page */
#define HID_USAGE_PAGE_SCALE 0x8d /* Scale page */
#define HID_USAGE_PAGE_MSR 0x8e /* Magnetic Stripe Reading (MSR) Devices */
#define HID_USAGE_PAGE_POS 0x8f /* Point of Sale devices */
#define HID_USAGE_PAGE_CAMERA_CTRL 0x90 /* Camera Control Page */
/* Generic Desktop Page Usage IDs (HuT 4) */
#define HID_DESKTOP_USAGE_UNDEFINED 0x00 /* Undefined */
#define HID_DESKTOP_USAGE_POINTER 0x01 /* Pointer */
#define HID_DESKTOP_USAGE_MOUSE 0x02 /* Mouse */
/* 0x03 Reserved */
#define HID_DESKTOP_USAGE_JOYSTICK 0x04 /* Joystick */
#define HID_DESKTOP_USAGE_GAMEPAD 0x05 /* Game Pad */
#define HID_DESKTOP_USAGE_KEYBOARD 0x06 /* Keyboard */
#define HID_DESKTOP_USAGE_KEYPAD 0x07 /* Keypad */
#define HID_DESKTOP_USAGE_MULTIAXIS 0x08 /* Multi-axis Controller */
#define HID_DESKTOP_USAGE_TABLET 0x09 /* Tablet PC System Controls */
/* 0x0a-2f Reserved */
#define HID_DESKTOP_USAGE_X 0x30 /* X */
#define HID_DESKTOP_USAGE_Y 0x31 /* Y */
#define HID_DESKTOP_USAGE_Z 0x32 /* Z */
#define HID_DESKTOP_USAGE_RX 0x33 /* Rx */
#define HID_DESKTOP_USAGE_RY 0x34 /* Ry */
#define HID_DESKTOP_USAGE_RZ 0x35 /* Rz */
#define HID_DESKTOP_USAGE_SLIDER 0x36 /* Slider */
#define HID_DESKTOP_USAGE_DIAL 0x37 /* Dial */
#define HID_DESKTOP_USAGE_WHEEL 0x38 /* Wheel */
#define HID_DESKTOP_USAGE_HATSWITCH 0x39 /* Hat switch */
#define HID_DESKTOP_USAGE_COUNTED 0x3a /* Counted Buffer */
#define HID_DESKTOP_USAGE_BYTECOUNT 0x3b /* Byte Count */
#define HID_DESKTOP_USAGE_MOTION 0x3c /* Motion Wakeup */
#define HID_DESKTOP_USAGE_START 0x3d /* Start */
#define HID_DESKTOP_USAGE_SELECT 0x3e /* Select */
/* 0x3f Reserved */
#define HID_DESKTOP_USAGE_VX 0x40 /* Vx */
#define HID_DESKTOP_USAGE_VY 0x41 /* Vy */
#define HID_DESKTOP_USAGE_VZ 0x42 /* Vz */
#define HID_DESKTOP_USAGE_VBRX 0x43 /* Vbrx */
#define HID_DESKTOP_USAGE_VBRY 0x44 /* Vbry */
#define HID_DESKTOP_USAGE_VBRZ 0x45 /* Vbrz */
#define HID_DESKTOP_USAGE_VNO 0x46 /* Vno */
#define HID_DESKTOP_USAGE_FEATURE 0x47 /* Feature Notification */
#define HID_DESKTOP_USAGE_RESOLUTION 0x48 /* Resolution Multiplier */
/* 0x49-7f Reserved */
#define HID_DESKTOP_USAGE_CONTROL 0x80 /* System Control */
#define HID_DESKTOP_USAGE_POWERDOWN 0x81 /* System Power Down */
#define HID_DESKTOP_USAGE_SLEEP 0x82 /* System Sleep */
#define HID_DESKTOP_USAGE_WAKEUP 0x83 /* System Wake Up */
#define HID_DESKTOP_USAGE_CONTEXT_MENU 0x84 /* System Context Menu */
#define HID_DESKTOP_USAGE_MAIN_MENU 0x85 /* System Main Menu */
#define HID_DESKTOP_USAGE_APP_MENU 0x86 /* System App Menu */
#define HID_DESKTOP_USAGE_MENU_HELP 0x87 /* System Menu Help */
#define HID_DESKTOP_USAGE_MENU_EXIT 0x88 /* System Menu Exit */
#define HID_DESKTOP_USAGE_MENU_SELECT 0x89 /* System Menu Select */
#define HID_DESKTOP_USAGE_MENU_RIGHT 0x8a /* System Menu Right */
#define HID_DESKTOP_USAGE_MENU_LEFT 0x8b /* System Menu Left */
#define HID_DESKTOP_USAGE_MENU_UP 0x8c /* System Menu Up */
#define HID_DESKTOP_USAGE_MENU_DOWN 0x8d /* System Menu Down */
#define HID_DESKTOP_USAGE_COLD_RESTART 0x8e /* System Cold Restart */
#define HID_DESKTOP_USAGE_WARM_RESTART 0x8f /* System Warm Restart */
#define HID_DESKTOP_USAGE_DPAD_UP 0x90 /* D-pad Up */
#define HID_DESKTOP_USAGE_DPAD_DOWN 0x91 /* D-pad Down */
#define HID_DESKTOP_USAGE_DPAD_RIGHT 0x92 /* D-pad Right */
#define HID_DESKTOP_USAGE_DPAD_LEFT 0x93 /* D-pad Left */
/* 0x94-9f Reserved */
#define HID_DESKTOP_USAGE_DOCK 0xa0 /* System Dock */
#define HID_DESKTOP_USAGE_UNDOCK 0xa1 /* System Undock */
#define HID_DESKTOP_USAGE_SETUP 0xa2 /* System Setup */
#define HID_DESKTOP_USAGE_BREAK 0xa3 /* System Break */
#define HID_DESKTOP_USAGE_DEBUG_BREAK 0xa4 /* System Debugger Break */
#define HID_DESKTOP_USAGE_APP_BREAK 0xa5 /* Application Break */
#define HID_DESKTOP_USAGE_APP_DEBUG_BREAK 0xa6 /* Application Debugger Break */
#define HID_DESKTOP_USAGE_MUTE 0xa7 /* System Speaker Mute */
#define HID_DESKTOP_USAGE_HIBERNATE 0xa8 /* System Hibernate */
/* 0xa9-af Reserved */
#define HID_DESKTOP_USAGE_DISPLAY_INVERT 0xb0 /* System Display Invert */
#define HID_DESKTOP_USAGE_DISPALY_INTERNAL 0xb1 /* System Display Internal */
#define HID_DESKTOP_USAGE_DISPLAY_EXTERNAL 0xb2 /* System Display External */
#define HID_DESKTOP_USAGE_DISPLAY_BOTH 0xb3 /* System Display Both */
#define HID_DESKTOP_USAGE_DISPLAY_DUAL 0xb4 /* System Display Dual */
#define HID_DESKTOP_USAGE_DISPLAY_TOGGLE 0xb5 /* System Display Toggle Int/Ext */
#define HID_DESKTOP_USAGE_DISPLAY_SWAP 0xb6 /* System Display Swap */
#define HID_DESKTOP_USAGE_ 0xb7 /* System Display LCD Autoscale */
/* 0xb8-ffff Reserved */
/* Keyboard usage IDs (HuT 10) */
#define HID_KBD_USAGE_NONE 0x00 /* Reserved (no event indicated) */
#define HID_KBD_USAGE_ERRORROLLOVER 0x01 /* Keyboard ErrorRollOver */
#define HID_KBD_USAGE_POSTFAIL 0x02 /* Keyboard POSTFail */
#define HID_KBD_USAGE_ERRUNDEF 0x03 /* Keyboard ErrorUndefined */
#define HID_KBD_USAGE_A 0x04 /* Keyboard a or A (B-Z follow) */
#define HID_KBD_USAGE_1 0x1e /* Keyboard 1 (2-9 follow) */
#define HID_KBD_USAGE_EXCLAM 0x1e /* Keyboard 1 and ! */
#define HID_KBD_USAGE_AT 0x1f /* Keyboard 2 and @ */
#define HID_KBD_USAGE_POUND 0x20 /* Keyboard 3 and # */
#define HID_KBD_USAGE_DOLLAR 0x21 /* Keyboard 4 and $ */
#define HID_KBD_USAGE_PERCENT 0x22 /* Keyboard 5 and % */
#define HID_KBD_USAGE_CARAT 0x23 /* Keyboard 6 and ^ */
#define HID_KBD_USAGE_AMPERSAND 0x24 /* Keyboard 7 and & */
#define HID_KBD_USAGE_ASTERISK 0x25 /* Keyboard 8 and * */
#define HID_KBD_USAGE_LPAREN 0x26 /* Keyboard 9 and ( */
#define HID_KBD_USAGE_0 0x27 /* Keyboard 0 and ) */
#define HID_KBD_USAGE_RPAREN 0x27 /* Keyboard 0 and ) */
#define HID_KBD_USAGE_ENTER 0x28 /* Keyboard Return (ENTER) */
#define HID_KBD_USAGE_ESCAPE 0x29 /* Keyboard ESCAPE */
#define HID_KBD_USAGE_DELETE 0x2a /* Keyboard DELETE (Backspace) */
#define HID_KBD_USAGE_TAB 0x2b /* Keyboard Tab */
#define HID_KBD_USAGE_SPACE 0x2c /* Keyboard Spacebar */
#define HID_KBD_USAGE_HYPHEN 0x2d /* Keyboard - and (underscore) */
#define HID_KBD_USAGE_UNDERSCORE 0x2d /* Keyboard - and (underscore) */
#define HID_KBD_USAGE_EQUAL 0x2e /* Keyboard = and + */
#define HID_KBD_USAGE_PLUS 0x2e /* Keyboard = and + */
#define HID_KBD_USAGE_LBRACKET 0x2f /* Keyboard [ and { */
#define HID_KBD_USAGE_LBRACE 0x2f /* Keyboard [ and { */
#define HID_KBD_USAGE_RBRACKET 0x30 /* Keyboard ] and } */
#define HID_KBD_USAGE_RBRACE 0x30 /* Keyboard ] and } */
#define HID_KBD_USAGE_BSLASH 0x31 /* Keyboard \ and | */
#define HID_KBD_USAGE_VERTBAR 0x31 /* Keyboard \ and | */
#define HID_KBD_USAGE_NONUSPOUND 0x32 /* Keyboard Non-US # and ~ */
#define HID_KBD_USAGE_TILDE 0x32 /* Keyboard Non-US # and ~ */
#define HID_KBD_USAGE_SEMICOLON 0x33 /* Keyboard ; and : */
#define HID_KBD_USAGE_COLON 0x33 /* Keyboard ; and : */
#define HID_KBD_USAGE_SQUOTE 0x34 /* Keyboard ' and " */
#define HID_KBD_USAGE_DQUOUTE 0x34 /* Keyboard ' and " */
#define HID_KBD_USAGE_GACCENT 0x35 /* Keyboard Grave Accent and Tilde */
#define HID_KBD_USAGE_GTILDE 0x35 /* Keyboard Grave Accent and Tilde */
#define HID_KBD_USAGE_COMMON 0x36 /* Keyboard , and < */
#define HID_KBD_USAGE_LT 0x36 /* Keyboard , and < */
#define HID_KBD_USAGE_PERIOD 0x37 /* Keyboard . and > */
#define HID_KBD_USAGE_GT 0x37 /* Keyboard . and > */
#define HID_KBD_USAGE_DIV 0x38 /* Keyboard / and ? */
#define HID_KBD_USAGE_QUESTION 0x38 /* Keyboard / and ? */
#define HID_KBD_USAGE_CAPSLOCK 0x39 /* Keyboard Caps Lock */
#define HID_KBD_USAGE_F1 0x3a /* Keyboard F1 */
#define HID_KBD_USAGE_F2 0x3b /* Keyboard F2 */
#define HID_KBD_USAGE_F3 0x3c /* Keyboard F3 */
#define HID_KBD_USAGE_F4 0x3d /* Keyboard F4 */
#define HID_KBD_USAGE_F5 0x3e /* Keyboard F5 */
#define HID_KBD_USAGE_F6 0x3f /* Keyboard F6 */
#define HID_KBD_USAGE_F7 0x40 /* Keyboard F7 */
#define HID_KBD_USAGE_F8 0x41 /* Keyboard F8 */
#define HID_KBD_USAGE_F9 0x42 /* Keyboard F9 */
#define HID_KBD_USAGE_F10 0x43 /* Keyboard F10 */
#define HID_KBD_USAGE_F11 0x44 /* Keyboard F11 */
#define HID_KBD_USAGE_F12 0x45 /* Keyboard F12 */
#define HID_KBD_USAGE_PRINTSCN 0x46 /* Keyboard PrintScreen */
#define HID_KBD_USAGE_SCROLLLOCK 0x47 /* Keyboard Scroll Lock */
#define HID_KBD_USAGE_PAUSE 0x48 /* Keyboard Pause */
#define HID_KBD_USAGE_INSERT 0x49 /* Keyboard Insert */
#define HID_KBD_USAGE_HOME 0x4a /* Keyboard Home */
#define HID_KBD_USAGE_PAGEUP 0x4b /* Keyboard PageUp */
#define HID_KBD_USAGE_DELFWD 0x4c /* Keyboard Delete Forward */
#define HID_KBD_USAGE_END 0x4d /* Keyboard End */
#define HID_KBD_USAGE_PAGEDOWN 0x4e /* Keyboard PageDown */
#define HID_KBD_USAGE_RIGHT 0x4f /* eyboard RightArrow */
#define HID_KBD_USAGE_LEFT 0x50 /* Keyboard LeftArrow */
#define HID_KBD_USAGE_DOWN 0x51 /* Keyboard DownArrow */
#define HID_KBD_USAGE_UP 0x52 /* Keyboard UpArrow */
#define HID_KBD_USAGE_KPDNUMLOCK 0x53 /* Keypad Num Lock and Clear */
#define HID_KBD_USAGE_KPDNUMLOCKCLEAR 0x53 /* Keypad Num Lock and Clear */
#define HID_KBD_USAGE_KPDDIV 0x54 /* Keypad / */
#define HID_KBD_USAGE_KPDMUL 0x55 /* Keypad * */
#define HID_KBD_USAGE_KPDHMINUS 0x56 /* Keypad - */
#define HID_KBD_USAGE_KPDPLUS 0x57 /* Keypad + */
#define HID_KBD_USAGE_KPDEMTER 0x58 /* Keypad ENTER */
#define HID_KBD_USAGE_KPD1 0x59 /* Keypad 1 (2-9 follow) */
#define HID_KBD_USAGE_KPDEND 0x59 /* Keypad 1 and End */
#define HID_KBD_USAGE_KPDDOWN 0x5a /* Keypad 2 and Down Arrow */
#define HID_KBD_USAGE_KPDPAGEDN 0x5b /* Keypad 3 and PageDn */
#define HID_KBD_USAGE_KPDLEFT 0x5c /* Keypad 4 and Left Arrow */
#define HID_KBD_USAGE_KPDRIGHT 0x5e /* Keypad 6 and Right Arrow */
#define HID_KBD_USAGE_KPDHOME 0x5f /* Keypad 7 and Home */
#define HID_KBD_USAGE_KPDUP 0x60 /* Keypad 8 and Up Arrow */
#define HID_KBD_USAGE_KPDPAGEUP 0x61 /* Keypad 9 and PageUp */
#define HID_KBD_USAGE_KPD0 0x62 /* Keypad 0 and Insert */
#define HID_KBD_USAGE_KPDINSERT 0x62 /* Keypad 0 and Insert */
#define HID_KBD_USAGE_KPDDECIMALPT 0x63 /* Keypad . and Delete */
#define HID_KBD_USAGE_KPDDELETE 0x63 /* Keypad . and Delete */
#define HID_KBD_USAGE_NONSLASH 0x64 /* Keyboard Non-US \ and | */
#define HID_KBD_USAGE_NONUSVERT 0x64 /* Keyboard Non-US \ and | */
#define HID_KBD_USAGE_APPLICATION 0x65 /* Keyboard Application */
#define HID_KBD_USAGE_POWER 0x66 /* Keyboard Power */
#define HID_KBD_USAGE_KPDEQUAL 0x67 /* Keypad = */
#define HID_KBD_USAGE_F13 0x68 /* Keyboard F13 */
#define HID_KBD_USAGE_F14 0x69 /* Keyboard F14 */
#define HID_KBD_USAGE_F15 0x6a /* Keyboard F15 */
#define HID_KBD_USAGE_F16 0x6b /* Keyboard F16 */
#define HID_KBD_USAGE_F17 0x6c /* Keyboard F17 */
#define HID_KBD_USAGE_F18 0x6d /* Keyboard F18 */
#define HID_KBD_USAGE_F19 0x6e /* Keyboard F19 */
#define HID_KBD_USAGE_F20 0x6f /* Keyboard F20 */
#define HID_KBD_USAGE_F21 0x70 /* Keyboard F21 */
#define HID_KBD_USAGE_F22 0x71 /* Keyboard F22 */
#define HID_KBD_USAGE_F23 0x72 /* Keyboard F23 */
#define HID_KBD_USAGE_F24 0x73 /* Keyboard F24 */
#define HID_KBD_USAGE_EXECUTE 0x74 /* Keyboard Execute */
#define HID_KBD_USAGE_HELP 0x75 /* Keyboard Help */
#define HID_KBD_USAGE_MENU 0x76 /* Keyboard Menu */
#define HID_KBD_USAGE_SELECT 0x77 /* Keyboard Select */
#define HID_KBD_USAGE_STOP 0x78 /* Keyboard Stop */
#define HID_KBD_USAGE_AGAIN 0x79 /* Keyboard Again */
#define HID_KBD_USAGE_UNDO 0x7a /* Keyboard Undo */
#define HID_KBD_USAGE_CUT 0x7b /* Keyboard Cut */
#define HID_KBD_USAGE_COPY 0x7c /* Keyboard Copy */
#define HID_KBD_USAGE_PASTE 0x7d /* Keyboard Paste */
#define HID_KBD_USAGE_FIND 0x7e /* Keyboard Find */
#define HID_KBD_USAGE_MUTE 0x7f /* Keyboard Mute */
#define HID_KBD_USAGE_VOLUP 0x80 /* Keyboard Volume Up */
#define HID_KBD_USAGE_VOLDOWN 0x81 /* Keyboard Volume Down */
#define HID_KBD_USAGE_LCAPSLOCK 0x82 /* Keyboard Locking Caps Lock */
#define HID_KBD_USAGE_LNUMLOCK 0x83 /* Keyboard Locking Num Lock */
#define HID_KBD_USAGE_LSCROLLLOCK 0x84 /* Keyboard Locking Scroll Lock */
#define HID_KBD_USAGE_KPDCOMMA 0x85 /* Keypad Comma */
#define HID_KBD_USAGE_KPDEQUALSIGN 0x86 /* Keypad Equal Sign */
#define HID_KBD_USAGE_INTERNATIONAL1 0x87 /* Keyboard International 1 */
#define HID_KBD_USAGE_INTERNATIONAL2 0x88 /* Keyboard International 2 */
#define HID_KBD_USAGE_INTERNATIONAL3 0x89 /* Keyboard International 3 */
#define HID_KBD_USAGE_INTERNATIONAL4 0x8a /* Keyboard International 4 */
#define HID_KBD_USAGE_INTERNATIONAL5 0x8b /* Keyboard International 5 */
#define HID_KBD_USAGE_INTERNATIONAL6 0x8c /* Keyboard International 6 */
#define HID_KBD_USAGE_INTERNATIONAL7 0x8d /* Keyboard International 7 */
#define HID_KBD_USAGE_INTERNATIONAL8 0x8e /* Keyboard International 8 */
#define HID_KBD_USAGE_INTERNATIONAL9 0x8f /* Keyboard International 9 */
#define HID_KBD_USAGE_LANG1 0x90 /* Keyboard LANG1 */
#define HID_KBD_USAGE_LANG2 0x91 /* Keyboard LANG2 */
#define HID_KBD_USAGE_LANG3 0x92 /* Keyboard LANG3 */
#define HID_KBD_USAGE_LANG4 0x93 /* Keyboard LANG4 */
#define HID_KBD_USAGE_LANG5 0x94 /* Keyboard LANG5 */
#define HID_KBD_USAGE_LANG6 0x95 /* Keyboard LANG6 */
#define HID_KBD_USAGE_LANG7 0x96 /* Keyboard LANG7 */
#define HID_KBD_USAGE_LANG8 0x97 /* Keyboard LANG8 */
#define HID_KBD_USAGE_LANG9 0x98 /* Keyboard LANG9 */
#define HID_KBD_USAGE_ALTERASE 0x99 /* Keyboard Alternate Erase */
#define HID_KBD_USAGE_SYSREQ 0x9a /* Keyboard SysReq/Attention */
#define HID_KBD_USAGE_CANCEL 0x9b /* Keyboard Cancel */
#define HID_KBD_USAGE_CLEAR 0x9c /* Keyboard Clear */
#define HID_KBD_USAGE_PRIOR 0x9d /* Keyboard Prior */
#define HID_KBD_USAGE_RETURN 0x9e /* Keyboard Return */
#define HID_KBD_USAGE_SEPARATOR 0x9f /* Keyboard Separator */
#define HID_KBD_USAGE_OUT 0xa0 /* Keyboard Out */
#define HID_KBD_USAGE_OPER 0xa1 /* Keyboard Oper */
#define HID_KBD_USAGE_CLEARAGAIN 0xa2 /* Keyboard Clear/Again */
#define HID_KBD_USAGE_CLRSEL 0xa3 /* Keyboard CrSel/Props */
#define HID_KBD_USAGE_EXSEL 0xa4 /* Keyboard ExSel */
#define HID_KBD_USAGE_KPD00 0xb0 /* Keypad 00 */
#define HID_KBD_USAGE_KPD000 0xb1 /* Keypad 000 */
#define HID_KBD_USAGE_THOUSEPARATOR 0xb2 /* Thousands Separator */
#define HID_KBD_USAGE_DECSEPARATOR 0xb3 /* Decimal Separator */
#define HID_KBD_USAGE_CURRUNIT 0xb4 /* Currency Unit */
#define HID_KBD_USAGE_CURRSUBUNIT 0xb5 /* Currency Sub-unit */
#define HID_KBD_USAGE_KPDLPAREN 0xb6 /* Keypad ( */
#define HID_KBD_USAGE_KPDRPAREN 0xb7 /* Keypad ) */
#define HID_KBD_USAGE_KPDLBRACE 0xb8 /* Keypad { */
#define HID_KBD_USAGE_KPDRBRACE 0xb9 /* Keypad } */
#define HID_KBD_USAGE_KPDTAB 0xba /* Keypad Tab */
#define HID_KBD_USAGE_KPDBACKSPACE 0xbb /* Keypad Backspace */
#define HID_KBD_USAGE_KPDA 0xbc /* Keypad A (B-F follow) */
#define HID_KBD_USAGE_KPDXOR 0xc2 /* Keypad XOR */
#define HID_KBD_USAGE_KPDEXP 0xc3 /* Keypad ^ */
#define HID_KBD_USAGE_KPDPERCENT 0xc4 /* Keypad % */
#define HID_KBD_USAGE_KPDLT 0xc5 /* Keypad < */
#define HID_KBD_USAGE_KPDGT 0xc6 /* Keypad > */
#define HID_KBD_USAGE_KPDAMPERSAND 0xc7 /* Keypad & */
#define HID_KBD_USAGE_KPDAND 0xc8 /* Keypad && */
#define HID_KBD_USAGE_KPDVERT 0xc9 /* Keypad | */
#define HID_KBD_USAGE_KPDOR 0xca /* Keypad || */
#define HID_KBD_USAGE_KPDCOLON 0xcb /* Keypad : */
#define HID_KBD_USAGE_KPDPOUND 0xcc /* Keypad # */
#define HID_KBD_USAGE_KPDSPACE 0xcd /* Keypad Space */
#define HID_KBD_USAGE_KPDAT 0xce /* Keypad @ */
#define HID_KBD_USAGE_KPDEXCLAM 0xcf /* Keypad ! */
#define HID_KBD_USAGE_KPDMEMSTORE 0xd0 /* Keypad Memory Store */
#define HID_KBD_USAGE_KPDMEMRECALL 0xd1 /* Keypad Memory Recall */
#define HID_KBD_USAGE_KPDMEMCLEAR 0xd2 /* Keypad Memory Clear */
#define HID_KBD_USAGE_KPDMEMADD 0xd3 /* Keypad Memory Add */
#define HID_KBD_USAGE_KPDMEMSUB 0xd4 /* Keypad Memory Subtract */
#define HID_KBD_USAGE_KPDMEMMULT 0xd5 /* Keypad Memory Multiply */
#define HID_KBD_USAGE_KPDMEMDIV 0xd6 /* Keypad Memory Divide */
#define HID_KBD_USAGE_KPDPLUSMINUS 0xd7 /* Keypad +/- */
#define HID_KBD_USAGE_KPDCLEAR 0xd8 /* Keypad Clear */
#define HID_KBD_USAGE_KPDCLEARENTRY 0xd9 /* Keypad Clear Entry */
#define HID_KBD_USAGE_KPDBINARY 0xda /* Keypad Binary */
#define HID_KBD_USAGE_KPDOCTAL 0xdb /* Keypad Octal */
#define HID_KBD_USAGE_KPDDECIMAL 0xdc /* Keypad Decimal */
#define HID_KBD_USAGE_KPDHEXADECIMAL 0xdd /* Keypad Hexadecimal */
#define HID_KBD_USAGE_LCTRL 0xe0 /* Keyboard LeftControl */
#define HID_KBD_USAGE_LSHIFT 0xe1 /* Keyboard LeftShift */
#define HID_KBD_USAGE_LALT 0xe2 /* Keyboard LeftAlt */
#define HID_KBD_USAGE_LGUI 0xe3 /* Keyboard Left GUI */
#define HID_KBD_USAGE_RCTRL 0xe4 /* Keyboard RightControl */
#define HID_KBD_USAGE_RSHIFT 0xe5 /* Keyboard RightShift */
#define HID_KBD_USAGE_RALT 0xe6 /* Keyboard RightAlt */
#define HID_KBD_USAGE_RGUI 0xe7 /* Keyboard Right GUI */
#define HID_KBD_USAGE_MAX 0xe7
/* HID Report Definitions */
struct usb_hid_class_subdescriptor {
uint8_t bDescriptorType;/* Class descriptor type (See 7.1) */
uint16_t wDescriptorLength;/* Size of the report descriptor */
} __PACKED;
struct usb_hid_descriptor {
uint8_t bLength; /* Size of the HID descriptor */
uint8_t bDescriptorType;/* HID descriptor type */
uint16_t bcdHID;/* HID class specification release */
uint8_t bCountryCode;/* Country code */
uint8_t bNumDescriptors;/* Number of descriptors (>=1) */
/*
* Specification says at least one Class Descriptor needs to
* be present (Report Descriptor).
*/
struct usb_hid_class_subdescriptor subdesc[1];
} __PACKED;
/* Standard Reports *********************************************************/
/* Keyboard input report (8 bytes) (HID B.1) */
struct usb_hid_kbd_report
{
uint8_t modifier; /* Modifier keys. See HID_MODIFER_* definitions */
uint8_t reserved;
uint8_t key[6]; /* Keycode 1-6 */
};
/* Keyboard output report (1 byte) (HID B.1),
* see USBHID_KBDOUT_* definitions
*/
/* Mouse input report (HID B.2) */
struct usb_hid_mouse_report
{
uint8_t buttons; /* See HID_MOUSE_INPUT_BUTTON_* definitions */
uint8_t xdisp; /* X displacement */
uint8_t ydisp; /* y displacement */
/* Device specific additional bytes may follow */
#ifdef CONFIG_INPUT_MOUSE_WHEEL
uint8_t wdisp; /* Wheel displacement */
#endif
};
/* Joystick input report (1 bytes) (HID D.1) */
struct usb_hid_js_report
{
uint8_t xpos; /* X position */
uint8_t ypos; /* X position */
uint8_t buttons; /* See USBHID_JSIN_* definitions */
uint8_t throttle; /* Throttle */
};
#endif /* USB_HID_H */

View File

@@ -0,0 +1,89 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbd_core.h"
#include "usbd_hid.h"
static int hid_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
{
USB_LOG_DBG("HID Class request: "
"bRequest 0x%02x\r\n",
setup->bRequest);
uint8_t intf_num = LO_BYTE(setup->wIndex);
switch (setup->bRequest) {
case HID_REQUEST_GET_REPORT:
/* report id ,report type */
usbd_hid_get_report(busid, intf_num, LO_BYTE(setup->wValue), HI_BYTE(setup->wValue), data, len);
break;
case HID_REQUEST_GET_IDLE:
(*data)[0] = usbd_hid_get_idle(busid, intf_num, LO_BYTE(setup->wValue));
*len = 1;
break;
case HID_REQUEST_GET_PROTOCOL:
(*data)[0] = usbd_hid_get_protocol(busid, intf_num);
*len = 1;
break;
case HID_REQUEST_SET_REPORT:
/* report id ,report type, report, report len */
usbd_hid_set_report(busid, intf_num, LO_BYTE(setup->wValue), HI_BYTE(setup->wValue), *data, *len);
break;
case HID_REQUEST_SET_IDLE:
/* report id, duration */
usbd_hid_set_idle(busid, intf_num, LO_BYTE(setup->wValue), HI_BYTE(setup->wValue));
break;
case HID_REQUEST_SET_PROTOCOL:
/* protocol */
usbd_hid_set_protocol(busid, intf_num, LO_BYTE(setup->wValue));
break;
default:
USB_LOG_WRN("Unhandled HID Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
return 0;
}
struct usbd_interface *usbd_hid_init_intf(uint8_t busid, struct usbd_interface *intf, const uint8_t *desc, uint32_t desc_len)
{
intf->class_interface_handler = hid_class_interface_request_handler;
intf->class_endpoint_handler = NULL;
intf->vendor_handler = NULL;
intf->notify_handler = NULL;
intf->hid_report_descriptor = desc;
intf->hid_report_descriptor_len = desc_len;
return intf;
}
__WEAK void usbd_hid_get_report(uint8_t busid, uint8_t intf, uint8_t report_id, uint8_t report_type, uint8_t **data, uint32_t *len)
{
(*data[0]) = 0;
*len = 1;
}
__WEAK uint8_t usbd_hid_get_idle(uint8_t busid, uint8_t intf, uint8_t report_id)
{
return 0;
}
__WEAK uint8_t usbd_hid_get_protocol(uint8_t busid, uint8_t intf)
{
return 0;
}
__WEAK void usbd_hid_set_report(uint8_t busid, uint8_t intf, uint8_t report_id, uint8_t report_type, uint8_t *report, uint32_t report_len)
{
}
__WEAK void usbd_hid_set_idle(uint8_t busid, uint8_t intf, uint8_t report_id, uint8_t duration)
{
}
__WEAK void usbd_hid_set_protocol(uint8_t busid, uint8_t intf, uint8_t protocol)
{
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBD_HID_H
#define USBD_HID_H
#include "usb_hid.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Init hid interface driver */
struct usbd_interface *usbd_hid_init_intf(uint8_t busid, struct usbd_interface *intf, const uint8_t *desc, uint32_t desc_len);
/* Register desc api */
void usbd_hid_descriptor_register(uint8_t busid, uint8_t intf_num, const uint8_t *desc);
void usbd_hid_report_descriptor_register(uint8_t busid, uint8_t intf_num, const uint8_t *desc, uint32_t desc_len);
/* Setup request command callback api */
void usbd_hid_get_report(uint8_t busid, uint8_t intf, uint8_t report_id, uint8_t report_type, uint8_t **data, uint32_t *len);
uint8_t usbd_hid_get_idle(uint8_t busid, uint8_t intf, uint8_t report_id);
uint8_t usbd_hid_get_protocol(uint8_t busid, uint8_t intf);
void usbd_hid_set_report(uint8_t busid, uint8_t intf, uint8_t report_id, uint8_t report_type, uint8_t *report, uint32_t report_len);
void usbd_hid_set_idle(uint8_t busid, uint8_t intf, uint8_t report_id, uint8_t duration);
void usbd_hid_set_protocol(uint8_t busid, uint8_t intf, uint8_t protocol);
#ifdef __cplusplus
}
#endif
#endif /* USBD_HID_H */

View File

@@ -0,0 +1,261 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbh_core.h"
#include "usbh_hid.h"
#undef USB_DBG_TAG
#define USB_DBG_TAG "usbh_hid"
#include "usb_log.h"
#define DEV_FORMAT "/dev/input%d"
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_hid_buf[128];
static struct usbh_hid g_hid_class[CONFIG_USBHOST_MAX_HID_CLASS];
static uint32_t g_devinuse = 0;
static struct usbh_hid *usbh_hid_class_alloc(void)
{
int devno;
for (devno = 0; devno < CONFIG_USBHOST_MAX_HID_CLASS; devno++) {
if ((g_devinuse & (1 << devno)) == 0) {
g_devinuse |= (1 << devno);
memset(&g_hid_class[devno], 0, sizeof(struct usbh_hid));
g_hid_class[devno].minor = devno;
return &g_hid_class[devno];
}
}
return NULL;
}
static void usbh_hid_class_free(struct usbh_hid *hid_class)
{
int devno = hid_class->minor;
if (devno >= 0 && devno < 32) {
g_devinuse &= ~(1 << devno);
}
memset(hid_class, 0, sizeof(struct usbh_hid));
}
static int usbh_hid_get_report_descriptor(struct usbh_hid *hid_class, uint8_t *buffer)
{
struct usb_setup_packet *setup;
int ret;
if (!hid_class || !hid_class->hport) {
return -USB_ERR_INVAL;
}
setup = hid_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = USB_REQUEST_GET_DESCRIPTOR;
setup->wValue = HID_DESCRIPTOR_TYPE_HID_REPORT << 8;
setup->wIndex = hid_class->intf;
setup->wLength = 128;
ret = usbh_control_transfer(hid_class->hport, setup, g_hid_buf);
if (ret < 0) {
return ret;
}
memcpy(buffer, g_hid_buf, ret - 8);
return ret;
}
int usbh_hid_set_idle(struct usbh_hid *hid_class, uint8_t report_id, uint8_t duration)
{
struct usb_setup_packet *setup;
if (!hid_class || !hid_class->hport) {
return -USB_ERR_INVAL;
}
setup = hid_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = HID_REQUEST_SET_IDLE;
setup->wValue = (duration << 8) | report_id;
setup->wIndex = hid_class->intf;
setup->wLength = 0;
return usbh_control_transfer(hid_class->hport, setup, NULL);
}
int usbh_hid_get_idle(struct usbh_hid *hid_class, uint8_t *buffer)
{
struct usb_setup_packet *setup;
int ret;
if (!hid_class || !hid_class->hport) {
return -USB_ERR_INVAL;
}
setup = hid_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = HID_REQUEST_GET_IDLE;
setup->wValue = 0;
setup->wIndex = hid_class->intf;
setup->wLength = 1;
ret = usbh_control_transfer(hid_class->hport, setup, g_hid_buf);
if (ret < 0) {
return ret;
}
memcpy(buffer, g_hid_buf, 1);
return ret;
}
int usbh_hid_set_protocol(struct usbh_hid *hid_class, uint8_t protocol)
{
struct usb_setup_packet *setup;
if (!hid_class || !hid_class->hport) {
return -USB_ERR_INVAL;
}
setup = hid_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = HID_REQUEST_SET_PROTOCOL;
setup->wValue = protocol;
setup->wIndex = 0;
setup->wLength = 0;
return usbh_control_transfer(hid_class->hport, setup, NULL);
}
int usbh_hid_set_report(struct usbh_hid *hid_class, uint8_t report_type, uint8_t report_id, uint8_t *buffer, uint32_t buflen)
{
struct usb_setup_packet *setup;
if (!hid_class || !hid_class->hport) {
return -USB_ERR_INVAL;
}
setup = hid_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = HID_REQUEST_SET_REPORT;
setup->wValue = (uint16_t)(((uint32_t)report_type << 8U) | (uint32_t)report_id);
setup->wIndex = 0;
setup->wLength = buflen;
return usbh_control_transfer(hid_class->hport, setup, buffer);
}
int usbh_hid_get_report(struct usbh_hid *hid_class, uint8_t report_type, uint8_t report_id, uint8_t *buffer, uint32_t buflen)
{
struct usb_setup_packet *setup;
if (!hid_class || !hid_class->hport) {
return -USB_ERR_INVAL;
}
setup = hid_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = HID_REQUEST_GET_REPORT;
setup->wValue = (uint16_t)(((uint32_t)report_type << 8U) | (uint32_t)report_id);
setup->wIndex = 0;
setup->wLength = buflen;
return usbh_control_transfer(hid_class->hport, setup, buffer);
}
int usbh_hid_connect(struct usbh_hubport *hport, uint8_t intf)
{
struct usb_endpoint_descriptor *ep_desc;
int ret;
struct usbh_hid *hid_class = usbh_hid_class_alloc();
if (hid_class == NULL) {
USB_LOG_ERR("Fail to alloc hid_class\r\n");
return -USB_ERR_NOMEM;
}
hid_class->hport = hport;
hid_class->intf = intf;
hport->config.intf[intf].priv = hid_class;
// /* 0x0 = boot protocol, 0x1 = report protocol */
// ret = usbh_hid_set_protocol(hid_class, 0x1);
// if (ret < 0) {
// return ret;
// }
ret = usbh_hid_set_idle(hid_class, 0, 0);
if (ret < 0) {
USB_LOG_WRN("Do not support set idle\r\n");
}
ret = usbh_hid_get_report_descriptor(hid_class, hid_class->report_desc);
if (ret < 0) {
return ret;
}
for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
if (ep_desc->bEndpointAddress & 0x80) {
USBH_EP_INIT(hid_class->intin, ep_desc);
} else {
USBH_EP_INIT(hid_class->intout, ep_desc);
}
}
snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, hid_class->minor);
USB_LOG_INFO("Register HID Class:%s\r\n", hport->config.intf[intf].devname);
usbh_hid_run(hid_class);
return ret;
}
int usbh_hid_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
int ret = 0;
struct usbh_hid *hid_class = (struct usbh_hid *)hport->config.intf[intf].priv;
if (hid_class) {
if (hid_class->intin) {
usbh_kill_urb(&hid_class->intin_urb);
}
if (hid_class->intout) {
usbh_kill_urb(&hid_class->intout_urb);
}
if (hport->config.intf[intf].devname[0] != '\0') {
USB_LOG_INFO("Unregister HID Class:%s\r\n", hport->config.intf[intf].devname);
usbh_hid_stop(hid_class);
}
usbh_hid_class_free(hid_class);
}
return ret;
}
__WEAK void usbh_hid_run(struct usbh_hid *hid_class)
{
}
__WEAK void usbh_hid_stop(struct usbh_hid *hid_class)
{
}
const struct usbh_class_driver hid_class_driver = {
.driver_name = "hid",
.connect = usbh_hid_connect,
.disconnect = usbh_hid_disconnect
};
CLASS_INFO_DEFINE const struct usbh_class_info hid_custom_class_info = {
.match_flags = USB_CLASS_MATCH_INTF_CLASS,
.class = USB_DEVICE_CLASS_HID,
.subclass = 0x00,
.protocol = 0x00,
.id_table = NULL,
.class_driver = &hid_class_driver
};

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_HID_H
#define USBH_HID_H
#include "usb_hid.h"
struct usbh_hid {
struct usbh_hubport *hport;
struct usb_endpoint_descriptor *intin; /* INTR IN endpoint */
struct usb_endpoint_descriptor *intout; /* INTR OUT endpoint */
struct usbh_urb intin_urb; /* INTR IN urb */
struct usbh_urb intout_urb; /* INTR OUT urb */
uint8_t report_desc[256];
uint8_t intf; /* interface number */
uint8_t minor;
void *user_data;
};
#ifdef __cplusplus
extern "C" {
#endif
int usbh_hid_set_idle(struct usbh_hid *hid_class, uint8_t report_id, uint8_t duration);
int usbh_hid_get_idle(struct usbh_hid *hid_class, uint8_t *buffer);
int usbh_hid_set_report(struct usbh_hid *hid_class, uint8_t report_type, uint8_t report_id, uint8_t *buffer, uint32_t buflen);
int usbh_hid_get_report(struct usbh_hid *hid_class, uint8_t report_type, uint8_t report_id, uint8_t *buffer, uint32_t buflen);
void usbh_hid_run(struct usbh_hid *hid_class);
void usbh_hid_stop(struct usbh_hid *hid_class);
#ifdef __cplusplus
}
#endif
#endif /* USBH_HID_H */

View File

@@ -0,0 +1,121 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USB_HUB_H
#define USB_HUB_H
/* HUB Class Descriptor Types */
#define HUB_DESCRIPTOR_TYPE_HUB 0x29
#define HUB_DESCRIPTOR_TYPE_HUB3 0x2A
/* Hub class requests */
#define HUB_REQUEST_GET_STATUS USB_REQUEST_GET_STATUS
#define HUB_REQUEST_CLEAR_FEATURE USB_REQUEST_CLEAR_FEATURE
#define HUB_REQUEST_SET_FEATURE USB_REQUEST_SET_FEATURE
#define HUB_REQUEST_GET_DESCRIPTOR USB_REQUEST_GET_DESCRIPTOR
#define HUB_REQUEST_SET_DESCRIPTOR USB_REQUEST_SET_DESCRIPTOR
#define HUB_REQUEST_CLEAR_TT_BUFFER (0x08)
#define HUB_REQUEST_RESET_TT (0x09)
#define HUB_REQUEST_GET_TT_STATE (0x0a)
#define HUB_REQUEST_STOP_TT (0x0b)
#define HUB_REQUEST_SET_HUB_DEPTH (0x0C)
/* Hub class features */
#define HUB_FEATURE_HUB_C_LOCALPOWER (0x0)
#define HUB_FEATURE_HUB_C_OVERCURRENT (0x1)
/* Port features */
#define HUB_PORT_FEATURE_CONNECTION (0x00)
#define HUB_PORT_FEATURE_ENABLE (0x01)
#define HUB_PORT_FEATURE_SUSPEND (0x02)
#define HUB_PORT_FEATURE_OVERCURRENT (0x03)
#define HUB_PORT_FEATURE_RESET (0x04)
#define HUB_PORT_FEATURE_L1 (0x05)
#define HUB_PORT_FEATURE_POWER (0x08)
#define HUB_PORT_FEATURE_LOWSPEED (0x09)
#define HUB_PORT_FEATURE_HIGHSPEED (0x0a)
#define HUB_PORT_FEATURE_C_CONNECTION (0x10)
#define HUB_PORT_FEATURE_C_ENABLE (0x11)
#define HUB_PORT_FEATURE_C_SUSPEND (0x12)
#define HUB_PORT_FEATURE_C_OVER_CURREN (0x13)
#define HUB_PORT_FEATURE_C_RESET (0x14)
#define HUB_PORT_FEATURE_TEST (0x15)
#define HUB_PORT_FEATURE_INDICATOR (0x16)
#define HUB_PORT_FEATURE_C_PORTL1 (0x17)
/* Hub status */
#define HUB_STATUS_LOCALPOWER (1 << 0)
#define HUB_STATUS_OVERCURRENT (1 << 1)
/* Hub status change */
#define HUB_STATUS_C_LOCALPOWER (1 << 0)
#define HUB_STATUS_C_OVERCURRENT (1 << 1)
/* Hub port status */
#define HUB_PORT_STATUS_CONNECTION (1 << 0)
#define HUB_PORT_STATUS_ENABLE (1 << 1)
#define HUB_PORT_STATUS_SUSPEND (1 << 2)
#define HUB_PORT_STATUS_OVERCURRENT (1 << 3)
#define HUB_PORT_STATUS_RESET (1 << 4)
#define HUB_PORT_STATUS_L1 (1 << 5)
#define HUB_PORT_STATUS_POWER (1 << 8)
#define HUB_PORT_STATUS_LOW_SPEED (1 << 9)
#define HUB_PORT_STATUS_HIGH_SPEED (1 << 10)
#define HUB_PORT_STATUS_TEST (1 << 11)
#define HUB_PORT_STATUS_INDICATOR (1 << 12)
/* Hub port status change */
#define HUB_PORT_STATUS_C_CONNECTION (1 << 0)
#define HUB_PORT_STATUS_C_ENABLE (1 << 1)
#define HUB_PORT_STATUS_C_SUSPEND (1 << 2)
#define HUB_PORT_STATUS_C_OVERCURRENT (1 << 3)
#define HUB_PORT_STATUS_C_RESET (1 << 4)
#define HUB_PORT_STATUS_C_L1 (1 << 5)
/* Hub characteristics */
#define HUB_CHAR_LPSM_SHIFT (0) /* Bits 0-1: Logical Power Switching Mode */
#define HUB_CHAR_LPSM_MASK (3 << HUB_CHAR_LPSM_SHIFT)
#define HUB_CHAR_LPSM_GANGED (0 << HUB_CHAR_LPSM_SHIFT)
#define HUB_CHAR_LPSM_INDIVIDUAL (1 << HUB_CHAR_LPSM_SHIFT)
#define HUB_CHAR_COMPOUND (1 << 2) /* Bit 2: Compound device */
#define HUB_CHAR_OCPM_SHIFT (3) /* Bits 3-4: Over-current Protection Mode */
#define HUB_CHAR_OCPM_MASK (3 << HUB_CHAR_OCPM_SHIFT)
#define HUB_CHAR_OCPM_GLOBAL (0 << HUB_CHAR_OCPM_SHIFT)
#define HUB_CHAR_OCPM_INDIVIDUAL (1 << HUB_CHAR_OCPM_SHIFT)
#define HUB_CHAR_TTTT_SHIFT (5) /* Bits 5-6: TT Think Time */
#define HUB_CHAR_TTTT_MASK (3 << HUB_CHAR_TTTT_SHIFT)
#define HUB_CHAR_TTTT_8_BITS (0 << HUB_CHAR_TTTT_SHIFT)
#define HUB_CHAR_TTTT_16_BITS (1 << HUB_CHAR_TTTT_SHIFT)
#define HUB_CHAR_TTTT_24_BITS (2 << HUB_CHAR_TTTT_SHIFT)
#define HUB_CHAR_TTTT_32_BITS (3 << HUB_CHAR_TTTT_SHIFT)
#define HUB_CHAR_PORTIND (1 << 7) /* Bit 7: Port Indicators Supported */
/* Hub descriptor */
struct usb_hub_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bNbrPorts;
uint16_t wHubCharacteristics;
uint8_t bPwrOn2PwrGood;
uint8_t bHubContrCurrent;
uint8_t DeviceRemovable;
uint8_t PortPwrCtrlMask;
} __PACKED;
#define USB_SIZEOF_HUB_DESC 9
/* Hub status */
struct hub_status {
uint16_t wPortStatus;
uint16_t wPortChange;
};
/* Hub port status */
struct hub_port_status {
uint16_t wPortStatus;
uint16_t wPortChange;
};
#endif /* USB_HUB_H */

View File

@@ -0,0 +1,686 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbh_core.h"
#include "usbh_hub.h"
#undef USB_DBG_TAG
#define USB_DBG_TAG "usbh_hub"
#include "usb_log.h"
#define DEV_FORMAT "/dev/hub%d"
#define HUB_DEBOUNCE_TIMEOUT 1500
#define HUB_DEBOUNCE_STEP 25
#define HUB_DEBOUNCE_STABLE 100
#define DELAY_TIME_AFTER_RESET 200
#define EXTHUB_FIRST_INDEX 2
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_hub_buf[CONFIG_USBHOST_MAX_BUS][USB_ALIGN_UP(32, CONFIG_USB_ALIGN_SIZE)];
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_hub_intbuf[CONFIG_USBHOST_MAX_BUS][CONFIG_USBHOST_MAX_EXTHUBS + 1][USB_ALIGN_UP(1, CONFIG_USB_ALIGN_SIZE)];
extern int usbh_enumerate(struct usbh_hubport *hport);
extern void usbh_hubport_release(struct usbh_hubport *hport);
static const char *speed_table[] = { "error-speed", "low-speed", "full-speed", "high-speed", "wireless-speed", "super-speed", "superplus-speed" };
#if CONFIG_USBHOST_MAX_EXTHUBS > 0
static struct usbh_hub g_hub_class[CONFIG_USBHOST_MAX_EXTHUBS];
static uint32_t g_devinuse = 0;
static struct usbh_hub *usbh_hub_class_alloc(void)
{
int devno;
for (devno = 0; devno < CONFIG_USBHOST_MAX_EXTHUBS; devno++) {
if ((g_devinuse & (1 << devno)) == 0) {
g_devinuse |= (1 << devno);
memset(&g_hub_class[devno], 0, sizeof(struct usbh_hub));
g_hub_class[devno].index = EXTHUB_FIRST_INDEX + devno;
return &g_hub_class[devno];
}
}
return NULL;
}
static void usbh_hub_class_free(struct usbh_hub *hub_class)
{
int devno = hub_class->index - EXTHUB_FIRST_INDEX;
if (devno >= 0 && devno < 32) {
g_devinuse &= ~(1 << devno);
}
memset(hub_class, 0, sizeof(struct usbh_hub));
}
#endif
#if CONFIG_USBHOST_MAX_EXTHUBS > 0
static int _usbh_hub_get_hub_descriptor(struct usbh_hub *hub, uint8_t *buffer)
{
struct usb_setup_packet *setup;
int ret;
setup = hub->parent->setup;
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = USB_REQUEST_GET_DESCRIPTOR;
/* TODO: hub descriptor has some difference between USB 2.0 and USB 3.x,
and we havn't handle the difference here */
if ((hub->parent->speed == USB_SPEED_SUPER) ||
(hub->parent->speed == USB_SPEED_SUPER_PLUS)) {
setup->wValue = HUB_DESCRIPTOR_TYPE_HUB3 << 8;
} else {
setup->wValue = HUB_DESCRIPTOR_TYPE_HUB << 8;
}
setup->wIndex = 0;
setup->wLength = USB_SIZEOF_HUB_DESC;
ret = usbh_control_transfer(hub->parent, setup, g_hub_buf[hub->bus->busid]);
if (ret < 0) {
return ret;
}
memcpy(buffer, g_hub_buf[hub->bus->busid], USB_SIZEOF_HUB_DESC);
return ret;
}
#if 0
static int _usbh_hub_get_status(struct usbh_hub *hub, uint8_t *buffer)
{
struct usb_setup_packet *setup;
int ret;
setup = hub->parent->setup;
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = HUB_REQUEST_GET_STATUS;
setup->wValue = 0;
setup->wIndex = 0;
setup->wLength = 2;
ret = usbh_control_transfer(hub->parent, setup, g_hub_buf[hub->bus->busid]);
if (ret < 0) {
return ret;
}
memcpy(buffer, g_hub_buf[hub->bus->busid], 2);
return ret;
}
#endif
#endif
static int _usbh_hub_get_portstatus(struct usbh_hub *hub, uint8_t port, struct hub_port_status *port_status)
{
struct usb_setup_packet *setup;
int ret;
setup = hub->parent->setup;
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER;
setup->bRequest = HUB_REQUEST_GET_STATUS;
setup->wValue = 0;
setup->wIndex = port;
setup->wLength = 4;
ret = usbh_control_transfer(hub->parent, setup, g_hub_buf[hub->bus->busid]);
if (ret < 0) {
return ret;
}
memcpy(port_status, g_hub_buf[hub->bus->busid], 4);
return ret;
}
static int _usbh_hub_set_feature(struct usbh_hub *hub, uint8_t port, uint8_t feature)
{
struct usb_setup_packet *setup;
setup = hub->parent->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER;
setup->bRequest = HUB_REQUEST_SET_FEATURE;
setup->wValue = feature;
setup->wIndex = port;
setup->wLength = 0;
return usbh_control_transfer(hub->parent, setup, NULL);
}
static int _usbh_hub_clear_feature(struct usbh_hub *hub, uint8_t port, uint8_t feature)
{
struct usb_setup_packet *setup;
setup = hub->parent->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER;
setup->bRequest = HUB_REQUEST_CLEAR_FEATURE;
setup->wValue = feature;
setup->wIndex = port;
setup->wLength = 0;
return usbh_control_transfer(hub->parent, setup, NULL);
}
static int _usbh_hub_set_depth(struct usbh_hub *hub, uint16_t depth)
{
struct usb_setup_packet *setup;
setup = hub->parent->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = HUB_REQUEST_SET_HUB_DEPTH;
setup->wValue = depth;
setup->wIndex = 0;
setup->wLength = 0;
return usbh_control_transfer(hub->parent, setup, NULL);
}
#if CONFIG_USBHOST_MAX_EXTHUBS > 0
static int parse_hub_descriptor(struct usb_hub_descriptor *desc, uint16_t length)
{
if (desc->bLength != USB_SIZEOF_HUB_DESC) {
USB_LOG_ERR("invalid device bLength 0x%02x\r\n", desc->bLength);
return -1;
} else if (desc->bDescriptorType != HUB_DESCRIPTOR_TYPE_HUB) {
USB_LOG_ERR("unexpected descriptor 0x%02x\r\n", desc->bDescriptorType);
return -2;
} else {
USB_LOG_RAW("Hub Descriptor:\r\n");
USB_LOG_RAW("bLength: 0x%02x \r\n", desc->bLength);
USB_LOG_RAW("bDescriptorType: 0x%02x \r\n", desc->bDescriptorType);
USB_LOG_RAW("bNbrPorts: 0x%02x \r\n", desc->bNbrPorts);
USB_LOG_RAW("wHubCharacteristics: 0x%04x \r\n", desc->wHubCharacteristics);
USB_LOG_RAW("bPwrOn2PwrGood: 0x%02x \r\n", desc->bPwrOn2PwrGood);
USB_LOG_RAW("bHubContrCurrent: 0x%02x \r\n", desc->bHubContrCurrent);
USB_LOG_RAW("DeviceRemovable: 0x%02x \r\n", desc->DeviceRemovable);
USB_LOG_RAW("PortPwrCtrlMask: 0x%02x \r\n", desc->PortPwrCtrlMask);
}
return 0;
}
#endif
static int usbh_hub_get_portstatus(struct usbh_hub *hub, uint8_t port, struct hub_port_status *port_status)
{
struct usb_setup_packet roothub_setup;
struct usb_setup_packet *setup;
if (hub->is_roothub) {
setup = &roothub_setup;
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER;
setup->bRequest = HUB_REQUEST_GET_STATUS;
setup->wValue = 0;
setup->wIndex = port;
setup->wLength = 4;
return usbh_roothub_control(hub->bus, &roothub_setup, (uint8_t *)port_status);
} else {
return _usbh_hub_get_portstatus(hub, port, port_status);
}
}
int usbh_hub_set_feature(struct usbh_hub *hub, uint8_t port, uint8_t feature)
{
struct usb_setup_packet roothub_setup;
struct usb_setup_packet *setup;
if (hub->is_roothub) {
setup = &roothub_setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER;
setup->bRequest = HUB_REQUEST_SET_FEATURE;
setup->wValue = feature;
setup->wIndex = port;
setup->wLength = 0;
return usbh_roothub_control(hub->bus, setup, NULL);
} else {
return _usbh_hub_set_feature(hub, port, feature);
}
}
int usbh_hub_clear_feature(struct usbh_hub *hub, uint8_t port, uint8_t feature)
{
struct usb_setup_packet roothub_setup;
struct usb_setup_packet *setup;
if (hub->is_roothub) {
setup = &roothub_setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER;
setup->bRequest = HUB_REQUEST_CLEAR_FEATURE;
setup->wValue = feature;
setup->wIndex = port;
setup->wLength = 0;
return usbh_roothub_control(hub->bus, setup, NULL);
} else {
return _usbh_hub_clear_feature(hub, port, feature);
}
}
static int usbh_hub_set_depth(struct usbh_hub *hub, uint16_t depth)
{
struct usb_setup_packet roothub_setup;
struct usb_setup_packet *setup;
if (hub->is_roothub) {
setup = &roothub_setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = HUB_REQUEST_SET_HUB_DEPTH;
setup->wValue = depth;
setup->wIndex = 0;
setup->wLength = 0;
return usbh_roothub_control(hub->bus, setup, NULL);
} else {
return _usbh_hub_set_depth(hub, depth);
}
}
#if CONFIG_USBHOST_MAX_EXTHUBS > 0
static void hub_int_complete_callback(void *arg, int nbytes)
{
struct usbh_hub *hub = (struct usbh_hub *)arg;
if (nbytes > 0) {
usbh_hub_thread_wakeup(hub);
} else if (nbytes == -USB_ERR_NAK) {
/* Restart timer to submit urb again */
USB_LOG_DBG("Restart timer\r\n");
usb_osal_timer_start(hub->int_timer);
} else {
}
}
static void hub_int_timeout(void *arg)
{
struct usbh_hub *hub = (struct usbh_hub *)arg;
usbh_int_urb_fill(&hub->intin_urb, hub->parent, hub->intin, hub->int_buffer, 1, 0, hub_int_complete_callback, hub);
usbh_submit_urb(&hub->intin_urb);
}
static int usbh_hub_connect(struct usbh_hubport *hport, uint8_t intf)
{
struct usb_endpoint_descriptor *ep_desc;
struct hub_port_status port_status;
int ret;
struct usbh_hub *hub = usbh_hub_class_alloc();
if (hub == NULL) {
USB_LOG_ERR("Fail to alloc hub_class\r\n");
return -USB_ERR_NOMEM;
}
hub->hub_addr = hport->dev_addr;
hub->parent = hport;
hub->bus = hport->bus;
hport->config.intf[intf].priv = hub;
ret = _usbh_hub_get_hub_descriptor(hub, (uint8_t *)&hub->hub_desc);
if (ret < 0) {
return ret;
}
parse_hub_descriptor(&hub->hub_desc, USB_SIZEOF_HUB_DESC);
for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) {
hub->child[port].port = port + 1;
hub->child[port].parent = hub;
hub->child[port].bus = hport->bus;
}
ep_desc = &hport->config.intf[intf].altsetting[0].ep[0].ep_desc;
if (ep_desc->bEndpointAddress & 0x80) {
USBH_EP_INIT(hub->intin, ep_desc);
} else {
return -1;
}
if (hport->speed == USB_SPEED_SUPER) {
uint16_t depth = 0;
struct usbh_hubport *parent = hport->parent->parent;
while (parent) {
depth++;
parent = parent->parent->parent;
}
ret = usbh_hub_set_depth(hub, depth);
if (ret < 0) {
return ret;
}
}
for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) {
ret = usbh_hub_set_feature(hub, port + 1, HUB_PORT_FEATURE_POWER);
if (ret < 0) {
return ret;
}
}
for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) {
ret = usbh_hub_get_portstatus(hub, port + 1, &port_status);
USB_LOG_INFO("port %u, status:0x%02x, change:0x%02x\r\n", port + 1, port_status.wPortStatus, port_status.wPortChange);
if (ret < 0) {
return ret;
}
}
hub->connected = true;
snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, hub->index);
USB_LOG_INFO("Register HUB Class:%s\r\n", hport->config.intf[intf].devname);
hub->int_buffer = g_hub_intbuf[hub->bus->busid][hub->index - 1];
hub->int_timer = usb_osal_timer_create("hubint_tim", USBH_GET_URB_INTERVAL(hub->intin->bInterval, hport->speed), hub_int_timeout, hub, 0);
if (hub->int_timer == NULL) {
USB_LOG_ERR("No memory to alloc int_timer\r\n");
return -USB_ERR_NOMEM;
}
usb_osal_timer_start(hub->int_timer);
return 0;
}
static int usbh_hub_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
struct usbh_hubport *child;
int ret = 0;
struct usbh_hub *hub = (struct usbh_hub *)hport->config.intf[intf].priv;
if (hub) {
if (hub->intin) {
usbh_kill_urb(&hub->intin_urb);
}
if (hub->int_timer) {
usb_osal_timer_delete(hub->int_timer);
}
for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) {
child = &hub->child[port];
usbh_hubport_release(child);
child->parent = NULL;
}
if (hport->config.intf[intf].devname[0] != '\0') {
USB_LOG_INFO("Unregister HUB Class:%s\r\n", hport->config.intf[intf].devname);
}
usbh_hub_class_free(hub);
}
return ret;
}
#endif
static void usbh_hub_events(struct usbh_hub *hub)
{
struct usbh_hubport *child;
struct hub_port_status port_status;
uint8_t portchange_index;
uint16_t portstatus;
uint16_t portchange;
uint16_t mask;
uint16_t feat;
uint8_t speed;
int ret;
size_t flags;
if (!hub->connected) {
return;
}
flags = usb_osal_enter_critical_section();
portchange_index = hub->int_buffer[0];
hub->int_buffer[0] &= ~portchange_index;
usb_osal_leave_critical_section(flags);
for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) {
USB_LOG_DBG("Port change:0x%02x\r\n", portchange_index);
if (!(portchange_index & (1 << (port + 1)))) {
continue;
}
portchange_index &= ~(1 << (port + 1));
USB_LOG_DBG("Port %d change\r\n", port + 1);
/* Read hub port status */
ret = usbh_hub_get_portstatus(hub, port + 1, &port_status);
if (ret < 0) {
USB_LOG_ERR("Failed to read port %u status, errorcode: %d\r\n", port + 1, ret);
continue;
}
portstatus = port_status.wPortStatus;
portchange = port_status.wPortChange;
USB_LOG_DBG("port %u, status:0x%02x, change:0x%02x\r\n", port + 1, portstatus, portchange);
/* First, clear all change bits */
mask = 1;
feat = HUB_PORT_FEATURE_C_CONNECTION;
while (portchange) {
if (portchange & mask) {
ret = usbh_hub_clear_feature(hub, port + 1, feat);
if (ret < 0) {
USB_LOG_ERR("Failed to clear port %u, change mask:%04x, errorcode:%d\r\n", port + 1, mask, ret);
continue;
}
portchange &= (~mask);
}
mask <<= 1;
feat++;
}
portchange = port_status.wPortChange;
/* Second, if port changes, debounces first */
if (portchange & HUB_PORT_STATUS_C_CONNECTION) {
uint16_t connection = 0;
uint16_t debouncestable = 0;
for (uint32_t debouncetime = 0; debouncetime < HUB_DEBOUNCE_TIMEOUT; debouncetime += HUB_DEBOUNCE_STEP) {
/* Read hub port status */
ret = usbh_hub_get_portstatus(hub, port + 1, &port_status);
if (ret < 0) {
USB_LOG_ERR("Failed to read port %u status, errorcode: %d\r\n", port + 1, ret);
continue;
}
portstatus = port_status.wPortStatus;
portchange = port_status.wPortChange;
USB_LOG_DBG("Port %u, status:0x%02x, change:0x%02x\r\n", port + 1, portstatus, portchange);
if (!(portchange & HUB_PORT_STATUS_C_CONNECTION) &&
((portstatus & HUB_PORT_STATUS_CONNECTION) == connection)) {
debouncestable += HUB_DEBOUNCE_STEP;
if (debouncestable >= HUB_DEBOUNCE_STABLE) {
break;
}
} else {
debouncestable = 0;
connection = portstatus & HUB_PORT_STATUS_CONNECTION;
}
if (portchange & HUB_PORT_STATUS_C_CONNECTION) {
usbh_hub_clear_feature(hub, port + 1, HUB_PORT_FEATURE_C_CONNECTION);
}
usb_osal_msleep(HUB_DEBOUNCE_STEP);
}
/** check if debounce ok */
if (debouncestable < HUB_DEBOUNCE_STABLE) {
USB_LOG_ERR("Failed to debounce port %u\r\n", port + 1);
break;
}
/* Last, check connect status */
if (portstatus & HUB_PORT_STATUS_CONNECTION) {
ret = usbh_hub_set_feature(hub, port + 1, HUB_PORT_FEATURE_RESET);
if (ret < 0) {
USB_LOG_ERR("Failed to reset port %u,errorcode:%d\r\n", port, ret);
continue;
}
usb_osal_msleep(DELAY_TIME_AFTER_RESET);
/* Read hub port status */
ret = usbh_hub_get_portstatus(hub, port + 1, &port_status);
if (ret < 0) {
USB_LOG_ERR("Failed to read port %u status, errorcode: %d\r\n", port + 1, ret);
continue;
}
portstatus = port_status.wPortStatus;
portchange = port_status.wPortChange;
if (!(portstatus & HUB_PORT_STATUS_RESET) && (portstatus & HUB_PORT_STATUS_ENABLE)) {
if (portchange & HUB_PORT_STATUS_C_RESET) {
ret = usbh_hub_clear_feature(hub, port + 1, HUB_PORT_FEATURE_C_RESET);
if (ret < 0) {
USB_LOG_ERR("Failed to clear port %u reset change, errorcode: %d\r\n", port, ret);
}
}
if (portstatus & HUB_PORT_STATUS_HIGH_SPEED) {
speed = USB_SPEED_HIGH;
} else if (portstatus & HUB_PORT_STATUS_LOW_SPEED) {
speed = USB_SPEED_LOW;
} else {
speed = USB_SPEED_FULL;
}
child = &hub->child[port];
/** release child sources first */
usbh_hubport_release(child);
memset(child, 0, sizeof(struct usbh_hubport));
child->parent = hub;
child->connected = true;
child->port = port + 1;
child->speed = speed;
child->bus = hub->bus;
child->mutex = usb_osal_mutex_create();
USB_LOG_INFO("New %s device on Bus %u, Hub %u, Port %u connected\r\n", speed_table[speed], hub->bus->busid, hub->index, port + 1);
if (usbh_enumerate(child) < 0) {
/** release child sources */
usbh_hubport_release(child);
USB_LOG_ERR("Port %u enumerate fail\r\n", child->port);
}
} else {
child = &hub->child[port];
/** release child sources */
usbh_hubport_release(child);
/** some USB 3.0 ip may failed to enable USB 2.0 port for USB 3.0 device */
USB_LOG_WRN("Failed to enable port %u\r\n", port + 1);
continue;
}
} else {
child = &hub->child[port];
/** release child sources */
usbh_hubport_release(child);
USB_LOG_INFO("Device on Bus %u, Hub %u, Port %u disconnected\r\n", hub->bus->busid, hub->index, port + 1);
}
}
}
/* Start next hub int transfer */
if (!hub->is_roothub && hub->connected) {
usb_osal_timer_start(hub->int_timer);
}
}
static void usbh_hub_thread(void *argument)
{
struct usbh_hub *hub;
int ret = 0;
struct usbh_bus *bus = (struct usbh_bus *)argument;
usb_hc_init(bus);
while (1) {
ret = usb_osal_mq_recv(bus->hub_mq, (uintptr_t *)&hub, USB_OSAL_WAITING_FOREVER);
if (ret < 0) {
continue;
}
usbh_hub_events(hub);
}
}
void usbh_hub_thread_wakeup(struct usbh_hub *hub)
{
usb_osal_mq_send(hub->bus->hub_mq, (uintptr_t)hub);
}
int usbh_hub_initialize(struct usbh_bus *bus)
{
char thread_name[32] = { 0 };
struct usbh_hub *hub;
hub = &bus->hcd.roothub;
hub->connected = true;
hub->index = 1;
hub->is_roothub = true;
hub->parent = NULL;
hub->hub_addr = 1;
hub->hub_desc.bNbrPorts = CONFIG_USBHOST_MAX_RHPORTS;
hub->int_buffer = bus->hcd.roothub_intbuf;
hub->bus = bus;
bus->hub_mq = usb_osal_mq_create(7);
if (bus->hub_mq == NULL) {
USB_LOG_ERR("Failed to create hub mq\r\n");
return -1;
}
snprintf(thread_name, 32, "usbh_hub%u", bus->busid);
bus->hub_thread = usb_osal_thread_create(thread_name, CONFIG_USBHOST_PSC_STACKSIZE, CONFIG_USBHOST_PSC_PRIO, usbh_hub_thread, bus);
if (bus->hub_thread == NULL) {
USB_LOG_ERR("Failed to create hub thread\r\n");
return -1;
}
return 0;
}
int usbh_hub_deinitialize(struct usbh_bus *bus)
{
struct usbh_hubport *hport;
struct usbh_hub *hub;
size_t flags;
flags = usb_osal_enter_critical_section();
hub = &bus->hcd.roothub;
for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) {
hport = &hub->child[port];
usbh_hubport_release(hport);
}
usb_hc_deinit(bus);
usb_osal_leave_critical_section(flags);
usb_osal_mq_delete(bus->hub_mq);
usb_osal_thread_delete(bus->hub_thread);
return 0;
}
#if CONFIG_USBHOST_MAX_EXTHUBS > 0
const struct usbh_class_driver hub_class_driver = {
.driver_name = "hub",
.connect = usbh_hub_connect,
.disconnect = usbh_hub_disconnect
};
CLASS_INFO_DEFINE const struct usbh_class_info hub_class_info = {
.match_flags = USB_CLASS_MATCH_INTF_CLASS,
.class = USB_DEVICE_CLASS_HUB,
.subclass = 0,
.protocol = 0,
.id_table = NULL,
.class_driver = &hub_class_driver
};
#endif

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_HUB_H
#define USBH_HUB_H
#include "usb_hub.h"
struct usbh_hub;
#define USBH_HUB_MAX_PORTS 4
/* Maximum size of an interrupt IN transfer */
#define USBH_HUB_INTIN_BUFSIZE ((USBH_HUB_MAX_PORTS + 8) >> 3)
#ifdef __cplusplus
extern "C" {
#endif
int usbh_hub_set_feature(struct usbh_hub *hub, uint8_t port, uint8_t feature);
int usbh_hub_clear_feature(struct usbh_hub *hub, uint8_t port, uint8_t feature);
void usbh_hub_thread_wakeup(struct usbh_hub *hub);
int usbh_hub_initialize(struct usbh_bus *bus);
int usbh_hub_deinitialize(struct usbh_bus *bus);
#ifdef __cplusplus
}
#endif
#endif /* USBH_HUB_H */

View File

@@ -0,0 +1,240 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USB_MIDI_H
#define USB_MIDI_H
/* bDescriptorSubType */
#define MIDI_VC_HEADER_DESCRIPTOR_SUBTYPE 0x01U
#define MIDI_MS_HEADER_DESCRIPTOR_SUBTYPE 0x01U
#define MIDI_MS_GENERAL_DESCRIPTOR_SUBTYPE 0x01U
#define MIDI_MIDI_IN_JACK_DESCRIPTOR_SUBTYPE 0x02U
#define MIDI_MIDI_OUT_JACK_DESCRIPTOR_SUBTYPE 0x03U
/* bJackType */
#define MIDI_JACK_TYPE_EMBEDDED 0x01
#define MIDI_JACK_TYPE_EXTERNAL 0x02
#define MIDI_CHANNEL_OMNI 0
#define MIDI_CHANNEL_OFF 17
#define MIDI_PITCHBEND_MIN -8192
#define MIDI_PITCHBEND_MAX 8191
/*! Enumeration of MIDI code index number */
enum MidiCodeIndexNumber {
MIDI_CIN_MISC = 0,
MIDI_CIN_CABLE_EVENT = 1,
MIDI_CIN_SYSCOM_2BYTE = 2, ///< 2 byte system common message e.g MTC, SongSelect
MIDI_CIN_SYSCOM_3BYTE = 3, ///< 3 byte system common message e.g SPP
MIDI_CIN_SYSEX_START = 4, ///< SysEx starts or continue
MIDI_CIN_SYSEX_END_1BYTE = 5, ///< SysEx ends with 1 data, or 1 byte system common message
MIDI_CIN_SYSEX_END_2BYTE = 6, ///< SysEx ends with 2 data
MIDI_CIN_SYSEX_END_3BYTE = 7, ///< SysEx ends with 3 data
MIDI_CIN_NOTE_OFF = 8,
MIDI_CIN_NOTE_ON = 9,
MIDI_CIN_POLY_KEYPRESS = 10,
MIDI_CIN_CONTROL_CHANGE = 11,
MIDI_CIN_PROGRAM_CHANGE = 12,
MIDI_CIN_CHANNEL_PRESSURE = 13,
MIDI_CIN_PITCH_BEND_CHANGE = 14,
MIDI_CIN_1BYTE_DATA = 15
};
/*! Enumeration of MIDI types */
enum MidiType {
InvalidType = 0x00, ///< For notifying errors
NoteOff = 0x80, ///< Note Off
NoteOn = 0x90, ///< Note On
AfterTouchPoly = 0xA0, ///< Polyphonic AfterTouch
ControlChange = 0xB0, ///< Control Change / Channel Mode
ProgramChange = 0xC0, ///< Program Change
AfterTouchChannel = 0xD0, ///< Channel (monophonic) AfterTouch
PitchBend = 0xE0, ///< Pitch Bend
SystemExclusive = 0xF0, ///< System Exclusive
TimeCodeQuarterFrame = 0xF1, ///< System Common - MIDI Time Code Quarter Frame
SongPosition = 0xF2, ///< System Common - Song Position Pointer
SongSelect = 0xF3, ///< System Common - Song Select
TuneRequest = 0xF6, ///< System Common - Tune Request
Clock = 0xF8, ///< System Real Time - Timing Clock
Start = 0xFA, ///< System Real Time - Start
Continue = 0xFB, ///< System Real Time - Continue
Stop = 0xFC, ///< System Real Time - Stop
ActiveSensing = 0xFE, ///< System Real Time - Active Sensing
SystemReset = 0xFF, ///< System Real Time - System Reset
};
/*! Enumeration of Thru filter modes */
enum MidiFilterMode {
Off = 0, ///< Thru disabled (nothing passes through).
Full = 1, ///< Fully enabled Thru (every incoming message is sent back).
SameChannel = 2, ///< Only the messages on the Input Channel will be sent back.
DifferentChannel = 3, ///< All the messages but the ones on the Input Channel will be sent back.
};
/*! \brief Enumeration of Control Change command numbers.
See the detailed controllers numbers & description here:
http://www.somascape.org/midi/tech/spec.html#ctrlnums
*/
enum MidiControlChangeNumber {
// High resolution Continuous Controllers MSB (+32 for LSB) ----------------
BankSelect = 0,
ModulationWheel = 1,
BreathController = 2,
// CC3 undefined
FootController = 4,
PortamentoTime = 5,
DataEntry = 6,
ChannelVolume = 7,
Balance = 8,
// CC9 undefined
Pan = 10,
ExpressionController = 11,
EffectControl1 = 12,
EffectControl2 = 13,
// CC14 undefined
// CC15 undefined
GeneralPurposeController1 = 16,
GeneralPurposeController2 = 17,
GeneralPurposeController3 = 18,
GeneralPurposeController4 = 19,
// Switches ----------------------------------------------------------------
Sustain = 64,
Portamento = 65,
Sostenuto = 66,
SoftPedal = 67,
Legato = 68,
Hold = 69,
// Low resolution continuous controllers -----------------------------------
SoundController1 = 70, ///< Synth: Sound Variation FX: Exciter On/Off
SoundController2 = 71, ///< Synth: Harmonic Content FX: Compressor On/Off
SoundController3 = 72, ///< Synth: Release Time FX: Distortion On/Off
SoundController4 = 73, ///< Synth: Attack Time FX: EQ On/Off
SoundController5 = 74, ///< Synth: Brightness FX: Expander On/Off
SoundController6 = 75, ///< Synth: Decay Time FX: Reverb On/Off
SoundController7 = 76, ///< Synth: Vibrato Rate FX: Delay On/Off
SoundController8 = 77, ///< Synth: Vibrato Depth FX: Pitch Transpose On/Off
SoundController9 = 78, ///< Synth: Vibrato Delay FX: Flange/Chorus On/Off
SoundController10 = 79, ///< Synth: Undefined FX: Special Effects On/Off
GeneralPurposeController5 = 80,
GeneralPurposeController6 = 81,
GeneralPurposeController7 = 82,
GeneralPurposeController8 = 83,
PortamentoControl = 84,
// CC85 to CC90 undefined
Effects1 = 91, ///< Reverb send level
Effects2 = 92, ///< Tremolo depth
Effects3 = 93, ///< Chorus send level
Effects4 = 94, ///< Celeste depth
Effects5 = 95, ///< Phaser depth
// Channel Mode messages ---------------------------------------------------
AllSoundOff = 120,
ResetAllControllers = 121,
LocalControl = 122,
AllNotesOff = 123,
OmniModeOff = 124,
OmniModeOn = 125,
MonoModeOn = 126,
PolyModeOn = 127
};
struct midi_cs_if_ac_header_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint16_t bcdADC;
uint16_t wTotalLength;
uint8_t bInCollection;
uint8_t baInterfaceNr[];
} __PACKED;
#define MIDI_SIZEOF_AC_HEADER_DESC(n) (8 + n)
struct midi_cs_if_ms_header_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint16_t bcdMSC;
uint16_t wTotalLength;
} __PACKED;
#define MIDI_SIZEOF_MS_HEADER_DESC (7)
struct midi_cs_if_in_jack_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint8_t bJackType;
uint8_t bJackId;
uint8_t iJack;
} __PACKED;
#define MIDI_SIZEOF_IN_JACK_DESC (6)
struct midi_cs_if_out_jack_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint8_t bJackType;
uint8_t bJackId;
uint8_t bNrInputPins;
uint8_t baSourceId;
uint8_t baSourcePin;
uint8_t iJack;
} __PACKED;
#define MIDI_SIZEOF_OUT_JACK_DESC (9)
struct midi_cs_ep_ms_general_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint8_t bNumEmbMIDIJack;
uint8_t baAssocJackID[];
} __PACKED;
#define MIDI_SIZEOF_MS_GENERAL_DESC(n) (4 + n)
// clang-format off
#define MIDI_CS_HEADER_DESCRIPTOR_INIT(wTotalLength) \
0x07, /* bLength */ \
USB_CS_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
MIDI_MS_HEADER_DESCRIPTOR_SUBTYPE, /* bDescriptorSubtype */ \
WBVAL(0x0100), /* bcdMSC */ \
WBVAL(wTotalLength) /* wTotalLength */
#define MIDI_IN_JACK_DESCRIPTOR_INIT(bJackType, bJackID) \
0x06, \
0x24, \
MIDI_MIDI_IN_JACK_DESCRIPTOR_SUBTYPE, \
bJackType, \
bJackID, \
0x00
#define MIDI_OUT_JACK_DESCRIPTOR_INIT(bJackType, bJackID, baSourceID) \
0x09, \
0x24, \
MIDI_MIDI_OUT_JACK_DESCRIPTOR_SUBTYPE, \
bJackType, \
bJackID, \
0x01, \
baSourceID, \
0x01, \
0x00
#define MIDI_JACK_DESCRIPTOR_INIT(bJackFirstID) \
MIDI_IN_JACK_DESCRIPTOR_INIT(MIDI_JACK_TYPE_EMBEDDED, bJackFirstID), \
MIDI_IN_JACK_DESCRIPTOR_INIT(MIDI_JACK_TYPE_EXTERNAL, (bJackFirstID + 1)), \
MIDI_OUT_JACK_DESCRIPTOR_INIT(MIDI_JACK_TYPE_EMBEDDED, (bJackFirstID + 2), (bJackFirstID + 1)), \
MIDI_OUT_JACK_DESCRIPTOR_INIT(MIDI_JACK_TYPE_EXTERNAL, (bJackFirstID + 3), (bJackFirstID))
#define MIDI_SIZEOF_JACK_DESC (6 + 6 + 9 + 9)
// clang-format on
#endif /* USB_MIDI_H */

View File

@@ -0,0 +1,89 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USB_MSC_H
#define USB_MSC_H
/* MSC Subclass Codes */
#define MSC_SUBCLASS_RBC 0x01 /* Reduced block commands (e.g., flash devices) */
#define MSC_SUBCLASS_SFF8020I_MMC2 0x02 /* SFF-8020i/MMC-2 (ATAPI) (e.g., C/DVD) */
#define MSC_SUBCLASS_QIC157 0x03 /* QIC-157 (e.g., tape device) */
#define MSC_SUBCLASS_UFI 0x04 /* e.g. floppy device */
#define MSC_SUBCLASS_SFF8070I 0x05 /* SFF-8070i (e.g. floppy disk) */
#define MSC_SUBCLASS_SCSI 0x06 /* SCSI transparent */
/* MSC Protocol Codes */
#define MSC_PROTOCOL_CBI_INT 0x00 /* CBI transport with command completion interrupt */
#define MSC_PROTOCOL_CBI_NOINT 0x01 /* CBI transport without command completion interrupt */
#define MSC_PROTOCOL_BULK_ONLY 0x50 /* Bulk only transport */
/* MSC Request Codes */
#define MSC_REQUEST_RESET 0xFF
#define MSC_REQUEST_GET_MAX_LUN 0xFE
/** MSC Command Block Wrapper (CBW) Signature */
#define MSC_CBW_Signature 0x43425355
/** Bulk-only Command Status Wrapper (CSW) Signature */
#define MSC_CSW_Signature 0x53425355
/** MSC Command Block Status Values */
#define CSW_STATUS_CMD_PASSED 0x00
#define CSW_STATUS_CMD_FAILED 0x01
#define CSW_STATUS_PHASE_ERROR 0x02
#define MSC_MAX_CDB_LEN (16) /* Max length of SCSI Command Data Block */
/** MSC Bulk-Only Command Block Wrapper (CBW) */
struct CBW {
uint32_t dSignature; /* 'USBC' = 0x43425355 */
uint32_t dTag; /* Depends on command id */
uint32_t dDataLength; /* Number of bytes that host expects to transfer */
uint8_t bmFlags; /* Bit 7: Direction=IN (other obsolete or reserved) */
uint8_t bLUN; /* LUN (normally 0) */
uint8_t bCBLength; /* len of cdb[] */
uint8_t CB[MSC_MAX_CDB_LEN]; /* Command Data Block */
} __PACKED;
#define USB_SIZEOF_MSC_CBW 31
/** MSC Bulk-Only Command Status Wrapper (CSW) */
struct CSW {
uint32_t dSignature; /* 'USBS' = 0x53425355 */
uint32_t dTag; /* Same tag as original command */
uint32_t dDataResidue; /* Amount not transferred */
uint8_t bStatus; /* Status of transfer */
} __PACKED;
#define USB_SIZEOF_MSC_CSW 13
/*Length of template descriptor: 23 bytes*/
#define MSC_DESCRIPTOR_LEN (9 + 7 + 7)
// clang-format off
#define MSC_DESCRIPTOR_INIT(bFirstInterface, out_ep, in_ep, wMaxPacketSize, str_idx) \
/* Interface */ \
0x09, /* bLength */ \
USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
bFirstInterface, /* bInterfaceNumber */ \
0x00, /* bAlternateSetting */ \
0x02, /* bNumEndpoints */ \
USB_DEVICE_CLASS_MASS_STORAGE, /* bInterfaceClass */ \
MSC_SUBCLASS_SCSI, /* bInterfaceSubClass */ \
MSC_PROTOCOL_BULK_ONLY, /* bInterfaceProtocol */ \
str_idx, /* iInterface */ \
0x07, /* bLength */ \
USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
out_ep, /* bEndpointAddress */ \
0x02, /* bmAttributes */ \
WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
0x00, /* bInterval */ \
0x07, /* bLength */ \
USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
in_ep, /* bEndpointAddress */ \
0x02, /* bmAttributes */ \
WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
0x00 /* bInterval */
// clang-format on
#endif /* USB_MSC_H */

View File

@@ -0,0 +1,972 @@
/*
* Apache NuttX
* Copyright 2020 The Apache Software Foundation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __INCLUDE_NUTTX_SCSI_H
#define __INCLUDE_NUTTX_SCSI_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <stdint.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* SCSI commands ************************************************************/
#define SCSI_CMD_TESTUNITREADY 0x00
#define SCSI_CMD_REZEROUNIT 0x01
#define SCSI_CMD_REQUESTSENSE 0x03
#define SCSI_CMD_FORMAT_UNIT 0x04
#define SCSI_CMD_REASSIGNBLOCKS 0x07
#define SCSI_CMD_READ6 0x08
#define SCSI_CMD_WRITE6 0x0a
#define SCSI_CMD_SEEK6 0x0b
#define SCSI_CMD_SPACE6 0x11
#define SCSI_CMD_INQUIRY 0x12
#define SCSI_CMD_MODESELECT6 0x15
#define SCSI_CMD_RESERVE6 0x16
#define SCSI_CMD_RELEASE6 0x17
#define SCSI_CMD_COPY 0x18
#define SCSI_CMD_MODESENSE6 0x1a
#define SCSI_CMD_STARTSTOPUNIT 0x1b
#define SCSI_CMD_RECEIVEDIAGNOSTICRESULTS 0x1c
#define SCSI_CMD_SENDDIAGNOSTIC 0x1d
#define SCSI_CMD_PREVENTMEDIAREMOVAL 0x1e
#define SCSI_CMD_READFORMATCAPACITIES 0x23
#define SCSI_CMD_READCAPACITY10 0x25
#define SCSI_CMD_READ10 0x28
#define SCSI_CMD_WRITE10 0x2a
#define SCSI_CMD_SEEK10 0x2b
#define SCSI_CMD_WRITEANDVERIFY 0x2e
#define SCSI_CMD_VERIFY10 0x2f
#define SCSI_CMD_SEARCHDATAHIGH 0x30
#define SCSI_CMD_SEARCHDATAEQUAL 0x31
#define SCSI_CMD_SEARCHDATALOW 0x32
#define SCSI_CMD_SETLIMITS10 0x33
#define SCSI_CMD_PREFETCH10 0x34
#define SCSI_CMD_SYNCHCACHE10 0x35
#define SCSI_CMD_LOCKCACHE 0x36
#define SCSI_CMD_READDEFECTDATA10 0x37
#define SCSI_CMD_COMPARE 0x39
#define SCSI_CMD_COPYANDVERIFY 0x3a
#define SCSI_CMD_WRITEBUFFER 0x3b
#define SCSI_CMD_READBUFFER 0x3c
#define SCSI_CMD_READLONG10 0x3e
#define SCSI_CMD_WRITELONG10 0x3f
#define SCSI_CMD_CHANGEDEFINITION 0x40
#define SCSI_CMD_WRITESAME10 0x41
#define SCSI_CMD_LOGSELECT 0x4c
#define SCSI_CMD_LOGSENSE 0x4d
#define SCSI_CMD_XDWRITE10 0x50
#define SCSI_CMD_XPWRITE10 0x51
#define SCSI_CMD_XDREAD10 0x52
#define SCSI_CMD_MODESELECT10 0x55
#define SCSI_CMD_RESERVE10 0x56
#define SCSI_CMD_RELEASE10 0x57
#define SCSI_CMD_MODESENSE10 0x5a
#define SCSI_CMD_PERSISTENTRESERVEIN 0x5e
#define SCSI_CMD_PERSISTENTRESERVEOUT 0x5f
#define SCSI_CMD_32 0x7f
#define SCSI_CMD_XDWRITEEXTENDED 0x80
#define SCSI_CMD_REBUILD 0x82
#define SCSI_CMD_REGENERATE 0x82
#define SCSI_CMD_EXTENDEDCOPY 0x83
#define SCSI_CMD_COPYRESULTS 0x84
#define SCSI_CMD_ACCESSCONTROLIN 0x86
#define SCSI_CMD_ACCESSCONTROLOUT 0x87
#define SCSI_CMD_READ16 0x88
#define SCSI_CMD_WRITE16 0x8a
#define SCSI_CMD_READATTRIBUTE 0x8c
#define SCSI_CMD_WRITEATTRIBUTE 0x8d
#define SCSI_CMD_WRITEANDVERIFY16 0x8e
#define SCSI_CMD_PREFETCH16 0x90
#define SCSI_CMD_SYNCHCACHE16 0x91
#define SCSI_CMD_LOCKUNLOCKACACHE 0x92
#define SCSI_CMD_WRITESAME16 0x93
#define SCSI_CMD_READCAPACITY16 0x9e
#define SCSI_CMD_READLONG16 0x9e
#define SCSI_CMD_WRITELONG106 0x9f
#define SCSI_CMD_REPORTLUNS 0xa0
#define SCSI_CMD_MAINTENANCEIN 0xa3
#define SCSI_CMD_MAINTENANCEOUT 0xa4
#define SCSI_CMD_MOVEMEDIUM 0xa5
#define SCSI_CMD_MOVEMEDIUMATTACHED 0xa7
#define SCSI_CMD_READ12 0xa8
#define SCSI_CMD_WRITE12 0xaa
#define SCSI_CMD_READMEDIASERIALNUMBER 0xab
#define SCSI_CMD_WRITEANDVERIFY12 0xae
#define SCSI_CMD_VERIFY12 0xaf
#define SCSI_CMD_SETLIMITS12 0xb3
#define SCSI_CMD_READELEMENTSTATUS 0xb4
#define SCSI_CMD_READDEFECTDATA12 0xb7
#define SCSI_CMD_REDUNDANCYGROUPIN 0xba
#define SCSI_CMD_REDUNDANCYGROUPOUT 0xbb
#define SCSI_CMD_SPAREIN 0xbc
#define SCSI_CMD_SPAREOUT 0xbd
#define SCSI_CMD_VOLUMESETIN 0xbe
#define SCSI_CMD_VOLUMESETOUT 0xbf
/* Common SCSI KCQ values (sense Key/additional sense Code/ASC Qualifier) ***
*
* 0xnn0386 Write Fault Data Corruption
* 0xnn0500 Illegal request
* 0xnn0600 Unit attention
* 0xnn0700 Data protect
* 0xnn0800 LUN communication failure
* 0xnn0801 LUN communication timeout
* 0xnn0802 LUN communication parity error
* 0xnn0803 LUN communication CRC error
* 0xnn0900 vendor specific sense key
* 0xnn0901 servo fault
* 0xnn0904 head select fault
* 0xnn0a00 error log overflow
* 0xnn0b00 aborted command
* 0xnn0c00 write error
* 0xnn0c02 write error - auto-realloc failed
* 0xnn0e00 data miscompare
* 0xnn1200 address mark not founf for ID field
* 0xnn1400 logical block not found
* 0xnn1500 random positioning error
* 0xnn1501 mechanical positioning error
* 0xnn1502 positioning error detected by read of medium
* 0xnn2700 write protected
* 0xnn2900 POR or bus reset occurred
* 0xnn3101 format failed
* 0xnn3191 format corrupted
* 0xnn3201 defect list update error
* 0xnn3202 no spares available
* 0xnn3501 unspecified enclosure services failure
* 0xnn3700 parameter rounded
* 0xnn3d00 invalid bits in identify message
* 0xnn3e00 LUN not self-configured yet
* 0xnn4001 DRAM parity error
* 0xnn4002 DRAM parity error
* 0xnn4200 power-on or self-test failure
* 0xnn4c00 LUN failed self-configuration
* 0xnn5c00 RPL status change
* 0xnn5c01 spindles synchronized
* 0xnn5c02 spindles not synchronized
* 0xnn6500 voltage fault
* 0xnn8000 general firmware error
*/
/* No sense KCQ values */
#define SCSI_KCQ_NOSENSE 0x000000 /* No error */
#define SCSI_KCQ_PFATHRESHOLDREACHED 0x005c00 /* No sense - PFA threshold reached */
/* Soft error KCQ values */
#define SCSI_KCQSE_RWENOINDEX 0x010100 /* Recovered Write error - no index */
#define SCSI_KCQSE_RECOVEREDNOSEEKCOMPLETION 0x010200 /* Recovered no seek completion */
#define SCSI_KCQSE_RWEWRITEFAULT 0x010300 /* Recovered Write error - write fault */
#define SCSI_KCQSE_TRACKFOLLOWINGERROR 0x010900 /* Track following error */
#define SCSI_KCQSE_TEMPERATUREWARNING 0x010b01 /* Temperature warning */
#define SCSI_KCQSE_RWEWARREALLOCATED 0x010c01 /* Recovered Write error with auto-realloc - reallocated */
#define SCSI_KCQSE_RWERECOMMENDREASSIGN 0x010c03 /* Recovered Write error - recommend reassign */
#define SCSI_KCQSE_RDWOEUSINGPREVLBI 0x011201 /* Recovered data without ECC using prev logical block ID */
#define SCSI_KCQSE_RDWEUSINGPREVLBI 0x011202 /* Recovered data with ECC using prev logical block ID */
#define SCSI_KCQSE_RECOVEREDRECORDNOTFOUND 0x011401 /* Recovered Record Not Found */
#define SCSI_KCQSE_RWEDSME 0x011600 /* Recovered Write error - Data Sync Mark Error */
#define SCSI_KCQSE_RWEDSEDATAREWRITTEN 0x011601 /* Recovered Write error - Data Sync Error - data rewritten */
#define SCSI_KCQSE_RWEDSERECOMMENDREWRITE 0x011602 /* Recovered Write error - Data Sync Error - recommend rewrite */
#define SCSI_KCQSE_RWEDSEDATAAUTOREALLOCATED 0x011603 /* Recovered Write error - Data Sync Error - data auto-reallocated */
#define SCSI_KCQSE_RWEDSERECOMMENDREASSIGNMENT 0x011604 /* Recovered Write error - Data Sync Error - recommend reassignment */
#define SCSI_KCQSE_RDWNECORRECTIONAPPLIED 0x011700 /* Recovered data with no error correction applied */
#define SCSI_KCQSE_RREWITHRETRIES 0x011701 /* Recovered Read error - with retries */
#define SCSI_KCQSE_RDUSINGPOSITIVEOFFSET 0x011702 /* Recovered data using positive offset */
#define SCSI_KCQSE_RDUSINGNEGATIVEOFFSET 0x011703 /* Recovered data using negative offset */
#define SCSI_KCQSE_RDUSINGPREVIOUSLBI 0x011705 /* Recovered data using previous logical block ID */
#define SCSI_KCQSE_RREWOEAUTOREALLOCATED 0x011706 /* Recovered Read error - without ECC, auto reallocated */
#define SCSI_KCQSE_RREWOERECOMMENDREASSIGN 0x011707 /* Recovered Read error - without ECC, recommend reassign */
#define SCSI_KCQSE_RREWOERECOMMENDREWRITE 0x011708 /* Recovered Read error - without ECC, recommend rewrite */
#define SCSI_KCQSE_RREWOEDATAREWRITTEN 0x011709 /* Recovered Read error - without ECC, data rewritten */
#define SCSI_KCQSE_RREWE 0x011800 /* Recovered Read error - with ECC */
#define SCSI_KCQSE_RDWEANDRETRIES 0x011801 /* Recovered data with ECC and retries */
#define SCSI_KCQSE_RREWEAUTOREALLOCATED 0x011802 /* Recovered Read error - with ECC, auto reallocated */
#define SCSI_KCQSE_RREWERECOMMENDREASSIGN 0x011805 /* Recovered Read error - with ECC, recommend reassign */
#define SCSI_KCQSE_RDUSINGECCANDOFFSETS 0x011806 /* Recovered data using ECC and offsets */
#define SCSI_KCQSE_RREWEDATAREWRITTEN 0x011807 /* Recovered Read error - with ECC, data rewritten */
#define SCSI_KCQSE_DLNOTFOUND 0x011c00 /* Defect List not found */
#define SCSI_KCQSE_PRIMARYDLNOTFOUND 0x011c01 /* Primary defect list not found */
#define SCSI_KCQSE_GROWNDLNOTFOUND 0x011c02 /* Grown defect list not found */
#define SCSI_KCQSE_PARTIALDLTRANSFERRED 0x011f00 /* Partial defect list transferred */
#define SCSI_KCQSE_INTERNALTARGETFAILURE 0x014400 /* Internal target failure */
#define SCSI_KCQSE_PFATHRESHOLDREACHED 0x015d00 /* PFA threshold reached */
#define SCSI_KCQSE_PFATESTWARNING 0x015dff /* PFA test warning */
#define SCSI_KCQSE_INTERNALLOGICFAILURE 0x018100 /* Internal logic failure */
/* Not Ready / Diagnostic Failure KCQ values */
#define SCSI_KCQNR_CAUSENOTREPORTABLE 0x020400 /* Not Ready - Cause not reportable. */
#define SCSI_KCQNR_BECOMINGREADY 0x020401 /* Not Ready - becoming ready */
#define SCSI_KCQNR_NEEDINITIALIZECOMMAND 0x020402 /* Not Ready - need initialize command (start unit) */
#define SCSI_KCQNR_MANUALINTERVENTIONREQUIRED 0x020403 /* Not Ready - manual intervention required */
#define SCSI_KCQNR_FORMATINPROGRESS 0x020404 /* Not Ready - format in progress */
#define SCSI_KCQNR_SELFTESTINPROGRESS 0x020409 /* Not Ready - self-test in progress */
#define SCSI_KCQNR_MEDIUMFORMATCORRUPTED 0x023100 /* Not Ready - medium format corrupted */
#define SCSI_KCQNR_FORMATCOMMANDFAILED 0x023101 /* Not Ready - format command failed */
#define SCSI_KCQNR_ESUNAVAILABLE 0x023502 /* Not Ready - enclosure services unavailable */
#define SCSI_KCQNR_MEDIANOTPRESENT 0x023a00 /* Not Ready - media not present */
#define SCSI_KCQDF_BRINGUPFAILORDEGRADEDMODE 0x024080 /* Diagnostic Failure - bring-up fail or degraded mode */
#define SCSI_KCQDF_HARDDISKCONTROLLER 0x024081 /* Diagnostic Failure - Hard Disk Controller */
#define SCSI_KCQDF_RAMMICROCODENOTLOADED 0x024085 /* Diagnostic Failure - RAM microcode not loaded */
#define SCSI_KCQDF_RROCALIBRATION 0x024090 /* Diagnostic Failure - RRO Calibration */
#define SCSI_KCQDF_CHANNELCALIBRATION 0x024091 /* Diagnostic Failure - Channel Calibration */
#define SCSI_KCQDF_HEADLOAD 0x024092 /* Diagnostic Failure - Head Load */
#define SCSI_KCQDF_WRITEAE 0x024093 /* Diagnostic Failure - Write AE */
#define SCSI_KCQDF_12VOVERCURRENT 0x024094 /* Diagnostic Failure - 12V over current */
#define SCSI_KCQDF_OTHERSPINDLEFAILURE 0x024095 /* Diagnostic Failure - Other spindle failure */
#define SCSI_KCQDF_SELFRESET 0x0240b0 /* Diagnostic Failure - self-reset */
#define SCSI_KCQDF_CONFIGNOTLOADED 0x024c00 /* Diagnostic Failure - config not loaded */
/* Medium error KCQ values */
#define SCSI_KCQME_WRITEFAULT 0x030300 /* Medium Error - write fault */
#define SCSI_KCQME_WRITEFAULTAUTOREALLOCFAILED 0x030c02 /* Medium Error - write error - auto-realloc failed */
#define SCSI_KCQME_WRITERTLIMITEXCEEDED 0x030cbb /* Medium Error - write recovery time limit exceeded */
#define SCSI_KCQME_IDCRCERROR 0x031000 /* Medium Error - ID CRC error */
#define SCSI_KCQME_UNRRE1 0x031100 /* Medium Error - unrecovered read error */
#define SCSI_KCQME_READRETRIESEXHAUSTED 0x031101 /* Medium Error - read retries exhausted */
#define SCSI_KCQME_ERRORTOOLONGTOCORRECT 0x031102 /* Medium Error - error too long to correct */
#define SCSI_KCQME_UREAUTOREALLOCFAILED 0x031104 /* Medium Error - unrecovered read error - auto re-alloc failed */
#define SCSI_KCQME_URERECOMMENDREASSIGN 0x03110b /* Medium Error - unrecovered read error - recommend reassign */
#define SCSI_KCQME_READRTLIMITEXCEEDED 0x0311ff /* Medium Error - read recovery time limit exceeded */
#define SCSI_KCQME_RECORDNOTFOUND 0x031401 /* Medium Error - record not found */
#define SCSI_KCQME_DSME 0x031600 /* Medium Error - Data Sync Mark error */
#define SCSI_KCQME_DSERECOMMENDREASSIGN 0x031604 /* Medium Error - Data Sync Error - recommend reassign */
#define SCSI_KCQME_DLE 0x031900 /* Medium Error - defect list error */
#define SCSI_KCQME_DLNOTAVAILABLE 0x031901 /* Medium Error - defect list not available */
#define SCSI_KCQME_DLEINPRIMARYLIST 0x031902 /* Medium Error - defect list error in primary list */
#define SCSI_KCQME_DLEINGROWNLIST 0x031903 /* Medium Error - defect list error in grown list */
#define SCSI_KCQME_FEWERTHAN50PCTDLCOPIES 0x03190e /* Medium Error - fewer than 50% defect list copies */
#define SCSI_KCQME_MEDIUMFORMATCORRUPTED 0x033100 /* Medium Error - medium format corrupted */
#define SCSI_KCQME_FORMATCOMMANDFAILED 0x033101 /* Medium Error - format command failed */
#define SCSI_KCQME_DATAAUTOREALLOCATED 0x038000 /* Medium Error - data auto-reallocated */
/* Hardware Error KCQ values */
#define SCSI_KCQHE_NOINDEXORSECTOR 0x040100 /* Hardware Error - no index or sector */
#define SCSI_KCQHE_NOSEEKCOMPLETE 0x040200 /* Hardware Error - no seek complete */
#define SCSI_KCQHE_WRITEFAULT 0x040300 /* Hardware Error - write fault */
#define SCSI_KCQHE_COMMUNICATIONFAILURE 0x040800 /* Hardware Error - communication failure */
#define SCSI_KCQHE_TRACKFOLLOWINGERROR 0x040900 /* Hardware Error - track following error */
#define SCSI_KCQHE_UREINRESERVEDAREA 0x041100 /* Hardware Error - unrecovered read error in reserved area */
#define SCSI_KCQHE_DSMEINRESERVEDAREA 0x041600 /* Hardware Error - Data Sync Mark error in reserved area */
#define SCSI_KCQHE_DLE 0x041900 /* Hardware Error - defect list error */
#define SCSI_KCQHE_DLEINPRIMARYLIST 0x041902 /* Hardware Error - defect list error in Primary List */
#define SCSI_KCQHE_DLEINGROWNLIST 0x041903 /* Hardware Error - defect list error in Grown List */
#define SCSI_KCQHE_REASSIGNFAILED 0x043100 /* Hardware Error - reassign failed */
#define SCSI_KCQHE_NODEFECTSPAREAVAILABLE 0x043200 /* Hardware Error - no defect spare available */
#define SCSI_KCQHE_UNSUPPORTEDENCLOSUREFUNCTION 0x043501 /* Hardware Error - unsupported enclosure function */
#define SCSI_KCQHE_ESUNAVAILABLE 0x043502 /* Hardware Error - enclosure services unavailable */
#define SCSI_KCQHE_ESTRANSFERFAILURE 0x043503 /* Hardware Error - enclosure services transfer failure */
#define SCSI_KCQHE_ESREFUSED 0x043504 /* Hardware Error - enclosure services refused */
#define SCSI_KCQHE_SELFTESTFAILED 0x043e03 /* Hardware Error - self-test failed */
#define SCSI_KCQHE_UNABLETOUPDATESELFTEST 0x043e04 /* Hardware Error - unable to update self-test */
#define SCSI_KCQHE_DMDIAGNOSTICFAIL 0x044080 /* Hardware Error - Degrade Mode. Diagnostic Fail */
#define SCSI_KCQHE_DMHWERROR 0x044081 /* Hardware Error - Degrade Mode. H/W Error */
#define SCSI_KCQHE_DMRAMMICROCODENOTLOADED 0x044085 /* Hardware Error - Degrade Mode. RAM microcode not loaded */
#define SCSI_KCQHE_SEEKTESTFAILURE 0x044090 /* Hardware Error - seek test failure */
#define SCSI_KCQHE_READWRITETESTFAILURE 0x0440a0 /* Hardware Error - read/write test failure */
#define SCSI_KCQHE_DEVICESELFRESET 0x0440b0 /* Hardware Error - device self-reset */
#define SCSI_KCQHE_COMPONENTMISMATCH 0x0440d0 /* Hardware Error - component mismatch */
#define SCSI_KCQHE_INTERNALTARGETFAILURE 0x044400 /* Hardware Error - internal target failure */
#define SCSI_KCQHE_INTERNALLOGICERROR 0x048100 /* Hardware Error - internal logic error */
#define SCSI_KCQHE_COMMANDTIMEOUT 0x048200 /* Hardware Error - command timeout */
/* Illegal Request KCQ values */
#define SCSI_KCQIR_PARMLISTLENGTHERROR 0x051a00 /* Illegal Request - parm list length error */
#define SCSI_KCQIR_INVALIDCOMMAND 0x052000 /* Illegal Request - invalid/unsupported command code */
#define SCSI_KCQIR_LBAOUTOFRANGE 0x052100 /* Illegal Request - LBA out of range */
#define SCSI_KCQIR_INVALIDFIELDINCBA 0x052400 /* Illegal Request - invalid field in CDB (Command Descriptor Block) */
#define SCSI_KCQIR_INVALIDLUN 0x052500 /* Illegal Request - invalid LUN */
#define SCSI_KCQIR_INVALIDFIELDSINPARMLIST 0x052600 /* Illegal Request - invalid fields in parm list */
#define SCSI_KCQIR_PARAMETERNOTSUPPORTED 0x052601 /* Illegal Request - parameter not supported */
#define SCSI_KCQIR_INVALIDPARMVALUE 0x052602 /* Illegal Request - invalid parm value */
#define SCSI_KCQIR_IFPTHRESHOLDPARAMETER 0x052603 /* Illegal Request - invalid field parameter - threshold parameter */
#define SCSI_KCQIR_INVALIDRELEASEOFPR 0x052604 /* Illegal Request - invalid release of persistent reservation */
#define SCSI_KCQIR_IFPTMSFIRMWARETAG 0x052697 /* Illegal Request - invalid field parameter - TMS firmware tag */
#define SCSI_KCQIR_IFPCHECKSUM 0x052698 /* Illegal Request - invalid field parameter - check sum */
#define SCSI_KCQIR_IFPFIRMWARETAG 0x052699 /* Illegal Request - invalid field parameter - firmware tag */
#define SCSI_KCQIR_COMMANDSEQUENCEERROR 0x052c00 /* Illegal Request - command sequence error */
#define SCSI_KCQIR_UNSUPPORTEDENCLOSUREFUNCTION 0x053501 /* Illegal Request - unsupported enclosure function */
#define SCSI_KCQIR_SAVINGPARMSNOTSUPPORTED 0x053900 /* Illegal Request - Saving parameters not supported */
#define SCSI_KCQIR_INVALIDMESSAGE 0x054900 /* Illegal Request - invalid message */
#define SCSI_KCQIR_MEDIALOADOREJECTFAILED 0x055300 /* Illegal Request - media load or eject failed */
#define SCSI_KCQIR_UNLOADTAPEFAILURE 0x055301 /* Illegal Request - unload tape failure */
#define SCSI_KCQIR_MEDIUMREMOVALPREVENTED 0x055302 /* Illegal Request - medium removal prevented */
#define SCSI_KCQIR_SYSTEMRESOURCEFAILURE 0x055500 /* Illegal Request - system resource failure */
#define SCSI_KCQIR_SYSTEMBUFFERFULL 0x055501 /* Illegal Request - system buffer full */
#define SCSI_KCQIR_INSUFFICIENTRR 0x055504 /* Illegal Request - Insufficient Registration Resources */
/* Unit Attention KCQ values */
#define SCSI_KCQUA_NOTREADYTOTRANSITION 0x062800 /* Unit Attention - not-ready to ready transition (format complete) */
#define SCSI_KCQUA_DEVICERESETOCCURRED 0x062900 /* Unit Attention - POR or device reset occurred */
#define SCSI_KCQUA_POROCCURRED 0x062901 /* Unit Attention - POR occurred */
#define SCSI_KCQUA_SCSIBUSRESETOCCURRED 0x062902 /* Unit Attention - SCSI bus reset occurred */
#define SCSI_KCQUA_TARGETRESETOCCURRED 0x062903 /* Unit Attention - TARGET RESET occurred */
#define SCSI_KCQUA_SELFINITIATEDRESETOCCURRED 0x062904 /* Unit Attention - self-initiated-reset occurred */
#define SCSI_KCQUA_TRANSCEIVERMODECHANGETOSE 0x062905 /* Unit Attention - transceiver mode change to SE */
#define SCSI_KCQUA_TRANSCEIVERMODECHANGETOLVD 0x062906 /* Unit Attention - transceiver mode change to LVD */
#define SCSI_KCQUA_PARAMETERSCHANGED 0x062a00 /* Unit Attention - parameters changed */
#define SCSI_KCQUA_MODEPARAMETERSCHANGED 0x062a01 /* Unit Attention - mode parameters changed */
#define SCSI_KCQUA_LOGSELECTPARMSCHANGED 0x062a02 /* Unit Attention - log select parms changed */
#define SCSI_KCQUA_RESERVATIONSPREEMPTED 0x062a03 /* Unit Attention - Reservations pre-empted */
#define SCSI_KCQUA_RESERVATIONSRELEASED 0x062a04 /* Unit Attention - Reservations released */
#define SCSI_KCQUA_REGISTRATIONSPREEMPTED 0x062a05 /* Unit Attention - Registrations pre-empted */
#define SCSI_KCQUA_COMMANDSCLEARED 0x062f00 /* Unit Attention - commands cleared by another initiator */
#define SCSI_KCQUA_OPERATINGCONDITIONSCHANGED 0x063f00 /* Unit Attention - target operating conditions have changed */
#define SCSI_KCQUA_MICROCODECHANGED 0x063f01 /* Unit Attention - microcode changed */
#define SCSI_KCQUA_CHANGEDOPERATINGDEFINITION 0x063f02 /* Unit Attention - changed operating definition */
#define SCSI_KCQUA_INQUIRYPARAMETERSCHANGED 0x063f03 /* Unit Attention - inquiry parameters changed */
#define SCSI_KCQUA_DEVICEIDENTIFIERCHANGED 0x063f05 /* Unit Attention - device identifier changed */
#define SCSI_KCQUA_INVALIDAPMPARAMETERS 0x063f90 /* Unit Attention - invalid APM parameters */
#define SCSI_KCQUA_WORLDWIDENAMEMISMATCH 0x063f91 /* Unit Attention - world-wide name mismatch */
#define SCSI_KCQUA_PFATHRESHOLDREACHED 0x065d00 /* Unit Attention - PFA threshold reached */
#define SCSI_KCQUA_PFATHRESHOLDEXCEEDED 0x065dff /* Unit Attention - PFA threshold exceeded */
/* Write Protect KCQ values */
#define SCSI_KCQWP_COMMANDNOTALLOWED 0x072700 /* Write Protect - command not allowed */
/* Aborted Command KCQ values */
#define SCSI_KCQAC_NOADDITIONALSENSECODE 0x0b0000 /* Aborted Command - no additional sense code */
#define SCSI_KCQAC_SYNCDATATRANSFERERROR 0x0b1b00 /* Aborted Command - sync data transfer error (extra ACK) */
#define SCSI_KCQAC_UNSUPPORTEDLUN 0x0b2500 /* Aborted Command - unsupported LUN */
#define SCSI_KCQAC_ECHOBUFFEROVERWRITTEN 0x0b3f0f /* Aborted Command - echo buffer overwritten */
#define SCSI_KCQAC_MESSAGEREJECTERROR 0x0b4300 /* Aborted Command - message reject error */
#define SCSI_KCQAC_INTERNALTARGETFAILURE 0x0b4400 /* Aborted Command - internal target failure */
#define SCSI_KCQAC_SELECTIONFAILURE 0x0b4500 /* Aborted Command - Selection/Reselection failure */
#define SCSI_KCQAC_SCSIPARITYERROR 0x0b4700 /* Aborted Command - SCSI parity error */
#define SCSI_KCQAC_INITIATORDETECTEDERRORECEIVED 0x0b4800 /* Aborted Command - initiator-detected error message received */
#define SCSI_KCQAC_ILLEGALMESSAGE 0x0b4900 /* Aborted Command - inappropriate/illegal message */
#define SCSI_KCQAC_DATAPHASEERROR 0x0b4b00 /* Aborted Command - data phase error */
#define SCSI_KCQAC_OVERLAPPEDCOMMANDSATTEMPTED 0x0b4e00 /* Aborted Command - overlapped commands attempted */
#define SCSI_KCQAC_LOOPINITIALIZATION 0x0b4f00 /* Aborted Command - due to loop initialization */
/* Other KCQ values: */
#define SCSO_KCQOTHER_MISCOMPARE 0x0e1d00 /* Miscompare - during verify byte check operation */
/* SSCSI Status Codes *******************************************************/
#define SCSI_STATUS_OK 0x00 /* OK */
#define SCSI_STATUS_CHECKCONDITION 0x02 /* Check condition */
#define SCSI_STATUS_CONDITIONMET 0x04 /* Condition met */
#define SCSI_STATUS_BUSY 0x08 /* Busy */
#define SCSI_STATUS_INTERMEDIATE 0x10 /* Intermediate */
#define SCSI_STATUS_DATAOVERUNDERRUN 0x12 /* Data Under/Over Run? */
#define SCSI_STATUS_INTERMEDIATECONDITIONMET 0x14 /* Intermediate - Condition met */
#define SCSI_STATUS_RESERVATIONCONFLICT 0x18 /* Reservation conflict */
#define SCSI_STATUS_COMMANDTERMINATED 0x22 /* Command terminated */
#define SCSI_STATUS_QUEUEFULL 0x28 /* Queue (task set) full */
#define SCSI_STATUS_ACAACTIVE 0x30 /* ACA active */
#define SCSI_STATUS_TASKABORTED 0x40 /* Task aborted */
/* Definitions for selected SCSI commands ***********************************/
/* Inquiry */
#define SCSICMD_INQUIRYFLAGS_EVPD 0x01 /* Bit 0: EVPD */
/* Bits 5-7: Peripheral Qualifier */
#define SCSIRESP_INQUIRYPQ_CONNECTED 0x00 /* 000: Device is connected */
#define SCSIRESP_INQUIRYPQ_NOTCONNECTED 0x20 /* 001: Device is NOT connected */
#define SCSIRESP_INQUIRYPQ_NOTCAPABLE 0x60 /* 011: LUN not supported */
/* Bits 0-4: Peripheral Device */
#define SCSIRESP_INQUIRYPD_DIRECTACCESS 0x00 /* Direct-access block device */
#define SCSIRESP_INQUIRYPD_SEQUENTIALACCESS 0x01 /* Sequential-access block device */
#define SCSIRESP_INQUIRYPD_PRINTER 0x02 /* Printer device */
#define SCSIRESP_INQUIRYPD_PROCESSOR 0x03 /* Processor device */
#define SCSIRESP_INQUIRYPD_WRONCE 0x04 /* Write once device */
#define SCSIRESP_INQUIRYPD_CDDVD 0x05 /* CD/DVD device */
#define SCSIRESP_INQUIRYPD_SCANNER 0x06 /* Scanner device (obsolete) */
#define SCSIRESP_INQUIRYPD_OPTICAL 0x07 /* Optical memory device */
#define SCSIRESP_INQUIRYPD_MEDIUMCHANGER 0x08 /* Medium changer device (Jukebox) */
#define SCSIRESP_INQUIRYPD_COMMUNICATIONS 0x09 /* Communications device (obsolete) */
#define SCSIRESP_INQUIRYPD_STORAGEARRAY 0x0c /* Storage array controller device */
#define SCSIRESP_INQUIRYPD_ENCLOSURESERVICES 0x0d /* Enclosure services device */
#define SCSIRESP_INQUIRYPD_RBC 0x0e /* Simplified direct-access device */
#define SCSIRESP_INQUIRYPD_OCRW 0x0f /* Optical reader/writer device */
#define SCSIRESP_INQUIRYPD_BCC 0x10 /* Bridge controller commands */
#define SCSIRESP_INQUIRYPD_OSD 0x11 /* Object-based storage device */
#define SCSIRESP_INQUIRYPD_ADC 0x12 /* Automation/drive interface */
#define SCSIRESP_INQUIRYPD_WKLU 0x1e /* Well-known logical unit */
#define SCSIRESP_INQUIRYPD_UNKNOWN 0x1f /* Direct-access block device */
#define SCSIRESP_INQUIRYFLAGS1_RMB 0x80 /* Bit 7: RMB */
#define SCSIRESP_INQUIRYFLAGS2_NORMACA 0x20 /* Bit 5: NormACA */
#define SCSIRESP_INQUIRYFLAGS2_HISUP 0x10 /* Bit 4: HiSup */
#define SCSIRESP_INQUIRYFLAGS2_FMTMASK 0x0f /* Bits 0-3: Response data format */
#define SCSIRESP_INQUIRYFLAGS3_SCCS 0x80 /* Bit 8: SCCS */
#define SCSIRESP_INQUIRYFLAGS3_ACC 0x40 /* Bit 7: ACC */
#define SCSIRESP_INQUIRYFLAGS3_TPGSMASK 0x30 /* Bits 4-5: TPGS */
#define SCSIRESP_INQUIRYFLAGS3_3PC 0x08 /* Bit 3: 3PC */
#define SCSIRESP_INQUIRYFLAGS3_PROTECT 0x01 /* Bit 0: Protect */
#define SCSIRESP_INQUIRYFLAGS4_BQUE 0x80 /* Bit 7: BQue */
#define SCSIRESP_INQUIRYFLAGS4_ENCSERV 0x40 /* Bit 6: EncServ */
#define SCSIRESP_INQUIRYFLAGS4_VS 0x20 /* Bit 5: VS */
#define SCSIRESP_INQUIRYFLAGS4_MULTIP 0x10 /* Bit 4: MultIP */
#define SCSIRESP_INQUIRYFLAGS4_MCHNGR 0x08 /* Bit 3: MChngr */
#define SCSIRESP_INQUIRYFLAGS4_ADDR16 0x01 /* Bit 0: Addr16 */
#define SCSIRESP_INQUIRYFLAGS5_WBUS16 0x20 /* Bit 5: WBus16 */
#define SCSIRESP_INQUIRYFLAGS5_SYNC 0x10 /* Bit 4: SYNC */
#define SCSIRESP_INQUIRYFLAGS5_LINKED 0x08 /* Bit 3: LINKED */
#define SCSIRESP_INQUIRYFLAGS5_CMDQUEUE 0x02 /* Bit 1: CmdQue */
#define SCSIRESP_INQUIRYFLAGS5_VS 0x01 /* Bit 0: VS */
#define SCSIRESP_INQUIRYFLAGS6_CLOCKINGMASK 0xc0 /* Bits 2-3: Clocking */
#define SCSIRESP_INQUIRYFLAGS6_QAS 0x02 /* Bit 1: QAS */
#define SCSIRESP_INQUIRYFLAGS6_IUS 0x01 /* Bit 0: IUS */
/* Sense data */
/* Sense data response codes */
#define SCSIRESP_SENSEDATA_CURRENTFIXED 0x70 /* Byte 1 is always the response code */
#define SCSIRESP_SENSEDATA_DEFERREDFIXED 0x71
#define SCSIRESP_SENSEDATA_CURRENTDESC 0x72
#define SCSIRESP_SENSEDATA_DEFERREDDESC 0x73
#define SCSIRESP_SENSEDATA_RESPVALID 0x80
/* Fixed sense data flags */
#define SCSIRESP_SENSEDATA_FILEMARK 0x80 /* Bit 7: FileMark */
#define SCSIRESP_SENSEDATA_EOM 0x40 /* Bit 6: EOM */
#define SCSIRESP_SENSEDATA_ILI 0x20 /* Bit 5: ILI */
#define SCSIRESP_SENSEDATA_SENSEKEYMASK 0x0f /* Bits 0-3: Sense key */
#define SCSIRESP_SENSEDATA_NOSENSE 0x00 /* Nothing to be reported */
#define SCSIRESP_SENSEDATA_RECOVEREDERROR 0x01 /* Successful after recovery action */
#define SCSIRESP_SENSEDATA_NOTREADY 0x02 /* Logical unit is not accessible */
#define SCSIRESP_SENSEDATA_MEDIUMERROR 0x03 /* Error possibly caused by flaw in medium */
#define SCSIRESP_SENSEDATA_HARDWAREERROR 0x04 /* Non-recoverable hardware error */
#define SCSIRESP_SENSEDATA_ILLEGALREQUEST 0x05 /* Error in received request */
#define SCSIRESP_SENSEDATA_UNITATTENTION 0x06 /* Unit attention condition */
#define SCSIRESP_SENSEDATA_DATAPROTECT 0x07 /* Action failed, medium protected */
#define SCSIRESP_SENSEDATA_BLANKCHECK 0x08 /* Encountered blank media */
#define SCSIRESP_SENSEDATA_VENDORSPECIFIC 0x09 /* Vendor specific condition */
#define SCSIRESP_SENSEDATA_ABORTEDCOMMAND 0x0b /* Command was aborted */
#define SCSIRESP_SENSEDATA_KEYVALID 0x80 /* Sense-specific data valid */
/* Mode Select 6 */
#define SCSICMD_MODESELECT6_PF 0x10 /* Bit 4: PF */
#define SCSICMD_MODESELECT6_SP 0x01 /* Bit 0: SP */
/* Mode Sense 6 */
#define SCSICMD_MODESENSE6_DBD 0x08 /* Bit 3: PF */
#define SCSICMD_MODESENSE_PCMASK 0xc0 /* Bits 6-7: Page control (PC) */
#define SCSICMD_MODESENSE_PCCURRENT 0x00 /* Current values */
#define SCSICMD_MODESENSE_PCCHANGEABLE 0x40 /* Changeable values */
#define SCSICMD_MODESENSE_PCDEFAULT 0x80 /* Default values */
#define SCSICMD_MODESENSE_PCSAVED 0xc0 /* Saved values */
#define SCSICMD_MODESENSE_PGCODEMASK 0x3f /* Bits 0-5: Page code */
#define SCSICMD_MODESENSE6_PCDEFAULT 0x80 /* Default values */
/* Direct-access device page codes */
#define SCSIRESP_MODESENSE_PGCCODE_VENDOR 0x00 /* Vendor-specific */
#define SCSIRESP_MODESENSE_PGCCODE_RWERROR 0x01 /* Read/Write error recovery mode page */
#define SCSIRESP_MODESENSE_PGCCODE_RECONNECT 0x02 /* Disconnect-reconnect mode page */
#define SCSIRESP_MODESENSE_PGCCODE_FORMATDEV 0x03 /* Format device mode page (obsolete) */
#define SCSIRESP_MODESENSE_PGCCODE_RIGID 0x04 /* Rigid disk geometry mode page (obsolete) */
#define SCSIRESP_MODESENSE_PGCCODE_FLEXIBLE 0x05 /* Flexible disk geometry mode page (obsolete) */
#define SCSIRESP_MODESENSE_PGCCODE_VERIFY 0x07 /* Verify error recovery mode page */
#define SCSIRESP_MODESENSE_PGCCODE_CACHING 0x08 /* Caching mode page */
#define SCSIRESP_MODESENSE_PGCCODE_CONTROL 0x0a /* Control mode page (0x0a/0x00) */
#define SCSIRESP_MODESENSE_PGCCODE_CONTROLEXT 0x0a /* Control extension mode page (0x0a/0x01) */
#define SCSIRESP_MODESENSE_PGCCODE_MEDIUMTYPES 0x0b /* Medum types supported mode page (obsolete) */
#define SCSIRESP_MODESENSE_PGCCODE_NP 0x0c /* Notch and partition mode page (obsolete) */
#define SCSIRESP_MODESENSE_PGCCODE_XOR 0x10 /* XOR control mode page */
#define SCSIRESP_MODESENSE_PGCCODE_ES 0x14 /* Enclosure services mode page */
#define SCSIRESP_MODESENSE_PGCCODE_PSLUN 0x18 /* Protocol-specific LUN mode page */
#define SCSIRESP_MODESENSE_PGCCODE_PSPORT 0x19 /* Protocol-specific port mode page */
#define SCSIRESP_MODESENSE_PGCCODE_POWER 0x1a /* Power condition mode page */
#define SCSIRESP_MODESENSE_PGCCODE_IE 0x1c /* Informational exceptions control mode page (0x1c/0x00) */
#define SCSIRESP_MODESENSE_PGCCODE_BC 0x1c /* Background control mode page (0x1c/0x01) */
#define SCSIRESP_MODESENSE_PGCCODE_RETURNALL 0x3f /* Return all mode pages */
/* Direct-access caching mode page */
#define SCSIRESP_CACHINGMODEPG_PS 0x80 /* Byte 0, Bit 7: PS */
#define SCSIRESP_CACHINGMODEPG_SPF 0x60 /* Byte 0, Bit 6: SPF */
#define SCSIRESP_CACHINGMODEPG_IC 0x80 /* Byte 2, Bit 7: IC */
#define SCSIRESP_CACHINGMODEPG_ABPF 0x40 /* Byte 2, Bit 6: ABPF */
#define SCSIRESP_CACHINGMODEPG_CAP 0x20 /* Byte 2, Bit 5: CAP */
#define SCSIRESP_CACHINGMODEPG_DISC 0x10 /* Byte 2, Bit 4: DISC */
#define SCSIRESP_CACHINGMODEPG_SIZE 0x08 /* Byte 2, Bit 3: SIZE */
#define SCSIRESP_CACHINGMODEPG_WCE 0x04 /* Byte 2, Bit 2: Write cache enable (WCE) */
#define SCSIRESP_CACHINGMODEPG_MF 0x02 /* Byte 2, Bit 1: MF */
#define SCSIRESP_CACHINGMODEPG_RCD 0x01 /* Byte 2, Bit 0: Read cache disable (RCD) */
#define SCSIRESP_MODEPARMHDR_DAPARM_WP 0x80 /* Bit 7: WP (Direct-access block devices only) */
#define SCSIRESP_MODEPARMHDR_DAPARM_DBPFUA 0x10 /* Bit 4: DBOFUA (Direct-access block devices only) */
#define SCSIRESP_PAGEFMT_PS 0x80 /* Bit 7: PS */
#define SCSIRESP_PAGEFMT_SPF 0x40 /* Bit 6: SPF */
#define SCSIRESP_PAGEFMT_PGCODEMASK 0x3f /* Bits 0-5: Page code */
/* Prevent / Allow Medium Removal */
#define SCSICMD_PREVENTMEDIUMREMOVAL_TRANSPORT 0x01 /* Removal prohibited from data transport */
#define SCSICMD_PREVENTMEDIUMREMOVAL_MCHANGER 0x02 /* Removal prohibited from medium changer */
/* Read format capacities */
#define SCIRESP_RDFMTCAPACITIES_UNFORMATED 0x01 /* Unformatted media */
#define SCIRESP_RDFMTCAPACITIES_FORMATED 0x02 /* Formatted media */
#define SCIRESP_RDFMTCAPACITIES_NOMEDIA 0x03 /* No media */
/* Read 6 */
#define SCSICMD_READ6_MSLBAMASK 0x1f
/* Write 6 */
#define SCSICMD_WRITE6_MSLBAMASK 0x1f
/* Mode Select 10 */
#define SCSICMD_MODESELECT10_PF 0x10 /* Bit 4: PF */
#define SCSICMD_MODESELECT10_SP 0x01 /* Bit 0: SP */
/* Mode Sense 10 */
#define SCSICMD_MODESENSE10_LLBAA 0x10 /* Bit 4: LLBAA */
#define SCSICMD_MODESENSE10_DBD 0x08 /* Bit 3: PF */
/* Read 10 */
#define SCSICMD_READ10FLAGS_RDPROTECTMASK 0xe0
#define SCSICMD_READ10FLAGS_DPO 0x10 /* Disable Page Out */
#define SCSICMD_READ10FLAGS_FUA 0x08
#define SCSICMD_READ10FLAGS_FUANV 0x02
/* Write 10 */
#define SCSICMD_WRITE10FLAGS_WRPROTECTMASK 0xe0
#define SCSICMD_WRITE10FLAGS_DPO 0x10 /* Disable Page Out */
#define SCSICMD_WRITE10FLAGS_FUA 0x08
#define SCSICMD_WRITE10FLAGS_FUANV 0x02
/* Verify 10 */
#define SCSICMD_VERIFY10_VRPROTECTMASK 0xe0 /* Byte 1: Bits 5-7: VRPROTECT */
#define SCSICMD_VERIFY10_DPO 0x10 /* Byte 1: Bit 4: Disable Page Out (DPO) */
#define SCSICMD_VERIFY10_BYTCHK 0x02 /* Byte 1: Bit 2: BytChk */
/* Read 12 */
#define SCSICMD_READ12FLAGS_RDPROTECTMASK 0xe0
#define SCSICMD_READ12FLAGS_DPO 0x10 /* Disable Page Out */
#define SCSICMD_READ12FLAGS_FUA 0x08
#define SCSICMD_READ12FLAGS_FUANV 0x02
/* Write 12 */
#define SCSICMD_WRITE12FLAGS_WRPROTECTMASK 0xe0
#define SCSICMD_WRITE12FLAGS_DPO 0x10 /* Disable Page Out */
#define SCSICMD_WRITE12FLAGS_FUA 0x08
#define SCSICMD_WRITE12FLAGS_FUANV 0x02
/* Verify 12 */
#define SCSICMD_VERIFY12_VRPROTECTMASK 0xe0 /* Byte 1: Bits 5-7: VRPROTECT */
#define SCSICMD_VERIFY12_DPO 0x10 /* Byte 1: Bit 4: Disable Page Out (DPO) */
#define SCSICMD_VERIFY12_BYTCHK 0x02 /* Byte 1: Bit 2: BytChk */
/****************************************************************************
* Public Types
****************************************************************************/
/* Format structures for selected SCSI primary commands */
#define SCSICMD_TESTUNITREADY_SIZEOF 6
struct scsicmd_requestsense_s
{
uint8_t opcode; /* 0: 0x03 */
uint8_t flags; /* 1: See SCSICMD_REQUESTSENSE_FLAGS_* */
uint8_t reserved[2]; /* 2-3: Reserved */
uint8_t alloclen; /* 4: Allocation length */
uint8_t control; /* 5: Control */
};
#define SCSICMD_REQUESTSENSE_SIZEOF 6
#define SCSICMD_REQUESTSENSE_MSSIZEOF 12 /* MS-Windows REQUEST SENSE with cbw->cdblen == 12 */
struct scsiresp_fixedsensedata_s
{
uint8_t code; /* 0: Response code See SCSIRESP_SENSEDATA_*FIXED defns */
uint8_t obsolete; /* 1: */
uint8_t flags; /* 2: See SCSIRESP_SENSEDATA_* definitions */
uint8_t info[4]; /* 3-6: Information */
uint8_t len; /* 7: Additional length */
uint8_t cmdinfo[4]; /* 8-11: Command-specific information */
uint8_t code2; /* 12: Additional sense code */
uint8_t qual2; /* 13: Additional sense code qualifier */
uint8_t fru; /* 14: Field replacement unit code */
uint8_t key[3]; /* 15-17: Sense key specific */
/* 18-: Additional bytes may follow */
};
#define SCSIRESP_FIXEDSENSEDATA_SIZEOF 18 /* Minimum size */
struct scscicmd_inquiry_s
{
uint8_t opcode; /* 0: 0x12 */
uint8_t flags; /* 1: See SCSICMD_INQUIRY_FLAGS_* */
uint8_t pagecode; /* 2: Page code */
uint8_t alloclen[2]; /* 3-4: Allocation length */
uint8_t control; /* 5: Control */
};
#define SCSICMD_INQUIRY_SIZEOF 6
struct scsiresp_inquiry_s
{
/* Mandatory */
uint8_t qualtype; /* 0: Bits 5-7: Peripheral qualifier; Bits 0-4: Peripheral device type */
uint8_t flags1; /* 1: See SCSIRESP_INQUIRY_FLAGS1_* */
uint8_t version; /* 2: Version */
uint8_t flags2; /* 3: See SCSIRESP_INQUIRY_FLAGS2_* */
uint8_t len; /* 4: Additional length */
uint8_t flags3; /* 5: See SCSIRESP_INQUIRY_FLAGS3_* */
uint8_t flags4; /* 6: See SCSIRESP_INQUIRY_FLAGS4_* */
uint8_t flags5; /* 7: See SCSIRESP_INQUIRY_FLAGS5_* */
uint8_t vendorid[8]; /* 8-15: T10 Vendor Identification */
uint8_t productid[16]; /* 16-31: Product Identification */
uint8_t revision[4]; /* 32-35: Product Revision Level */
/* Optional */
uint8_t vendor[20]; /* 36-55: Vendor specific */
uint8_t flags6; /* 56: See SCSIRESP_INQUIRY_FLAGS6_* */
uint8_t reserved1; /* 57: Reserved */
uint8_t version1[2]; /* 58-59: Version Descriptor 1 */
uint8_t version2[2]; /* 60-61: Version Descriptor 2 */
uint8_t version3[2]; /* 62-63: Version Descriptor 3 */
uint8_t version4[2]; /* 64-65: Version Descriptor 4 */
uint8_t version5[2]; /* 66-67: Version Descriptor 5 */
uint8_t version6[2]; /* 68-69: Version Descriptor 6 */
uint8_t version7[2]; /* 70-71: Version Descriptor 7 */
uint8_t version8[2]; /* 72-73: Version Descriptor 8 */
uint8_t reserved2[22]; /* 74-95: Reserved */
/* 96-: Vendor-specific parameters may follow */
};
#define SCSIRESP_INQUIRY_SIZEOF 36 /* Minimum size */
struct scsicmd_modeselect6_s
{
uint8_t opcode; /* 0x15 */
uint8_t flags; /* 1: See SCSICMD_MODESELECT6_FLAGS_* */
uint8_t reserved[2]; /* 2-3: Reserved */
uint8_t plen; /* 4: Parameter list length */
uint8_t control; /* 5: Control */
};
#define SCSICMD_MODESELECT6_SIZEOF 6
struct scsicmd_modesense6_s
{
uint8_t opcode; /* 0x1a */
uint8_t flags; /* 1: See SCSICMD_MODESENSE6_FLAGS_* */
uint8_t pcpgcode; /* 2: Bits 6-7: PC, bits 0-5: page code */
uint8_t subpgcode; /* 3: subpage code */
uint8_t alloclen; /* 4: Allocation length */
uint8_t control; /* 5: Control */
};
#define SCSICMD_MODESENSE6_SIZEOF 6
struct scsiresp_modeparameterhdr6_s
{
uint8_t mdlen; /* 0: Mode data length */
uint8_t type; /* 1: Medium type */
uint8_t param; /* 2: Device-specific parameter */
uint8_t bdlen; /* 3: Block descriptor length */
};
#define SCSIRESP_MODEPARAMETERHDR6_SIZEOF 4
struct scsiresp_blockdesc_s
{
uint8_t density; /* 0: density code */
uint8_t nblocks[3]; /* 1-3: Number of blocks */
uint8_t reserved; /* 4: reserved */
uint8_t blklen[3]; /* 5-7: Block len */
};
#define SCSIRESP_BLOCKDESC_SIZEOF 8
struct scsiresp_pageformat_s
{
uint8_t pgcode; /* 0: See SCSIRESP_PAGEFMT_* definitions */
uint8_t pglen; /* 1: Page length (n-1) */
uint8_t parms[1]; /* 2-n: Mode parameters */
};
struct scsiresp_subpageformat_s
{
uint8_t pgcode; /* 0: See SCSIRESP_PAGEFMT_* definitions */
uint8_t subpgcode; /* 1: sub-page code */
uint8_t pglen[2]; /* 2-3: Page length (n-3) */
uint8_t parms[1]; /* 4-n: Mode parameters */
};
struct scsiresp_cachingmodepage_s
{
uint8_t pgcode; /* 0: Bit 7: PS; Bit 6: SPF, Bits 0-5: page code == 8 */
uint8_t len; /* 1: Page length (18) */
uint8_t flags1; /* 2: See SCSIRESP_CACHINGMODEPG_* definitions */
uint8_t priority; /* 3: Bits 4-7: Demand read retention priority; Bits 0-3: Write retention priority */
uint8_t dpflen[2]; /* 4-5: Disable prefetch transfer length */
uint8_t minpf[2]; /* 6-7: Minimum pre-fetch */
uint8_t maxpf[2]; /* 8-9: Maximum pre-fetch */
uint8_t maxpfc[2]; /* 10-11: Maximum pref-fetch ceiling */
uint8_t flags2; /* 12: See SCSIRESP_CACHINGMODEPG_* definitions */
uint8_t nsegments; /* 13: Number of cache segments */
uint8_t segsize[2]; /* 14-15: Cache segment size */
uint8_t reserved; /* 16: Reserved */
uint8_t obsolete[3]; /* 17-19: Obsolete */
};
/* Format structures for selected SCSI block commands */
struct scsicmd_read6_s
{
uint8_t opcode; /* 0: 0x08 */
uint8_t mslba; /* 1: Bits 5-7: reserved; Bits 0-6: MS Logical Block Address (LBA) */
uint8_t lslba[2]; /* 2-3: LS Logical Block Address (LBA) */
uint8_t xfrlen; /* 4: Transfer length (in contiguous logical blocks) */
uint8_t control; /* 5: Control */
};
#define SCSICMD_READ6_SIZEOF 6
struct scsicmd_write6_s
{
uint8_t opcode; /* 0: 0x0a */
uint8_t mslba; /* 1: Bits 5-7: reserved; Bits 0-6: MS Logical Block Address (LBA) */
uint8_t lslba[2]; /* 2-3: LS Logical Block Address (LBA) */
uint8_t xfrlen; /* 4: Transfer length (in contiguous logical blocks) */
uint8_t control; /* 5: Control */
};
#define SCSICMD_WRITE6_SIZEOF 6
struct scsicmd_startstopunit_s
{
uint8_t opcode; /* 0: 0x1b */
uint8_t immed; /* 1: Bits 2-7: Reserved, Bit 0: Immed */
uint8_t reserved; /* 2: reserved */
uint8_t pcm; /* 3: Bits 4-7: Reserved, Bits 0-3: Power condition modifier */
uint8_t pc; /* 4: Bits 4-7: Power condition, Bit 2: NO_FLUSH, Bit 1: LOEJ, Bit 0: START */
uint8_t control; /* 5: Control */
};
#define SCSICMD_STARTSTOPUNIT_SIZEOF 6
struct scsicmd_preventmediumremoval_s
{
uint8_t opcode; /* 0: 0x1e */
uint8_t reserved[3]; /* 1-3: Reserved */
uint8_t prevent; /* 4: Bits 2-7: Reserved, Bits 0:1: prevent */
uint8_t control; /* 5: Control */
};
#define SCSICMD_PREVENTMEDIUMREMOVAL_SIZEOF 6
struct scsicmd_readformatcapcacities_s
{
uint8_t opcode; /* 0: 0x23 */
uint8_t reserved[6]; /* 1-6: Reserved */
uint8_t alloclen[2]; /* 7-8: Allocation length */
uint8_t control; /* 9: Control */
};
#define SCSICMD_READFORMATCAPACITIES_SIZEOF 10
struct scsiresp_readformatcapacities_s
{
/* Current capacity header */
uint8_t reserved[3]; /* 0-2: Reserved */
uint8_t listlen; /* 3: Capacity list length */
/* Current/Maximum Capacity Descriptor (actually a separate structure) */
uint8_t nblocks[4]; /* 4-7: Number of blocks */
uint8_t type; /* 8: Bits 2-7: Reserved, Bits 0-1: Descriptor type */
uint8_t blocklen[3]; /* 9-11: Block length */
};
#define SCSIRESP_READFORMATCAPACITIES_SIZEOF 12
#define SCSIRESP_CURRCAPACITYDESC_SIZEOF 8
struct scsiresp_formattedcapacitydesc_s
{
uint8_t nblocks[4]; /* 0-3: Number of blocks */
uint8_t type; /* 4: Bits 2-7: Type, bits 0-1, reserved */
uint8_t param[3]; /* 5-7: Type dependent parameter */
};
#define SCSIRESP_FORMATTEDCAPACITYDESC_SIZEOF 8
struct scsicmd_readcapacity10_s
{
uint8_t opcode; /* 0: 0x25 */
uint8_t reserved1; /* 1: Bits 1-7: Reserved, Bit 0: Obsolete */
uint8_t lba[4]; /* 2-5: Logical block address (LBA) */
uint8_t reserved2[2]; /* 6-7: Reserved */
uint8_t pmi; /* 8: Bits 1-7 Reserved; Bit 0: PMI */
uint8_t control; /* 9: Control */
};
#define SCSICMD_READCAPACITY10_SIZEOF 10
struct scsiresp_readcapacity10_s
{
uint8_t lba[4]; /* 0-3: Returned logical block address (LBA) */
uint8_t blklen[4]; /* 4-7: Logical block length (in bytes) */
};
#define SCSIRESP_READCAPACITY10_SIZEOF 8
struct scsicmd_read10_s
{
uint8_t opcode; /* 0: 0x28 */
uint8_t flags; /* 1: See SCSICMD_READ10FLAGS_* */
uint8_t lba[4]; /* 2-5: Logical Block Address (LBA) */
uint8_t groupno; /* 6: Bits 5-7: reserved; Bits 0-6: group number */
uint8_t xfrlen[2]; /* 7-8: Transfer length (in contiguous logical blocks) */
uint8_t control; /* 9: Control */
};
#define SCSICMD_READ10_SIZEOF 10
struct scsicmd_write10_s
{
uint8_t opcode; /* 0: 0x2a */
uint8_t flags; /* 1: See SCSICMD_WRITE10FLAGS_* */
uint8_t lba[4]; /* 2-5: Logical Block Address (LBA) */
uint8_t groupno; /* 6: Bits 5-7: reserved; Bits 0-6: group number */
uint8_t xfrlen[2]; /* 7-8: Transfer length (in contiguous logical blocks) */
uint8_t control; /* 9: Control */
};
#define SCSICMD_WRITE10_SIZEOF 10
struct scsicmd_verify10_s
{
uint8_t opcode; /* 0: 0x2f */
uint8_t flags; /* 1: See SCSICMD_VERIFY10_* definitions */
uint8_t lba[4]; /* 2-5: Logical block address (LBA) */
uint8_t groupno; /* 6: Bit 7: restricted; Bits 5-6: Reserved, Bits 0-4: Group number */
uint8_t len[2]; /* 7-8: Verification length (in blocks) */
uint8_t control; /* 9: Control */
};
#define SCSICMD_VERIFY10_SIZEOF 10
struct scsicmd_synchronizecache10_s
{
uint8_t opcode; /* 0: 0x35 */
uint8_t flags; /* 1: See SCSICMD_SYNCHRONIZECACHE10_* definitions */
uint8_t lba[4]; /* 2-5: Logical block address (LBA) */
uint8_t groupno; /* 6: Bit 7: restricted; Bits 5-6: Reserved, Bits 0-4: Group number */
uint8_t len[2]; /* 7-8: Number of logical blocks */
uint8_t control; /* 9: Control */
};
#define SCSICMD_SYNCHRONIZECACHE10_SIZEOF 10
struct scsicmd_modeselect10_s
{
uint8_t opcode; /* 0: 0x55 */
uint8_t flags; /* 1: See SCSICMD_MODESELECT10_FLAGS_* */
uint8_t reserved[5]; /* 2-6: Reserved */
uint8_t parmlen[2]; /* 7-8: Parameter list length */
uint8_t control; /* 9: Control */
};
#define SCSICMD_MODESELECT10_SIZEOF 10
struct scsiresp_modeparameterhdr10_s
{
uint8_t mdlen[2]; /* 0-1: Mode data length */
uint8_t type; /* 2: Medium type */
uint8_t param; /* 3: Device-specific parameter */
uint8_t reserved[2]; /* 4-5: reserved */
uint8_t bdlen[2]; /* 6-7: Block descriptor length */
};
#define SCSIRESP_MODEPARAMETERHDR10_SIZEOF 8
struct scsicmd_modesense10_s
{
uint8_t opcode; /* O: 0x5a */
uint8_t flags; /* 1: See SCSICMD_MODESENSE10_FLAGS_* */
uint8_t pcpgcode; /* 2: Bits 6-7: PC, bits 0-5: page code */
uint8_t subpgcode; /* 3: subpage code */
uint8_t reserved[3]; /* 4-6: reserved */
uint8_t alloclen[2]; /* 7-8: Allocation length */
uint8_t control; /* 9: Control */
};
#define SCSICMD_MODESENSE10_SIZEOF 10
struct scsicmd_readcapacity16_s
{
uint8_t opcode; /* 0: 0x9e */
uint8_t action; /* 1: Bits 5-7: Reserved, Bits 0-4: Service action */
uint8_t lba[8]; /* 2-9: Logical block address (LBA) */
uint8_t len[4]; /* 10-13: Allocation length */
uint8_t reserved; /* 14: Reserved */
uint8_t control; /* 15: Control */
};
#define SCSICMD_READCAPACITY16_SIZEOF 16
struct scsicmd_read12_s
{
uint8_t opcode; /* 0: 0xa8 */
uint8_t flags; /* 1: See SCSICMD_READ12FLAGS_* */
uint8_t lba[4]; /* 2-5: Logical Block Address (LBA) */
uint8_t xfrlen[4]; /* 6-9: Transfer length (in contiguous logical blocks) */
uint8_t groupno; /* 10: Bit 7: restricted; Bits 5-6: reserved; Bits 0-6: group number */
uint8_t control; /* 11: Control */
};
#define SCSICMD_READ12_SIZEOF 12
struct scsicmd_write12_s
{
uint8_t opcode; /* 0: 0xaa */
uint8_t flags; /* 1: See SCSICMD_WRITE12FLAGS_* */
uint8_t lba[4]; /* 2-5: Logical Block Address (LBA) */
uint8_t xfrlen[4]; /* 6-9: Transfer length (in contiguous logical blocks) */
uint8_t groupno; /* 10: Bit 7: restricted; Bits 5-6: reserved; Bits 0-6: group number */
uint8_t control; /* 11: Control */
};
#define SCSICMD_WRITE12_SIZEOF 12
struct scsicmd_verify12_s
{
uint8_t opcode; /* 0: 0xaf */
uint8_t flags; /* 1: See SCSICMD_VERIFY12_* definitions */
uint8_t lba[4]; /* 2-5: Logical block address (LBA) */
uint8_t len[4]; /* 6-9: Verification length */
uint8_t groupno; /* 10: Bit 7: restricted; Bits 5-6: Reserved, Bits 0-4: Group number */
uint8_t control; /* 11: Control */
};
#define SCSICMD_VERIFY12_SIZEOF 12
/****************************************************************************
* Public Functions Definitions
****************************************************************************/
#undef EXTERN
#if defined(__cplusplus)
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
#undef EXTERN
#if defined(__cplusplus)
}
#endif
#endif /* __INCLUDE_NUTTX_SCSI_H */

View File

@@ -0,0 +1,938 @@
/*
* Copyright (c) 2022, sakumisu
* Copyright (c) 2024, zhihong chen
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbd_core.h"
#include "usbd_msc.h"
#include "usb_scsi.h"
#if defined(CONFIG_USBDEV_MSC_THREAD)
#include "usb_osal.h"
#endif
#define MSD_OUT_EP_IDX 0
#define MSD_IN_EP_IDX 1
/* Describe EndPoints configuration */
static struct usbd_endpoint mass_ep_data[CONFIG_USBDEV_MAX_BUS][2];
/* MSC Bulk-only Stage */
enum Stage {
MSC_READ_CBW = 0, /* Command Block Wrapper */
MSC_DATA_OUT = 1, /* Data Out Phase */
MSC_DATA_IN = 2, /* Data In Phase */
MSC_SEND_CSW = 3, /* Command Status Wrapper */
MSC_WAIT_CSW = 4, /* Command Status Wrapper */
};
/* Device data structure */
USB_NOCACHE_RAM_SECTION struct usbd_msc_priv {
/* state of the bulk-only state machine */
enum Stage stage;
USB_MEM_ALIGNX struct CBW cbw;
USB_MEM_ALIGNX struct CSW csw;
bool readonly;
bool popup;
uint8_t sKey; /* Sense key */
uint8_t ASC; /* Additional Sense Code */
uint8_t ASQ; /* Additional Sense Qualifier */
uint8_t max_lun;
uint32_t start_sector;
uint32_t nsectors;
uint32_t scsi_blk_size[CONFIG_USBDEV_MSC_MAX_LUN];
uint32_t scsi_blk_nbr[CONFIG_USBDEV_MSC_MAX_LUN];
USB_MEM_ALIGNX uint8_t block_buffer[CONFIG_USBDEV_MSC_MAX_BUFSIZE];
#if defined(CONFIG_USBDEV_MSC_THREAD)
usb_osal_mq_t usbd_msc_mq;
usb_osal_thread_t usbd_msc_thread;
uint32_t nbytes;
#endif
} g_usbd_msc[CONFIG_USBDEV_MAX_BUS];
#ifdef CONFIG_USBDEV_MSC_THREAD
static void usbdev_msc_thread(void *argument);
#endif
static void usdb_msc_set_max_lun(uint8_t busid)
{
g_usbd_msc[busid].max_lun = CONFIG_USBDEV_MSC_MAX_LUN - 1u;
}
static void usbd_msc_reset(uint8_t busid)
{
g_usbd_msc[busid].stage = MSC_READ_CBW;
g_usbd_msc[busid].readonly = false;
}
static int msc_storage_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
{
USB_LOG_DBG("MSC Class request: "
"bRequest 0x%02x\r\n",
setup->bRequest);
switch (setup->bRequest) {
case MSC_REQUEST_RESET:
usbd_msc_reset(busid);
break;
case MSC_REQUEST_GET_MAX_LUN:
(*data)[0] = g_usbd_msc[busid].max_lun;
*len = 1;
break;
default:
USB_LOG_WRN("Unhandled MSC Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
return 0;
}
void msc_storage_notify_handler(uint8_t busid, uint8_t event, void *arg)
{
switch (event) {
case USBD_EVENT_INIT:
#ifdef CONFIG_USBDEV_MSC_THREAD
g_usbd_msc[busid].usbd_msc_mq = usb_osal_mq_create(1);
if (g_usbd_msc[busid].usbd_msc_mq == NULL) {
USB_LOG_ERR("No memory to alloc for g_usbd_msc[busid].usbd_msc_mq\r\n");
}
g_usbd_msc[busid].usbd_msc_thread = usb_osal_thread_create("usbd_msc", CONFIG_USBDEV_MSC_STACKSIZE, CONFIG_USBDEV_MSC_PRIO, usbdev_msc_thread, (void *)busid);
if (g_usbd_msc[busid].usbd_msc_thread == NULL) {
USB_LOG_ERR("No memory to alloc for g_usbd_msc[busid].usbd_msc_thread\r\n");
}
#endif
break;
case USBD_EVENT_DEINIT:
#ifdef CONFIG_USBDEV_MSC_THREAD
if (g_usbd_msc[busid].usbd_msc_mq) {
usb_osal_mq_delete(g_usbd_msc[busid].usbd_msc_mq);
}
if (g_usbd_msc[busid].usbd_msc_thread) {
usb_osal_thread_delete(g_usbd_msc[busid].usbd_msc_thread);
}
#endif
break;
case USBD_EVENT_RESET:
usbd_msc_reset(busid);
break;
case USBD_EVENT_CONFIGURED:
USB_LOG_DBG("Start reading cbw\r\n");
usbd_ep_start_read(busid, mass_ep_data[busid][MSD_OUT_EP_IDX].ep_addr, (uint8_t *)&g_usbd_msc[busid].cbw, USB_SIZEOF_MSC_CBW);
break;
default:
break;
}
}
static void usbd_msc_bot_abort(uint8_t busid)
{
if ((g_usbd_msc[busid].cbw.bmFlags == 0) && (g_usbd_msc[busid].cbw.dDataLength != 0)) {
usbd_ep_set_stall(busid, mass_ep_data[busid][MSD_OUT_EP_IDX].ep_addr);
}
usbd_ep_set_stall(busid, mass_ep_data[busid][MSD_IN_EP_IDX].ep_addr);
usbd_ep_start_read(busid, mass_ep_data[busid][0].ep_addr, (uint8_t *)&g_usbd_msc[busid].cbw, USB_SIZEOF_MSC_CBW);
}
static void usbd_msc_send_csw(uint8_t busid, uint8_t CSW_Status)
{
g_usbd_msc[busid].csw.dSignature = MSC_CSW_Signature;
g_usbd_msc[busid].csw.bStatus = CSW_Status;
/* updating the State Machine , so that we wait CSW when this
* transfer is complete, ie when we get a bulk in callback
*/
g_usbd_msc[busid].stage = MSC_WAIT_CSW;
USB_LOG_DBG("Send csw\r\n");
usbd_ep_start_write(busid, mass_ep_data[busid][MSD_IN_EP_IDX].ep_addr, (uint8_t *)&g_usbd_msc[busid].csw, sizeof(struct CSW));
}
static void usbd_msc_send_info(uint8_t busid, uint8_t *buffer, uint8_t size)
{
size = MIN(size, g_usbd_msc[busid].cbw.dDataLength);
/* updating the State Machine , so that we send CSW when this
* transfer is complete, ie when we get a bulk in callback
*/
g_usbd_msc[busid].stage = MSC_SEND_CSW;
usbd_ep_start_write(busid, mass_ep_data[busid][MSD_IN_EP_IDX].ep_addr, buffer, size);
g_usbd_msc[busid].csw.dDataResidue -= size;
g_usbd_msc[busid].csw.bStatus = CSW_STATUS_CMD_PASSED;
}
static bool SCSI_processWrite(uint8_t busid, uint32_t nbytes);
static bool SCSI_processRead(uint8_t busid);
/**
* @brief SCSI_SetSenseData
* Load the last error code in the error list
* @param sKey: Sense Key
* @param ASC: Additional Sense Code
* @retval none
*/
static void SCSI_SetSenseData(uint8_t busid, uint32_t KCQ)
{
g_usbd_msc[busid].sKey = (uint8_t)(KCQ >> 16);
g_usbd_msc[busid].ASC = (uint8_t)(KCQ >> 8);
g_usbd_msc[busid].ASQ = (uint8_t)(KCQ);
}
/**
* @brief SCSI Command list
*
*/
static bool SCSI_testUnitReady(uint8_t busid, uint8_t **data, uint32_t *len)
{
if (g_usbd_msc[busid].cbw.dDataLength != 0U) {
SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
return false;
}
*data = NULL;
*len = 0;
return true;
}
static bool SCSI_requestSense(uint8_t busid, uint8_t **data, uint32_t *len)
{
uint8_t data_len = SCSIRESP_FIXEDSENSEDATA_SIZEOF;
if (g_usbd_msc[busid].cbw.dDataLength == 0U) {
SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
return false;
}
if (g_usbd_msc[busid].cbw.CB[4] < SCSIRESP_FIXEDSENSEDATA_SIZEOF) {
data_len = g_usbd_msc[busid].cbw.CB[4];
}
uint8_t request_sense[SCSIRESP_FIXEDSENSEDATA_SIZEOF] = {
0x70,
0x00,
0x00, /* Sense Key */
0x00,
0x00,
0x00,
0x00,
SCSIRESP_FIXEDSENSEDATA_SIZEOF - 8,
0x00,
0x00,
0x00,
0x00,
0x00, /* Additional Sense Code */
0x00, /* Additional Sense Request */
0x00,
0x00,
0x00,
0x00,
};
request_sense[2] = g_usbd_msc[busid].sKey;
request_sense[12] = g_usbd_msc[busid].ASC;
request_sense[13] = g_usbd_msc[busid].ASQ;
#if 0
request_sense[ 2] = 0x06; /* UNIT ATTENTION */
request_sense[12] = 0x28; /* Additional Sense Code: Not ready to ready transition */
request_sense[13] = 0x00; /* Additional Sense Code Qualifier */
#endif
#if 0
request_sense[ 2] = 0x02; /* NOT READY */
request_sense[12] = 0x3A; /* Additional Sense Code: Medium not present */
request_sense[13] = 0x00; /* Additional Sense Code Qualifier */
#endif
#if 0
request_sense[ 2] = 0x05; /* ILLEGAL REQUEST */
request_sense[12] = 0x20; /* Additional Sense Code: Invalid command */
request_sense[13] = 0x00; /* Additional Sense Code Qualifier */
#endif
#if 0
request_sense[ 2] = 0x00; /* NO SENSE */
request_sense[12] = 0x00; /* Additional Sense Code: No additional code */
request_sense[13] = 0x00; /* Additional Sense Code Qualifier */
#endif
memcpy(*data, (uint8_t *)request_sense, data_len);
*len = data_len;
return true;
}
static bool SCSI_inquiry(uint8_t busid, uint8_t **data, uint32_t *len)
{
uint8_t data_len = SCSIRESP_INQUIRY_SIZEOF;
uint8_t inquiry00[6] = {
0x00,
0x00,
0x00,
(0x06 - 4U),
0x00,
0x80
};
/* USB Mass storage VPD Page 0x80 Inquiry Data for Unit Serial Number */
uint8_t inquiry80[8] = {
0x00,
0x80,
0x00,
0x08,
0x20, /* Put Product Serial number */
0x20,
0x20,
0x20
};
uint8_t inquiry[SCSIRESP_INQUIRY_SIZEOF] = {
/* 36 */
/* LUN 0 */
0x00,
0x80,
0x02,
0x02,
(SCSIRESP_INQUIRY_SIZEOF - 5),
0x00,
0x00,
0x00,
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', /* Manufacturer : 8 bytes */
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', /* Product : 16 Bytes */
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
' ', ' ', ' ', ' ' /* Version : 4 Bytes */
};
memcpy(&inquiry[8], CONFIG_USBDEV_MSC_MANUFACTURER_STRING, strlen(CONFIG_USBDEV_MSC_MANUFACTURER_STRING));
memcpy(&inquiry[16], CONFIG_USBDEV_MSC_PRODUCT_STRING, strlen(CONFIG_USBDEV_MSC_PRODUCT_STRING));
memcpy(&inquiry[32], CONFIG_USBDEV_MSC_VERSION_STRING, strlen(CONFIG_USBDEV_MSC_VERSION_STRING));
if (g_usbd_msc[busid].cbw.dDataLength == 0U) {
SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
return false;
}
if ((g_usbd_msc[busid].cbw.CB[1] & 0x01U) != 0U) { /* Evpd is set */
if (g_usbd_msc[busid].cbw.CB[2] == 0U) { /* Request for Supported Vital Product Data Pages*/
data_len = 0x06;
memcpy(*data, (uint8_t *)inquiry00, data_len);
} else if (g_usbd_msc[busid].cbw.CB[2] == 0x80U) { /* Request for VPD page 0x80 Unit Serial Number */
data_len = 0x08;
memcpy(*data, (uint8_t *)inquiry80, data_len);
} else { /* Request Not supported */
SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDFIELDINCBA);
return false;
}
} else {
if (g_usbd_msc[busid].cbw.CB[4] < SCSIRESP_INQUIRY_SIZEOF) {
data_len = g_usbd_msc[busid].cbw.CB[4];
}
memcpy(*data, (uint8_t *)inquiry, data_len);
}
*len = data_len;
return true;
}
static bool SCSI_startStopUnit(uint8_t busid, uint8_t **data, uint32_t *len)
{
if (g_usbd_msc[busid].cbw.dDataLength != 0U) {
SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
return false;
}
if ((g_usbd_msc[busid].cbw.CB[4] & 0x3U) == 0x1U) /* START=1 */
{
//SCSI_MEDIUM_UNLOCKED;
} else if ((g_usbd_msc[busid].cbw.CB[4] & 0x3U) == 0x2U) /* START=0 and LOEJ Load Eject=1 */
{
//SCSI_MEDIUM_EJECTED;
g_usbd_msc[busid].popup = true;
} else if ((g_usbd_msc[busid].cbw.CB[4] & 0x3U) == 0x3U) /* START=1 and LOEJ Load Eject=1 */
{
//SCSI_MEDIUM_UNLOCKED;
} else {
}
*data = NULL;
*len = 0;
return true;
}
static bool SCSI_preventAllowMediaRemoval(uint8_t busid, uint8_t **data, uint32_t *len)
{
if (g_usbd_msc[busid].cbw.dDataLength != 0U) {
SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
return false;
}
if (g_usbd_msc[busid].cbw.CB[4] == 0U) {
//SCSI_MEDIUM_UNLOCKED;
} else {
//SCSI_MEDIUM_LOCKED;
}
*data = NULL;
*len = 0;
return true;
}
static bool SCSI_modeSense6(uint8_t busid, uint8_t **data, uint32_t *len)
{
uint8_t data_len = 4;
if (g_usbd_msc[busid].cbw.dDataLength == 0U) {
SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
return false;
}
if (g_usbd_msc[busid].cbw.CB[4] < SCSIRESP_MODEPARAMETERHDR6_SIZEOF) {
data_len = g_usbd_msc[busid].cbw.CB[4];
}
uint8_t sense6[SCSIRESP_MODEPARAMETERHDR6_SIZEOF] = { 0x03, 0x00, 0x00, 0x00 };
if (g_usbd_msc[busid].readonly) {
sense6[2] = 0x80;
}
memcpy(*data, (uint8_t *)sense6, data_len);
*len = data_len;
return true;
}
static bool SCSI_modeSense10(uint8_t busid, uint8_t **data, uint32_t *len)
{
uint8_t data_len = 27;
if (g_usbd_msc[busid].cbw.dDataLength == 0U) {
SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
return false;
}
if (g_usbd_msc[busid].cbw.CB[8] < 27) {
data_len = g_usbd_msc[busid].cbw.CB[8];
}
uint8_t sense10[27] = {
0x00,
0x26,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x08,
0x12,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00
};
memcpy(*data, (uint8_t *)sense10, data_len);
*len = data_len;
return true;
}
static bool SCSI_readFormatCapacity(uint8_t busid, uint8_t **data, uint32_t *len)
{
if (g_usbd_msc[busid].cbw.dDataLength == 0U) {
SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
return false;
}
uint8_t format_capacity[SCSIRESP_READFORMATCAPACITIES_SIZEOF] = {
0x00,
0x00,
0x00,
0x08, /* Capacity List Length */
(uint8_t)((g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN] >> 24) & 0xff),
(uint8_t)((g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN] >> 16) & 0xff),
(uint8_t)((g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN] >> 8) & 0xff),
(uint8_t)((g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN] >> 0) & 0xff),
0x02, /* Descriptor Code: Formatted Media */
0x00,
(uint8_t)((g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN] >> 8) & 0xff),
(uint8_t)((g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN] >> 0) & 0xff),
};
memcpy(*data, (uint8_t *)format_capacity, SCSIRESP_READFORMATCAPACITIES_SIZEOF);
*len = SCSIRESP_READFORMATCAPACITIES_SIZEOF;
return true;
}
static bool SCSI_readCapacity10(uint8_t busid, uint8_t **data, uint32_t *len)
{
if (g_usbd_msc[busid].cbw.dDataLength == 0U) {
SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
return false;
}
uint8_t capacity10[SCSIRESP_READCAPACITY10_SIZEOF] = {
(uint8_t)(((g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN] - 1) >> 24) & 0xff),
(uint8_t)(((g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN] - 1) >> 16) & 0xff),
(uint8_t)(((g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN] - 1) >> 8) & 0xff),
(uint8_t)(((g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN] - 1) >> 0) & 0xff),
(uint8_t)((g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN] >> 24) & 0xff),
(uint8_t)((g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN] >> 16) & 0xff),
(uint8_t)((g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN] >> 8) & 0xff),
(uint8_t)((g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN] >> 0) & 0xff),
};
memcpy(*data, (uint8_t *)capacity10, SCSIRESP_READCAPACITY10_SIZEOF);
*len = SCSIRESP_READCAPACITY10_SIZEOF;
return true;
}
static bool SCSI_read10(uint8_t busid, uint8_t **data, uint32_t *len)
{
if (((g_usbd_msc[busid].cbw.bmFlags & 0x80U) != 0x80U) || (g_usbd_msc[busid].cbw.dDataLength == 0U)) {
SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
return false;
}
g_usbd_msc[busid].start_sector = GET_BE32(&g_usbd_msc[busid].cbw.CB[2]); /* Logical Block Address of First Block */
USB_LOG_DBG("lba: 0x%04x\r\n", g_usbd_msc[busid].start_sector);
g_usbd_msc[busid].nsectors = GET_BE16(&g_usbd_msc[busid].cbw.CB[7]); /* Number of Blocks to transfer */
USB_LOG_DBG("nsectors: 0x%02x\r\n", g_usbd_msc[busid].nsectors);
if ((g_usbd_msc[busid].start_sector + g_usbd_msc[busid].nsectors) > g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN]) {
SCSI_SetSenseData(busid, SCSI_KCQIR_LBAOUTOFRANGE);
USB_LOG_ERR("LBA out of range\r\n");
return false;
}
if (g_usbd_msc[busid].cbw.dDataLength != (g_usbd_msc[busid].nsectors * g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN])) {
USB_LOG_ERR("scsi_blk_len does not match with dDataLength\r\n");
return false;
}
g_usbd_msc[busid].stage = MSC_DATA_IN;
#ifdef CONFIG_USBDEV_MSC_THREAD
usb_osal_mq_send(g_usbd_msc[busid].usbd_msc_mq, MSC_DATA_IN);
return true;
#else
return SCSI_processRead(busid);
#endif
}
static bool SCSI_read12(uint8_t busid, uint8_t **data, uint32_t *len)
{
if (((g_usbd_msc[busid].cbw.bmFlags & 0x80U) != 0x80U) || (g_usbd_msc[busid].cbw.dDataLength == 0U)) {
SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
return false;
}
g_usbd_msc[busid].start_sector = GET_BE32(&g_usbd_msc[busid].cbw.CB[2]); /* Logical Block Address of First Block */
USB_LOG_DBG("lba: 0x%04x\r\n", g_usbd_msc[busid].start_sector);
g_usbd_msc[busid].nsectors = GET_BE32(&g_usbd_msc[busid].cbw.CB[6]); /* Number of Blocks to transfer */
USB_LOG_DBG("nsectors: 0x%02x\r\n", g_usbd_msc[busid].nsectors);
if ((g_usbd_msc[busid].start_sector + g_usbd_msc[busid].nsectors) > g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN]) {
SCSI_SetSenseData(busid, SCSI_KCQIR_LBAOUTOFRANGE);
USB_LOG_ERR("LBA out of range\r\n");
return false;
}
if (g_usbd_msc[busid].cbw.dDataLength != (g_usbd_msc[busid].nsectors * g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN])) {
USB_LOG_ERR("scsi_blk_len does not match with dDataLength\r\n");
return false;
}
g_usbd_msc[busid].stage = MSC_DATA_IN;
#ifdef CONFIG_USBDEV_MSC_THREAD
usb_osal_mq_send(g_usbd_msc[busid].usbd_msc_mq, MSC_DATA_IN);
return true;
#else
return SCSI_processRead(busid);
#endif
}
static bool SCSI_write10(uint8_t busid, uint8_t **data, uint32_t *len)
{
uint32_t data_len = 0;
if (((g_usbd_msc[busid].cbw.bmFlags & 0x80U) != 0x00U) || (g_usbd_msc[busid].cbw.dDataLength == 0U)) {
SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
return false;
}
g_usbd_msc[busid].start_sector = GET_BE32(&g_usbd_msc[busid].cbw.CB[2]); /* Logical Block Address of First Block */
USB_LOG_DBG("lba: 0x%04x\r\n", g_usbd_msc[busid].start_sector);
g_usbd_msc[busid].nsectors = GET_BE16(&g_usbd_msc[busid].cbw.CB[7]); /* Number of Blocks to transfer */
USB_LOG_DBG("nsectors: 0x%02x\r\n", g_usbd_msc[busid].nsectors);
data_len = g_usbd_msc[busid].nsectors * g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN];
if ((g_usbd_msc[busid].start_sector + g_usbd_msc[busid].nsectors) > g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN]) {
USB_LOG_ERR("LBA out of range\r\n");
return false;
}
if (g_usbd_msc[busid].cbw.dDataLength != data_len) {
return false;
}
g_usbd_msc[busid].stage = MSC_DATA_OUT;
data_len = MIN(data_len, CONFIG_USBDEV_MSC_MAX_BUFSIZE);
usbd_ep_start_read(busid, mass_ep_data[busid][MSD_OUT_EP_IDX].ep_addr, g_usbd_msc[busid].block_buffer, data_len);
return true;
}
static bool SCSI_write12(uint8_t busid, uint8_t **data, uint32_t *len)
{
uint32_t data_len = 0;
if (((g_usbd_msc[busid].cbw.bmFlags & 0x80U) != 0x00U) || (g_usbd_msc[busid].cbw.dDataLength == 0U)) {
SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
return false;
}
g_usbd_msc[busid].start_sector = GET_BE32(&g_usbd_msc[busid].cbw.CB[2]); /* Logical Block Address of First Block */
USB_LOG_DBG("lba: 0x%04x\r\n", g_usbd_msc[busid].start_sector);
g_usbd_msc[busid].nsectors = GET_BE32(&g_usbd_msc[busid].cbw.CB[6]); /* Number of Blocks to transfer */
USB_LOG_DBG("nsectors: 0x%02x\r\n", g_usbd_msc[busid].nsectors);
data_len = g_usbd_msc[busid].nsectors * g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN];
if ((g_usbd_msc[busid].start_sector + g_usbd_msc[busid].nsectors) > g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN]) {
USB_LOG_ERR("LBA out of range\r\n");
return false;
}
if (g_usbd_msc[busid].cbw.dDataLength != data_len) {
return false;
}
g_usbd_msc[busid].stage = MSC_DATA_OUT;
data_len = MIN(data_len, CONFIG_USBDEV_MSC_MAX_BUFSIZE);
usbd_ep_start_read(busid, mass_ep_data[busid][MSD_OUT_EP_IDX].ep_addr, g_usbd_msc[busid].block_buffer, data_len);
return true;
}
/* do not use verify to reduce code size */
#if 0
static bool SCSI_verify10(uint8_t busid, uint8_t **data, uint32_t *len)
{
/* Logical Block Address of First Block */
uint32_t lba = 0;
uint32_t blk_num = 0;
if ((g_usbd_msc[busid].cbw.CB[1] & 0x02U) == 0x00U) {
return true;
}
if (((g_usbd_msc[busid].cbw.bmFlags & 0x80U) != 0x00U) || (g_usbd_msc[busid].cbw.dDataLength == 0U)) {
SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
return false;
}
if ((g_usbd_msc[busid].cbw.CB[1] & 0x02U) == 0x02U) {
SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDFIELDINCBA);
return false; /* Error, Verify Mode Not supported*/
}
lba = GET_BE32(&g_usbd_msc[busid].cbw.CB[2]);
USB_LOG_DBG("lba: 0x%x\r\n", lba);
g_usbd_msc[busid].scsi_blk_addr = lba * g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN];
/* Number of Blocks to transfer */
blk_num = GET_BE16(&g_usbd_msc[busid].cbw.CB[7]);
USB_LOG_DBG("num (block) : 0x%x\r\n", blk_num);
g_usbd_msc[busid].scsi_blk_len = blk_num * g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN];
if ((lba + blk_num) > g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN]) {
USB_LOG_ERR("LBA out of range\r\n");
return false;
}
if (g_usbd_msc[busid].cbw.dDataLength != g_usbd_msc[busid].scsi_blk_len) {
return false;
}
g_usbd_msc[busid].stage = MSC_DATA_OUT;
return true;
}
#endif
static bool SCSI_processRead(uint8_t busid)
{
uint32_t transfer_len;
USB_LOG_DBG("read lba:%d\r\n", g_usbd_msc[busid].start_sector);
transfer_len = MIN(g_usbd_msc[busid].nsectors * g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN], CONFIG_USBDEV_MSC_MAX_BUFSIZE);
if (usbd_msc_sector_read(busid, g_usbd_msc[busid].cbw.bLUN, g_usbd_msc[busid].start_sector, g_usbd_msc[busid].block_buffer, transfer_len) != 0) {
SCSI_SetSenseData(busid, SCSI_KCQHE_UREINRESERVEDAREA);
return false;
}
g_usbd_msc[busid].start_sector += (transfer_len / g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN]);
g_usbd_msc[busid].nsectors -= (transfer_len / g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN]);
g_usbd_msc[busid].csw.dDataResidue -= transfer_len;
if (g_usbd_msc[busid].nsectors == 0) {
g_usbd_msc[busid].stage = MSC_SEND_CSW;
}
usbd_ep_start_write(busid, mass_ep_data[busid][MSD_IN_EP_IDX].ep_addr, g_usbd_msc[busid].block_buffer, transfer_len);
return true;
}
static bool SCSI_processWrite(uint8_t busid, uint32_t nbytes)
{
uint32_t data_len = 0;
USB_LOG_DBG("write lba:%d\r\n", g_usbd_msc[busid].start_sector);
if (usbd_msc_sector_write(busid, g_usbd_msc[busid].cbw.bLUN, g_usbd_msc[busid].start_sector, g_usbd_msc[busid].block_buffer, nbytes) != 0) {
SCSI_SetSenseData(busid, SCSI_KCQHE_WRITEFAULT);
return false;
}
g_usbd_msc[busid].start_sector += (nbytes / g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN]);
g_usbd_msc[busid].nsectors -= (nbytes / g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN]);
g_usbd_msc[busid].csw.dDataResidue -= nbytes;
if (g_usbd_msc[busid].nsectors == 0) {
usbd_msc_send_csw(busid, CSW_STATUS_CMD_PASSED);
} else {
data_len = MIN(g_usbd_msc[busid].nsectors * g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN], CONFIG_USBDEV_MSC_MAX_BUFSIZE);
usbd_ep_start_read(busid, mass_ep_data[busid][MSD_OUT_EP_IDX].ep_addr, g_usbd_msc[busid].block_buffer, data_len);
}
return true;
}
static bool SCSI_CBWDecode(uint8_t busid, uint32_t nbytes)
{
uint8_t *buf2send = g_usbd_msc[busid].block_buffer;
uint32_t len2send = 0;
bool ret = false;
if (nbytes != sizeof(struct CBW)) {
USB_LOG_ERR("size != sizeof(cbw)\r\n");
SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
return false;
}
g_usbd_msc[busid].csw.dTag = g_usbd_msc[busid].cbw.dTag;
g_usbd_msc[busid].csw.dDataResidue = g_usbd_msc[busid].cbw.dDataLength;
if ((g_usbd_msc[busid].cbw.dSignature != MSC_CBW_Signature) || (g_usbd_msc[busid].cbw.bCBLength < 1) || (g_usbd_msc[busid].cbw.bCBLength > 16)) {
SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
return false;
} else {
USB_LOG_DBG("Decode CB:0x%02x\r\n", g_usbd_msc[busid].cbw.CB[0]);
switch (g_usbd_msc[busid].cbw.CB[0]) {
case SCSI_CMD_TESTUNITREADY:
ret = SCSI_testUnitReady(busid, &buf2send, &len2send);
break;
case SCSI_CMD_REQUESTSENSE:
ret = SCSI_requestSense(busid, &buf2send, &len2send);
break;
case SCSI_CMD_INQUIRY:
ret = SCSI_inquiry(busid, &buf2send, &len2send);
break;
case SCSI_CMD_STARTSTOPUNIT:
ret = SCSI_startStopUnit(busid, &buf2send, &len2send);
break;
case SCSI_CMD_PREVENTMEDIAREMOVAL:
ret = SCSI_preventAllowMediaRemoval(busid, &buf2send, &len2send);
break;
case SCSI_CMD_MODESENSE6:
ret = SCSI_modeSense6(busid, &buf2send, &len2send);
break;
case SCSI_CMD_MODESENSE10:
ret = SCSI_modeSense10(busid, &buf2send, &len2send);
break;
case SCSI_CMD_READFORMATCAPACITIES:
ret = SCSI_readFormatCapacity(busid, &buf2send, &len2send);
break;
case SCSI_CMD_READCAPACITY10:
ret = SCSI_readCapacity10(busid, &buf2send, &len2send);
break;
case SCSI_CMD_READ10:
ret = SCSI_read10(busid, NULL, 0);
break;
case SCSI_CMD_READ12:
ret = SCSI_read12(busid, NULL, 0);
break;
case SCSI_CMD_WRITE10:
ret = SCSI_write10(busid, NULL, 0);
break;
case SCSI_CMD_WRITE12:
ret = SCSI_write12(busid, NULL, 0);
break;
case SCSI_CMD_VERIFY10:
//ret = SCSI_verify10(NULL, 0);
ret = false;
break;
default:
SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
USB_LOG_WRN("unsupported cmd:0x%02x\r\n", g_usbd_msc[busid].cbw.CB[0]);
ret = false;
break;
}
}
if (ret) {
if (g_usbd_msc[busid].stage == MSC_READ_CBW) {
if (len2send) {
USB_LOG_DBG("Send info len:%d\r\n", len2send);
usbd_msc_send_info(busid, buf2send, len2send);
} else {
usbd_msc_send_csw(busid, CSW_STATUS_CMD_PASSED);
}
}
}
return ret;
}
void mass_storage_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
switch (g_usbd_msc[busid].stage) {
case MSC_READ_CBW:
if (SCSI_CBWDecode(busid, nbytes) == false) {
USB_LOG_ERR("Command:0x%02x decode err\r\n", g_usbd_msc[busid].cbw.CB[0]);
usbd_msc_bot_abort(busid);
return;
}
break;
case MSC_DATA_OUT:
switch (g_usbd_msc[busid].cbw.CB[0]) {
case SCSI_CMD_WRITE10:
case SCSI_CMD_WRITE12:
#ifdef CONFIG_USBDEV_MSC_THREAD
g_usbd_msc[busid].nbytes = nbytes;
usb_osal_mq_send(g_usbd_msc[busid].usbd_msc_mq, MSC_DATA_OUT);
#else
if (SCSI_processWrite(busid, nbytes) == false) {
usbd_msc_send_csw(busid, CSW_STATUS_CMD_FAILED); /* send fail status to host,and the host will retry*/
}
#endif
break;
default:
break;
}
break;
default:
break;
}
}
void mass_storage_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
switch (g_usbd_msc[busid].stage) {
case MSC_DATA_IN:
switch (g_usbd_msc[busid].cbw.CB[0]) {
case SCSI_CMD_READ10:
case SCSI_CMD_READ12:
#ifdef CONFIG_USBDEV_MSC_THREAD
usb_osal_mq_send(g_usbd_msc[busid].usbd_msc_mq, MSC_DATA_IN);
#else
if (SCSI_processRead(busid) == false) {
usbd_msc_send_csw(busid, CSW_STATUS_CMD_FAILED); /* send fail status to host,and the host will retry*/
return;
}
#endif
break;
default:
break;
}
break;
/*the device has to send a CSW*/
case MSC_SEND_CSW:
usbd_msc_send_csw(busid, CSW_STATUS_CMD_PASSED);
break;
/*the host has received the CSW*/
case MSC_WAIT_CSW:
g_usbd_msc[busid].stage = MSC_READ_CBW;
USB_LOG_DBG("Start reading cbw\r\n");
usbd_ep_start_read(busid, mass_ep_data[busid][MSD_OUT_EP_IDX].ep_addr, (uint8_t *)&g_usbd_msc[busid].cbw, USB_SIZEOF_MSC_CBW);
break;
default:
break;
}
}
#ifdef CONFIG_USBDEV_MSC_THREAD
static void usbdev_msc_thread(void *argument)
{
uintptr_t event;
int ret;
uint8_t busid = (uint8_t)argument;
while (1) {
ret = usb_osal_mq_recv(g_usbd_msc[busid].usbd_msc_mq, (uintptr_t *)&event, USB_OSAL_WAITING_FOREVER);
if (ret < 0) {
continue;
}
USB_LOG_DBG("%d\r\n", event);
if (event == MSC_DATA_OUT) {
if (SCSI_processWrite(busid, g_usbd_msc[busid].nbytes) == false) {
usbd_msc_send_csw(busid, CSW_STATUS_CMD_FAILED); /* send fail status to host,and the host will retry*/
}
} else if (event == MSC_DATA_IN) {
if (SCSI_processRead(busid) == false) {
usbd_msc_send_csw(busid, CSW_STATUS_CMD_FAILED); /* send fail status to host,and the host will retry*/
}
} else {
}
}
}
#endif
struct usbd_interface *usbd_msc_init_intf(uint8_t busid, struct usbd_interface *intf, const uint8_t out_ep, const uint8_t in_ep)
{
intf->class_interface_handler = msc_storage_class_interface_request_handler;
intf->class_endpoint_handler = NULL;
intf->vendor_handler = NULL;
intf->notify_handler = msc_storage_notify_handler;
mass_ep_data[busid][MSD_OUT_EP_IDX].ep_addr = out_ep;
mass_ep_data[busid][MSD_OUT_EP_IDX].ep_cb = mass_storage_bulk_out;
mass_ep_data[busid][MSD_IN_EP_IDX].ep_addr = in_ep;
mass_ep_data[busid][MSD_IN_EP_IDX].ep_cb = mass_storage_bulk_in;
usbd_add_endpoint(busid, &mass_ep_data[busid][MSD_OUT_EP_IDX]);
usbd_add_endpoint(busid, &mass_ep_data[busid][MSD_IN_EP_IDX]);
memset((uint8_t *)&g_usbd_msc[busid], 0, sizeof(struct usbd_msc_priv));
usdb_msc_set_max_lun(busid);
for (uint8_t i = 0u; i <= g_usbd_msc[busid].max_lun; i++) {
usbd_msc_get_cap(busid, i, &g_usbd_msc[busid].scsi_blk_nbr[i], &g_usbd_msc[busid].scsi_blk_size[i]);
if (g_usbd_msc[busid].scsi_blk_size[i] > CONFIG_USBDEV_MSC_MAX_BUFSIZE) {
USB_LOG_ERR("msc block buffer overflow\r\n");
return NULL;
}
}
return intf;
}
void usbd_msc_set_readonly(uint8_t busid, bool readonly)
{
g_usbd_msc[busid].readonly = readonly;
}
bool usbd_msc_set_popup(uint8_t busid)
{
return g_usbd_msc[busid].popup;
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2022, sakumisu
* Copyright (c) 2024, zhihong chen
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBD_MSC_H
#define USBD_MSC_H
#include "usb_msc.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Init msc interface driver */
struct usbd_interface *usbd_msc_init_intf(uint8_t busid, struct usbd_interface *intf,
const uint8_t out_ep,
const uint8_t in_ep);
void usbd_msc_get_cap(uint8_t busid, uint8_t lun, uint32_t *block_num, uint32_t *block_size);
int usbd_msc_sector_read(uint8_t busid, uint8_t lun, uint32_t sector, uint8_t *buffer, uint32_t length);
int usbd_msc_sector_write(uint8_t busid, uint8_t lun, uint32_t sector, uint8_t *buffer, uint32_t length);
void usbd_msc_set_readonly(uint8_t busid, bool readonly);
bool usbd_msc_set_popup(uint8_t busid);
#ifdef __cplusplus
}
#endif
#endif /* USBD_MSC_H */

View File

@@ -0,0 +1,443 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbh_core.h"
#include "usbh_msc.h"
#include "usb_scsi.h"
#undef USB_DBG_TAG
#define USB_DBG_TAG "usbh_msc"
#include "usb_log.h"
#define DEV_FORMAT "/dev/sd%c"
#define MSC_INQUIRY_TIMEOUT 500
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_msc_buf[64];
static struct usbh_msc g_msc_class[CONFIG_USBHOST_MAX_MSC_CLASS];
static uint32_t g_devinuse = 0;
static struct usbh_msc_modeswitch_config *g_msc_modeswitch_config = NULL;
static struct usbh_msc *usbh_msc_class_alloc(void)
{
int devno;
for (devno = 0; devno < CONFIG_USBHOST_MAX_MSC_CLASS; devno++) {
if ((g_devinuse & (1 << devno)) == 0) {
g_devinuse |= (1 << devno);
memset(&g_msc_class[devno], 0, sizeof(struct usbh_msc));
g_msc_class[devno].sdchar = 'a' + devno;
return &g_msc_class[devno];
}
}
return NULL;
}
static void usbh_msc_class_free(struct usbh_msc *msc_class)
{
int devno = msc_class->sdchar - 'a';
if (devno >= 0 && devno < 32) {
g_devinuse &= ~(1 << devno);
}
memset(msc_class, 0, sizeof(struct usbh_msc));
}
static int usbh_msc_get_maxlun(struct usbh_msc *msc_class, uint8_t *buffer)
{
struct usb_setup_packet *setup;
if (!msc_class || !msc_class->hport) {
return -USB_ERR_INVAL;
}
setup = msc_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = MSC_REQUEST_GET_MAX_LUN;
setup->wValue = 0;
setup->wIndex = msc_class->intf;
setup->wLength = 1;
return usbh_control_transfer(msc_class->hport, setup, buffer);
}
static void usbh_msc_cbw_dump(struct CBW *cbw)
{
int i;
USB_LOG_DBG("CBW:\r\n");
USB_LOG_DBG(" signature: 0x%08x\r\n", (unsigned int)cbw->dSignature);
USB_LOG_DBG(" tag: 0x%08x\r\n", (unsigned int)cbw->dTag);
USB_LOG_DBG(" datlen: 0x%08x\r\n", (unsigned int)cbw->dDataLength);
USB_LOG_DBG(" flags: 0x%02x\r\n", cbw->bmFlags);
USB_LOG_DBG(" lun: 0x%02x\r\n", cbw->bLUN);
USB_LOG_DBG(" cblen: 0x%02x\r\n", cbw->bCBLength);
USB_LOG_DBG("CB:\r\n");
for (i = 0; i < cbw->bCBLength; i += 8) {
USB_LOG_DBG(" 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\r\n",
cbw->CB[i], cbw->CB[i + 1], cbw->CB[i + 2],
cbw->CB[i + 3], cbw->CB[i + 4], cbw->CB[i + 5],
cbw->CB[i + 6], cbw->CB[i + 7]);
}
}
static void usbh_msc_csw_dump(struct CSW *csw)
{
USB_LOG_DBG("CSW:\r\n");
USB_LOG_DBG(" signature: 0x%08x\r\n", (unsigned int)csw->dSignature);
USB_LOG_DBG(" tag: 0x%08x\r\n", (unsigned int)csw->dTag);
USB_LOG_DBG(" residue: 0x%08x\r\n", (unsigned int)csw->dDataResidue);
USB_LOG_DBG(" status: 0x%02x\r\n", csw->bStatus);
}
static inline int usbh_msc_bulk_in_transfer(struct usbh_msc *msc_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
{
int ret;
struct usbh_urb *urb = &msc_class->bulkin_urb;
usbh_bulk_urb_fill(urb, msc_class->hport, msc_class->bulkin, buffer, buflen, timeout, NULL, NULL);
ret = usbh_submit_urb(urb);
if (ret == 0) {
ret = urb->actual_length;
}
return ret;
}
static inline int usbh_msc_bulk_out_transfer(struct usbh_msc *msc_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
{
int ret;
struct usbh_urb *urb = &msc_class->bulkout_urb;
usbh_bulk_urb_fill(urb, msc_class->hport, msc_class->bulkout, buffer, buflen, timeout, NULL, NULL);
ret = usbh_submit_urb(urb);
if (ret == 0) {
ret = urb->actual_length;
}
return ret;
}
static int usbh_bulk_cbw_csw_xfer(struct usbh_msc *msc_class, struct CBW *cbw, struct CSW *csw, uint8_t *buffer, uint32_t timeout)
{
int nbytes;
usbh_msc_cbw_dump(cbw);
/* Send the CBW */
nbytes = usbh_msc_bulk_out_transfer(msc_class, (uint8_t *)cbw, USB_SIZEOF_MSC_CBW, timeout);
if (nbytes < 0) {
USB_LOG_ERR("cbw transfer error\r\n");
goto __err_exit;
}
if (cbw->dDataLength != 0) {
if (cbw->CB[0] == SCSI_CMD_WRITE10) {
nbytes = usbh_msc_bulk_out_transfer(msc_class, buffer, cbw->dDataLength, timeout);
} else if (cbw->CB[0] == SCSI_CMD_READCAPACITY10) {
nbytes = usbh_msc_bulk_in_transfer(msc_class, buffer, cbw->dDataLength, timeout);
if (nbytes >= 0) {
/* Save the capacity information */
msc_class->blocknum = GET_BE32(&buffer[0]) + 1;
msc_class->blocksize = GET_BE32(&buffer[4]);
}
} else {
nbytes = usbh_msc_bulk_in_transfer(msc_class, buffer, cbw->dDataLength, timeout);
}
if (nbytes < 0) {
USB_LOG_ERR("msc data transfer error\r\n");
goto __err_exit;
}
}
/* Receive the CSW */
memset(csw, 0, USB_SIZEOF_MSC_CSW);
nbytes = usbh_msc_bulk_in_transfer(msc_class, (uint8_t *)csw, USB_SIZEOF_MSC_CSW, timeout);
if (nbytes < 0) {
USB_LOG_ERR("csw transfer error\r\n");
goto __err_exit;
}
usbh_msc_csw_dump(csw);
/* check csw status */
if (csw->dSignature != MSC_CSW_Signature) {
USB_LOG_ERR("csw signature error\r\n");
return -USB_ERR_INVAL;
}
if (csw->bStatus != 0) {
USB_LOG_ERR("csw bStatus %d\r\n", csw->bStatus);
return -USB_ERR_INVAL;
}
__err_exit:
return nbytes < 0 ? (int)nbytes : 0;
}
static inline int usbh_msc_scsi_testunitready(struct usbh_msc *msc_class)
{
struct CBW *cbw;
/* Construct the CBW */
cbw = (struct CBW *)g_msc_buf;
memset(cbw, 0, USB_SIZEOF_MSC_CBW);
cbw->dSignature = MSC_CBW_Signature;
cbw->bCBLength = SCSICMD_TESTUNITREADY_SIZEOF;
cbw->CB[0] = SCSI_CMD_TESTUNITREADY;
return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf, NULL, MSC_INQUIRY_TIMEOUT);
}
static inline int usbh_msc_scsi_requestsense(struct usbh_msc *msc_class)
{
struct CBW *cbw;
/* Construct the CBW */
cbw = (struct CBW *)g_msc_buf;
memset(cbw, 0, USB_SIZEOF_MSC_CBW);
cbw->dSignature = MSC_CBW_Signature;
cbw->bmFlags = 0x80;
cbw->dDataLength = SCSIRESP_FIXEDSENSEDATA_SIZEOF;
cbw->bCBLength = SCSICMD_REQUESTSENSE_SIZEOF;
cbw->CB[0] = SCSI_CMD_REQUESTSENSE;
cbw->CB[4] = SCSIRESP_FIXEDSENSEDATA_SIZEOF;
return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf, g_msc_buf, MSC_INQUIRY_TIMEOUT);
}
static inline int usbh_msc_scsi_inquiry(struct usbh_msc *msc_class)
{
struct CBW *cbw;
/* Construct the CBW */
cbw = (struct CBW *)g_msc_buf;
memset(cbw, 0, USB_SIZEOF_MSC_CBW);
cbw->dSignature = MSC_CBW_Signature;
cbw->dDataLength = SCSIRESP_INQUIRY_SIZEOF;
cbw->bmFlags = 0x80;
cbw->bCBLength = SCSICMD_INQUIRY_SIZEOF;
cbw->CB[0] = SCSI_CMD_INQUIRY;
cbw->CB[4] = SCSIRESP_INQUIRY_SIZEOF;
return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf, g_msc_buf, MSC_INQUIRY_TIMEOUT);
}
static inline int usbh_msc_scsi_readcapacity10(struct usbh_msc *msc_class)
{
struct CBW *cbw;
/* Construct the CBW */
cbw = (struct CBW *)g_msc_buf;
memset(cbw, 0, USB_SIZEOF_MSC_CBW);
cbw->dSignature = MSC_CBW_Signature;
cbw->dDataLength = SCSIRESP_READCAPACITY10_SIZEOF;
cbw->bmFlags = 0x80;
cbw->bCBLength = SCSICMD_READCAPACITY10_SIZEOF;
cbw->CB[0] = SCSI_CMD_READCAPACITY10;
return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf, g_msc_buf, MSC_INQUIRY_TIMEOUT);
}
static inline void usbh_msc_modeswitch(struct usbh_msc *msc_class, const uint8_t *message)
{
struct CBW *cbw;
/* Construct the CBW */
cbw = (struct CBW *)g_msc_buf;
memcpy(g_msc_buf, message, 31);
usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf, NULL, MSC_INQUIRY_TIMEOUT);
}
static int usbh_msc_connect(struct usbh_hubport *hport, uint8_t intf)
{
struct usb_endpoint_descriptor *ep_desc;
int ret;
struct usbh_msc_modeswitch_config *config;
struct usbh_msc *msc_class = usbh_msc_class_alloc();
if (msc_class == NULL) {
USB_LOG_ERR("Fail to alloc msc_class\r\n");
return -USB_ERR_NOMEM;
}
msc_class->hport = hport;
msc_class->intf = intf;
hport->config.intf[intf].priv = msc_class;
ret = usbh_msc_get_maxlun(msc_class, g_msc_buf);
if (ret < 0) {
return ret;
}
USB_LOG_INFO("Get max LUN:%u\r\n", g_msc_buf[0] + 1);
for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
if (ep_desc->bEndpointAddress & 0x80) {
USBH_EP_INIT(msc_class->bulkin, ep_desc);
} else {
USBH_EP_INIT(msc_class->bulkout, ep_desc);
}
}
if (g_msc_modeswitch_config) {
uint8_t num = 0;
while (1) {
config = &g_msc_modeswitch_config[num];
if (config && config->name) {
if ((hport->device_desc.idVendor == config->vid) &&
(hport->device_desc.idProduct == config->pid)) {
USB_LOG_INFO("%s usb_modeswitch enable\r\n", config->name);
usbh_msc_modeswitch(msc_class, config->message_content);
return 0;
}
num++;
} else {
break;
}
}
}
ret = usbh_msc_scsi_testunitready(msc_class);
if (ret < 0) {
ret = usbh_msc_scsi_requestsense(msc_class);
if (ret < 0) {
USB_LOG_ERR("Fail to scsi_testunitready\r\n");
return ret;
}
}
ret = usbh_msc_scsi_inquiry(msc_class);
if (ret < 0) {
USB_LOG_ERR("Fail to scsi_inquiry\r\n");
return ret;
}
ret = usbh_msc_scsi_readcapacity10(msc_class);
if (ret < 0) {
USB_LOG_ERR("Fail to scsi_readcapacity10\r\n");
return ret;
}
if (msc_class->blocksize > 0) {
USB_LOG_INFO("Capacity info:\r\n");
USB_LOG_INFO("Block num:%d,block size:%d\r\n", (unsigned int)msc_class->blocknum, (unsigned int)msc_class->blocksize);
} else {
USB_LOG_ERR("Invalid block size\r\n");
return -USB_ERR_RANGE;
}
snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, msc_class->sdchar);
USB_LOG_INFO("Register MSC Class:%s\r\n", hport->config.intf[intf].devname);
usbh_msc_run(msc_class);
return ret;
}
static int usbh_msc_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
int ret = 0;
struct usbh_msc *msc_class = (struct usbh_msc *)hport->config.intf[intf].priv;
if (msc_class) {
if (msc_class->bulkin) {
usbh_kill_urb(&msc_class->bulkin_urb);
}
if (msc_class->bulkout) {
usbh_kill_urb(&msc_class->bulkout_urb);
}
if (hport->config.intf[intf].devname[0] != '\0') {
USB_LOG_INFO("Unregister MSC Class:%s\r\n", hport->config.intf[intf].devname);
usbh_msc_stop(msc_class);
}
usbh_msc_class_free(msc_class);
}
return ret;
}
int usbh_msc_scsi_write10(struct usbh_msc *msc_class, uint32_t start_sector, const uint8_t *buffer, uint32_t nsectors)
{
struct CBW *cbw;
/* Construct the CBW */
cbw = (struct CBW *)g_msc_buf;
memset(cbw, 0, USB_SIZEOF_MSC_CBW);
cbw->dSignature = MSC_CBW_Signature;
cbw->dDataLength = (msc_class->blocksize * nsectors);
cbw->bCBLength = SCSICMD_WRITE10_SIZEOF;
cbw->CB[0] = SCSI_CMD_WRITE10;
SET_BE32(&cbw->CB[2], start_sector);
SET_BE16(&cbw->CB[7], nsectors);
return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf, (uint8_t *)buffer, CONFIG_USBHOST_MSC_TIMEOUT);
}
int usbh_msc_scsi_read10(struct usbh_msc *msc_class, uint32_t start_sector, const uint8_t *buffer, uint32_t nsectors)
{
struct CBW *cbw;
/* Construct the CBW */
cbw = (struct CBW *)g_msc_buf;
memset(cbw, 0, USB_SIZEOF_MSC_CBW);
cbw->dSignature = MSC_CBW_Signature;
cbw->dDataLength = (msc_class->blocksize * nsectors);
cbw->bmFlags = 0x80;
cbw->bCBLength = SCSICMD_READ10_SIZEOF;
cbw->CB[0] = SCSI_CMD_READ10;
SET_BE32(&cbw->CB[2], start_sector);
SET_BE16(&cbw->CB[7], nsectors);
return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf, (uint8_t *)buffer, CONFIG_USBHOST_MSC_TIMEOUT);
}
void usbh_msc_modeswitch_enable(struct usbh_msc_modeswitch_config *config)
{
if (config) {
g_msc_modeswitch_config = config;
} else {
g_msc_modeswitch_config = NULL;
}
}
__WEAK void usbh_msc_run(struct usbh_msc *msc_class)
{
}
__WEAK void usbh_msc_stop(struct usbh_msc *msc_class)
{
}
const struct usbh_class_driver msc_class_driver = {
.driver_name = "msc",
.connect = usbh_msc_connect,
.disconnect = usbh_msc_disconnect
};
CLASS_INFO_DEFINE const struct usbh_class_info msc_class_info = {
.match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
.class = USB_DEVICE_CLASS_MASS_STORAGE,
.subclass = MSC_SUBCLASS_SCSI,
.protocol = MSC_PROTOCOL_BULK_ONLY,
.id_table = NULL,
.class_driver = &msc_class_driver
};

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_MSC_H
#define USBH_MSC_H
#include "usb_msc.h"
#include "usb_scsi.h"
struct usbh_msc {
struct usbh_hubport *hport;
struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
struct usbh_urb bulkin_urb; /* Bulk IN urb */
struct usbh_urb bulkout_urb; /* Bulk OUT urb */
uint8_t intf; /* Data interface number */
uint8_t sdchar;
uint32_t blocknum; /* Number of blocks on the USB mass storage device */
uint16_t blocksize; /* Block size of USB mass storage device */
void *user_data;
};
struct usbh_msc_modeswitch_config {
const char *name;
uint16_t vid; /* Vendor ID (for vendor/product specific devices) */
uint16_t pid; /* Product ID (for vendor/product specific devices) */
const uint8_t *message_content;
};
void usbh_msc_modeswitch_enable(struct usbh_msc_modeswitch_config *config);
int usbh_msc_scsi_write10(struct usbh_msc *msc_class, uint32_t start_sector, const uint8_t *buffer, uint32_t nsectors);
int usbh_msc_scsi_read10(struct usbh_msc *msc_class, uint32_t start_sector, const uint8_t *buffer, uint32_t nsectors);
void usbh_msc_run(struct usbh_msc *msc_class);
void usbh_msc_stop(struct usbh_msc *msc_class);
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif /* USBH_MSC_H */

View File

@@ -0,0 +1,4 @@
#ifndef _USB_XXX_H
#define _USB_XXX_H
#endif

View File

@@ -0,0 +1,39 @@
#include "usbd_core.h"
#include "usbd_xxx.h"
static int xxx_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
{
USB_LOG_WRN("XXX Class request: "
"bRequest 0x%02x\r\n",
setup->bRequest);
switch (setup->bRequest) {
default:
USB_LOG_WRN("Unhandled XXX Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
return 0;
}
static void xxx_notify_handler(uint8_t busid, uint8_t event, void *arg)
{
switch (event) {
case USBD_EVENT_RESET:
break;
default:
break;
}
}
struct usbd_interface *usbd_xxx_init_intf(uint8_t busid, struct usbd_interface *intf)
{
intf->class_interface_handler = xxx_class_interface_request_handler;
intf->class_endpoint_handler = NULL;
intf->vendor_handler = NULL;
intf->notify_handler = xxx_notify_handler;
return intf;
}

View File

@@ -0,0 +1,16 @@
#ifndef _USBD_XXX_H_
#define _USBD_XXX_H_
#include "usb_xxx.h"
#ifdef __cplusplus
extern "C" {
#endif
struct usbd_interface *usbd_xxx_init_intf(uint8_t busid, struct usbd_interface *intf);
#ifdef __cplusplus
}
#endif
#endif /* _USBD_XXX_H_ */

View File

@@ -0,0 +1,97 @@
#include "usbh_core.h"
#include "usbh_xxx.h"
#define DEV_FORMAT "/dev/xxx"
#define CONFIG_USBHOST_MAX_CUSTOM_CLASS 1
static struct usbh_xxx g_xxx_class[CONFIG_USBHOST_MAX_CUSTOM_CLASS];
static uint32_t g_devinuse = 0;
static struct usbh_xxx *usbh_xxx_class_alloc(void)
{
int devno;
for (devno = 0; devno < CONFIG_USBHOST_MAX_CUSTOM_CLASS; devno++) {
if ((g_devinuse & (1 << devno)) == 0) {
g_devinuse |= (1 << devno);
memset(&g_xxx_class[devno], 0, sizeof(struct usbh_xxx));
g_xxx_class[devno].minor = devno;
return &g_xxx_class[devno];
}
}
return NULL;
}
static void usbh_xxx_class_free(struct usbh_xxx *xxx_class)
{
int devno = xxx_class->minor;
if (devno >= 0 && devno < 32) {
g_devinuse &= ~(1 << devno);
}
memset(xxx_class, 0, sizeof(struct usbh_xxx));
}
static int usbh_xxx_connect(struct usbh_hubport *hport, uint8_t intf)
{
struct usb_endpoint_descriptor *ep_desc;
int ret;
struct usbh_xxx *xxx_class = usbh_xxx_class_alloc();
if (xxx_class == NULL) {
USB_LOG_ERR("Fail to alloc xxx_class\r\n");
return -USB_ERR_NOMEM;
}
return ret;
}
static int usbh_xxx_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
int ret = 0;
struct usbh_xxx *xxx_class = (struct usbh_xxx *)hport->config.intf[intf].priv;
if (xxx_class) {
if (xxx_class->xxxin) {
usbh_kill_urb(&xxx_class->xxxin_urb);
}
if (xxx_class->xxxout) {
usbh_kill_urb(&xxx_class->xxxout_urb);
}
if (hport->config.intf[intf].devname[0] != '\0') {
USB_LOG_INFO("Unregister xxx Class:%s\r\n", hport->config.intf[intf].devname);
usbh_xxx_stop(xxx_class);
}
usbh_xxx_class_free(xxx_class);
}
return ret;
}
__WEAK void usbh_xxx_run(struct usbh_xxx *xxx_class)
{
}
__WEAK void usbh_xxx_stop(struct usbh_xxx *xxx_class)
{
}
static const struct usbh_class_driver xxx_class_driver = {
.driver_name = "xxx",
.connect = usbh_xxx_connect,
.disconnect = usbh_xxx_disconnect
};
CLASS_INFO_DEFINE const struct usbh_class_info xxx_class_info = {
.match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
.class = 0,
.subclass = 0,
.protocol = 0,
.id_table = NULL,
.class_driver = &xxx_class_driver
};

View File

@@ -0,0 +1,22 @@
#ifndef _USBH_XXX_H
#define _USBH_XXX_H
#include "usb_xxx.h"
struct usbh_xxx {
struct usbh_hubport *hport;
struct usb_endpoint_descriptor *xxxin;
struct usb_endpoint_descriptor *xxxout;
struct usbh_urb xxxin_urb;
struct usbh_urb xxxout_urb;
uint8_t intf; /* interface number */
uint8_t minor;
void *user_data;
};
void usbh_xxx_run(struct usbh_xxx *xxx_class);
void usbh_xxx_stop(struct usbh_xxx *xxx_class);
#endif

View File

@@ -0,0 +1,819 @@
/*
* Copyright (c) 2024, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbh_core.h"
#include "usbh_asix.h"
#include "usb_cdc.h"
#undef USB_DBG_TAG
#define USB_DBG_TAG "asix"
#include "usb_log.h"
#define DEV_FORMAT "/dev/asix"
static struct usbh_asix g_asix_class;
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_asix_rx_buffer[CONFIG_USBHOST_ASIX_ETH_MAX_TX_SIZE];
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_asix_tx_buffer[CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE];
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_asix_inttx_buffer[16];
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_asix_buf[32];
#define ETH_ALEN 6
#define PHY_MODE_MARVELL 0x0000
#define MII_MARVELL_LED_CTRL 0x0018
#define MII_MARVELL_STATUS 0x001b
#define MII_MARVELL_CTRL 0x0014
#define MARVELL_LED_MANUAL 0x0019
#define MARVELL_STATUS_HWCFG 0x0004
#define MARVELL_CTRL_TXDELAY 0x0002
#define MARVELL_CTRL_RXDELAY 0x0080
#define PHY_MODE_RTL8211CL 0x000C
#define AX88772A_PHY14H 0x14
#define AX88772A_PHY14H_DEFAULT 0x442C
#define AX88772A_PHY15H 0x15
#define AX88772A_PHY15H_DEFAULT 0x03C8
#define AX88772A_PHY16H 0x16
#define AX88772A_PHY16H_DEFAULT 0x4044
#define SPEED_100 0
#define SPEED_10 1
static int usbh_asix_read_cmd(struct usbh_asix *asix_class,
uint8_t cmd,
uint16_t value,
uint16_t index,
void *data,
uint16_t size)
{
struct usb_setup_packet *setup;
int ret;
if (!asix_class || !asix_class->hport) {
return -USB_ERR_INVAL;
}
setup = asix_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = cmd;
setup->wValue = value;
setup->wIndex = index;
setup->wLength = size;
ret = usbh_control_transfer(asix_class->hport, setup, g_asix_buf);
if (ret < 0) {
return ret;
}
memcpy(data, g_asix_buf, ret - 8);
return ret;
}
static int usbh_asix_write_cmd(struct usbh_asix *asix_class,
uint8_t cmd,
uint16_t value,
uint16_t index,
void *data,
uint16_t size)
{
struct usb_setup_packet *setup;
if (!asix_class || !asix_class->hport) {
return -USB_ERR_INVAL;
}
setup = asix_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = cmd;
setup->wValue = value;
setup->wIndex = index;
setup->wLength = size;
memcpy(g_asix_buf, data, size);
return usbh_control_transfer(asix_class->hport, setup, g_asix_buf);
}
static int usbh_asix_mdio_write(struct usbh_asix *asix_class, int phy_id, int loc, int val)
{
uint8_t smsr;
uint16_t res = (uint16_t)val;
int ret;
for (uint8_t i = 0; i < 10; i++) {
ret = usbh_asix_write_cmd(asix_class, AX_CMD_SET_SW_MII, 0, 0, NULL, 0);
if (ret < 0) {
return ret;
}
usb_osal_msleep(1);
ret = usbh_asix_read_cmd(asix_class, AX_CMD_STATMNGSTS_REG, 0, 0, &smsr, 1);
if (ret < 0) {
return ret;
}
if (smsr & AX_HOST_EN) {
break;
}
}
ret = usbh_asix_write_cmd(asix_class, AX_CMD_WRITE_MII_REG, phy_id, loc, &res, 2);
if (ret < 0) {
return ret;
}
ret = usbh_asix_write_cmd(asix_class, AX_CMD_SET_HW_MII, 0, 0, NULL, 0);
if (ret < 0) {
return ret;
}
return 0;
}
static int usbh_asix_mdio_read(struct usbh_asix *asix_class, int phy_id, int loc)
{
uint8_t smsr;
uint16_t res;
int ret;
for (uint8_t i = 0; i < 10; i++) {
ret = usbh_asix_write_cmd(asix_class, AX_CMD_SET_SW_MII, 0, 0, NULL, 0);
if (ret < 0) {
return ret;
}
usb_osal_msleep(1);
ret = usbh_asix_read_cmd(asix_class, AX_CMD_STATMNGSTS_REG, 0, 0, &smsr, 1);
if (ret < 0) {
return ret;
}
if (smsr & AX_HOST_EN) {
break;
}
}
ret = usbh_asix_read_cmd(asix_class, AX_CMD_READ_MII_REG, phy_id, loc, &res, 2);
if (ret < 0) {
return ret;
}
ret = usbh_asix_write_cmd(asix_class, AX_CMD_SET_HW_MII, 0, 0, NULL, 0);
if (ret < 0) {
return ret;
}
return res;
}
static int usbh_asix_read_phy_addr(struct usbh_asix *asix_class, bool internal)
{
int ret, offset;
uint8_t buf[2];
ret = usbh_asix_read_cmd(asix_class, AX_CMD_READ_PHY_ID, 0, 0, buf, 2);
if (ret < 0) {
return ret;
}
offset = (internal ? 1 : 0);
ret = buf[offset];
USB_LOG_INFO("%s PHY address 0x%x\r\n", internal ? "internal" : "external", ret);
return ret;
}
static int usbh_asix_sw_reset(struct usbh_asix *asix_class, uint8_t flags)
{
int ret;
ret = usbh_asix_write_cmd(asix_class, AX_CMD_SW_RESET, flags, 0, NULL, 0);
if (ret < 0)
USB_LOG_ERR("Failed to send software reset: %d\r\n", ret);
return ret;
}
static uint16_t usbh_asix_read_rx_ctl(struct usbh_asix *asix_class)
{
uint16_t v;
int ret = usbh_asix_read_cmd(asix_class, AX_CMD_READ_RX_CTL, 0, 0, &v, 2);
if (ret < 0) {
return ret;
}
return v;
}
static int usbh_asix_write_rx_ctl(struct usbh_asix *asix_class, uint16_t mode)
{
int ret;
USB_LOG_DBG("asix_write_rx_ctl() - mode = 0x%04x\r\n", mode);
ret = usbh_asix_write_cmd(asix_class, AX_CMD_WRITE_RX_CTL, mode, 0, NULL, 0);
if (ret < 0)
USB_LOG_ERR("Failed to write RX_CTL mode to 0x%04x: %02x\r\n",
mode, ret);
return ret;
}
static uint16_t usbh_asix_read_medium_status(struct usbh_asix *asix_class)
{
uint16_t v;
int ret = usbh_asix_read_cmd(asix_class, AX_CMD_READ_MEDIUM_STATUS, 0, 0, &v, 2);
if (ret < 0) {
USB_LOG_ERR("Error reading Medium Status register: %02x\r\n",
ret);
return ret; /* TODO: callers not checking for error ret */
}
return v;
}
static int usbh_asix_write_medium_mode(struct usbh_asix *asix_class, uint16_t mode)
{
int ret;
USB_LOG_DBG("asix_write_medium_mode() - mode = 0x%04x\r\n", mode);
ret = usbh_asix_write_cmd(asix_class, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, NULL, 0);
if (ret < 0)
USB_LOG_ERR("Failed to write Medium Mode mode to 0x%04x: %02x\r\n",
mode, ret);
return ret;
}
static int usbh_asix_write_gpio(struct usbh_asix *asix_class, uint16_t value, int sleep)
{
int ret;
USB_LOG_DBG("asix_write_gpio() - value = 0x%04x\r\n", value);
ret = usbh_asix_write_cmd(asix_class, AX_CMD_WRITE_GPIOS, value, 0, NULL, 0);
if (ret < 0)
USB_LOG_ERR("Failed to write GPIO value 0x%04x: %d\r\n",
value, ret);
if (sleep)
usb_osal_msleep(sleep);
return ret;
}
/*
* AX88772 & AX88178 have a 16-bit RX_CTL value
*/
static void usbh_asix_set_multicast(struct usbh_asix *asix_class)
{
uint16_t rx_ctl = AX_DEFAULT_RX_CTL | AX_RX_CTL_AM;
#if CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE == 4096
rx_ctl |= AX_RX_CTL_MFB_4096;
#elif CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE == 8192
rx_ctl |= AX_RX_CTL_MFB_8192;
#elif CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE == 16384
rx_ctl |= AX_RX_CTL_MFB_16384;
#else
rx_ctl |= AX_RX_CTL_MFB_2048;
#endif
const uint8_t multi_filter[] = { 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x40 };
usbh_asix_write_cmd(asix_class, AX_CMD_WRITE_MULTI_FILTER, 0, 0, (uint8_t *)multi_filter, AX_MCAST_FILTER_SIZE);
usbh_asix_write_cmd(asix_class, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, NULL, 0);
}
static int usbh_ax88772_hw_reset(struct usbh_asix *asix_class)
{
uint16_t rx_ctl;
int ret;
ret = usbh_asix_write_gpio(asix_class, AX_GPIO_RSE | AX_GPIO_GPO_2 | AX_GPIO_GPO2EN, 5);
if (ret < 0)
goto out;
ret = usbh_asix_write_cmd(asix_class, AX_CMD_SW_PHY_SELECT, asix_class->embd_phy,
0, NULL, 0);
if (ret < 0) {
USB_LOG_ERR("Select PHY #1 failed: %d\r\n", ret);
goto out;
}
if (asix_class->embd_phy) {
ret = usbh_asix_sw_reset(asix_class, AX_SWRESET_IPPD);
if (ret < 0)
goto out;
usb_osal_msleep(10);
ret = usbh_asix_sw_reset(asix_class, AX_SWRESET_CLEAR);
if (ret < 0)
goto out;
usb_osal_msleep(60);
ret = usbh_asix_sw_reset(asix_class, AX_SWRESET_IPRL | AX_SWRESET_PRL);
if (ret < 0)
goto out;
} else {
ret = usbh_asix_sw_reset(asix_class, AX_SWRESET_IPPD | AX_SWRESET_PRL);
if (ret < 0)
goto out;
}
usb_osal_msleep(150);
ret = usbh_asix_write_rx_ctl(asix_class, AX_DEFAULT_RX_CTL);
if (ret < 0)
goto out;
ret = usbh_asix_write_medium_mode(asix_class, AX88772_MEDIUM_DEFAULT);
if (ret < 0)
goto out;
ret = usbh_asix_write_cmd(asix_class, AX_CMD_WRITE_IPG0,
AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
AX88772_IPG2_DEFAULT, NULL, 0);
if (ret < 0) {
USB_LOG_ERR("Write IPG,IPG1,IPG2 failed: %d\r\n", ret);
goto out;
}
/* Rewrite MAC address */
ret = usbh_asix_write_cmd(asix_class, AX_CMD_WRITE_NODE_ID, 0, 0, asix_class->mac, ETH_ALEN);
if (ret < 0)
goto out;
/* Set RX_CTL to default values with 2k buffer, and enable cactus */
ret = usbh_asix_write_rx_ctl(asix_class, AX_DEFAULT_RX_CTL);
if (ret < 0)
goto out;
rx_ctl = usbh_asix_read_rx_ctl(asix_class);
USB_LOG_INFO("RX_CTL is 0x%04x after all initializations\r\n",
rx_ctl);
rx_ctl = usbh_asix_read_medium_status(asix_class);
USB_LOG_INFO("Medium Status is 0x%04x after all initializations\r\n",
rx_ctl);
return 0;
out:
return ret;
}
static int usbh_ax88772a_hw_reset(struct usbh_asix *asix_class)
{
uint16_t rx_ctl, phy14h, phy15h, phy16h;
int ret;
ret = usbh_asix_write_gpio(asix_class, AX_GPIO_RSE, 5);
if (ret < 0)
goto out;
ret = usbh_asix_write_cmd(asix_class, AX_CMD_SW_PHY_SELECT, asix_class->embd_phy | AX_PHYSEL_SSEN, 0, NULL, 0);
if (ret < 0) {
USB_LOG_ERR("Select PHY #1 failed: %d\r\n", ret);
goto out;
}
usb_osal_msleep(10);
ret = usbh_asix_sw_reset(asix_class, AX_SWRESET_IPPD | AX_SWRESET_IPRL);
if (ret < 0)
goto out;
usb_osal_msleep(10);
ret = usbh_asix_sw_reset(asix_class, AX_SWRESET_IPRL);
if (ret < 0)
goto out;
usb_osal_msleep(160);
ret = usbh_asix_sw_reset(asix_class, AX_SWRESET_CLEAR);
if (ret < 0)
goto out;
ret = usbh_asix_sw_reset(asix_class, AX_SWRESET_IPRL);
if (ret < 0)
goto out;
usb_osal_msleep(200);
if (asix_class->chipcode == AX_AX88772B_CHIPCODE) {
ret = usbh_asix_write_cmd(asix_class, AX_QCTCTRL, 0x8000, 0x8001, NULL, 0);
if (ret < 0) {
USB_LOG_ERR("Write BQ setting failed: %d\r\n", ret);
goto out;
}
} else if (asix_class->chipcode == AX_AX88772A_CHIPCODE) {
/* Check if the PHY registers have default settings */
phy14h = usbh_asix_mdio_read(asix_class, asix_class->phy_addr,
AX88772A_PHY14H);
phy15h = usbh_asix_mdio_read(asix_class, asix_class->phy_addr,
AX88772A_PHY15H);
phy16h = usbh_asix_mdio_read(asix_class, asix_class->phy_addr,
AX88772A_PHY16H);
USB_LOG_DBG("772a_hw_reset: MR20=0x%x MR21=0x%x MR22=0x%x\r\n",
phy14h, phy15h, phy16h);
/* Restore PHY registers default setting if not */
if (phy14h != AX88772A_PHY14H_DEFAULT)
usbh_asix_mdio_write(asix_class, asix_class->phy_addr,
AX88772A_PHY14H,
AX88772A_PHY14H_DEFAULT);
if (phy15h != AX88772A_PHY15H_DEFAULT)
usbh_asix_mdio_write(asix_class, asix_class->phy_addr,
AX88772A_PHY15H,
AX88772A_PHY15H_DEFAULT);
if (phy16h != AX88772A_PHY16H_DEFAULT)
usbh_asix_mdio_write(asix_class, asix_class->phy_addr,
AX88772A_PHY16H,
AX88772A_PHY16H_DEFAULT);
}
ret = usbh_asix_write_cmd(asix_class, AX_CMD_WRITE_IPG0,
AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
AX88772_IPG2_DEFAULT, NULL, 0);
if (ret < 0) {
USB_LOG_ERR("Write IPG,IPG1,IPG2 failed: %d\r\n", ret);
goto out;
}
/* Rewrite MAC address */
ret = usbh_asix_write_cmd(asix_class, AX_CMD_WRITE_NODE_ID, 0, 0, asix_class->mac, ETH_ALEN);
if (ret < 0)
goto out;
/* Set RX_CTL to default values with 2k buffer, and enable cactus */
ret = usbh_asix_write_rx_ctl(asix_class, AX_DEFAULT_RX_CTL);
if (ret < 0)
goto out;
ret = usbh_asix_write_medium_mode(asix_class, AX88772_MEDIUM_DEFAULT);
if (ret < 0)
return ret;
/* Set RX_CTL to default values with 2k buffer, and enable cactus */
ret = usbh_asix_write_rx_ctl(asix_class, AX_DEFAULT_RX_CTL);
if (ret < 0)
goto out;
rx_ctl = usbh_asix_read_rx_ctl(asix_class);
USB_LOG_INFO("RX_CTL is 0x%04x after all initializations\r\n", rx_ctl);
rx_ctl = usbh_asix_read_medium_status(asix_class);
USB_LOG_INFO("Medium Status is 0x%04x after all initializations\r\n", rx_ctl);
return 0;
out:
return ret;
}
static void usbh_ax88772_mac_link_down(struct usbh_asix *asix_class)
{
usbh_asix_write_medium_mode(asix_class, 0);
}
static void usbh_ax88772_mac_link_up(struct usbh_asix *asix_class, int speed, int duplex, bool tx_pause, bool rx_pause)
{
uint16_t m = AX_MEDIUM_AC | AX_MEDIUM_RE;
m |= duplex ? AX_MEDIUM_FD : 0;
switch (speed) {
case SPEED_100:
m |= AX_MEDIUM_PS;
break;
case SPEED_10:
break;
default:
return;
}
if (tx_pause)
m |= AX_MEDIUM_TFC;
if (rx_pause)
m |= AX_MEDIUM_RFC;
usbh_asix_write_medium_mode(asix_class, m);
}
static int usbh_asix_connect(struct usbh_hubport *hport, uint8_t intf)
{
struct usb_endpoint_descriptor *ep_desc;
int ret;
struct usbh_asix *asix_class = &g_asix_class;
memset(asix_class, 0, sizeof(struct usbh_asix));
asix_class->hport = hport;
asix_class->intf = intf;
hport->config.intf[intf].priv = asix_class;
if ((hport->device_desc.idVendor == 0x0b95) && (hport->device_desc.idProduct == 0x772b)) {
asix_class->name = "ASIX AX88772B";
} else if ((hport->device_desc.idVendor == 0x0b95) && (hport->device_desc.idProduct == 0x7720)) {
asix_class->name = "ASIX AX88772";
} else if ((hport->device_desc.idVendor == 0x0b95) && (hport->device_desc.idProduct == 0x1780)) {
asix_class->name = "ASIX AX88178";
}
for (uint8_t i = 0; i < (ETH_ALEN >> 1); i++) {
ret = usbh_asix_read_cmd(asix_class, AX_CMD_READ_EEPROM,
0x04 + i, 0, &asix_class->mac[i * 2], 2);
if (ret < 0) {
return ret;
}
}
USB_LOG_INFO("asix MAC address %02x:%02x:%02x:%02x:%02x:%02x\r\n",
asix_class->mac[0],
asix_class->mac[1],
asix_class->mac[2],
asix_class->mac[3],
asix_class->mac[4],
asix_class->mac[5]);
ret = usbh_asix_read_phy_addr(asix_class, true);
if (ret < 0) {
USB_LOG_ERR("Failed to read phy addr: %d\r\n", ret);
return ret;
}
asix_class->phy_addr = ret;
asix_class->embd_phy = ((ret & 0x1f) == AX_EMBD_PHY_ADDR);
ret = usbh_asix_read_cmd(asix_class, AX_CMD_STATMNGSTS_REG, 0, 0, &asix_class->chipcode, 1);
if (ret < 0) {
USB_LOG_ERR("Failed to read STATMNGSTS_REG: %d\r\n", ret);
return ret;
}
asix_class->chipcode &= AX_CHIPCODE_MASK;
USB_LOG_INFO("asix chipcode 0x%x\r\n", asix_class->chipcode);
if (asix_class->chipcode == AX_AX88772_CHIPCODE) {
usbh_ax88772_hw_reset(asix_class);
} else {
usbh_ax88772a_hw_reset(asix_class);
}
for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
if (USB_GET_ENDPOINT_TYPE(ep_desc->bmAttributes) == USB_ENDPOINT_TYPE_INTERRUPT) {
if (ep_desc->bEndpointAddress & 0x80) {
USBH_EP_INIT(asix_class->intin, ep_desc);
} else {
return -USB_ERR_NOTSUPP;
}
} else {
if (ep_desc->bEndpointAddress & 0x80) {
USBH_EP_INIT(asix_class->bulkin, ep_desc);
} else {
USBH_EP_INIT(asix_class->bulkout, ep_desc);
}
}
}
if (asix_class->chipcode == AX_AX88772B_CHIPCODE) {
usbh_asix_mdio_write(asix_class, asix_class->phy_addr, 0, 0);
usbh_asix_mdio_read(asix_class, asix_class->phy_addr, 0);
usbh_asix_mdio_write(asix_class, asix_class->phy_addr, 0, 0x8200);
usbh_asix_mdio_read(asix_class, asix_class->phy_addr, 0);
usbh_asix_mdio_write(asix_class, asix_class->phy_addr, 0, 0x3900);
usbh_asix_mdio_read(asix_class, asix_class->phy_addr, 0);
usbh_asix_mdio_write(asix_class, asix_class->phy_addr, 0, 0x3100);
usbh_asix_mdio_read(asix_class, asix_class->phy_addr, 4);
usbh_asix_mdio_write(asix_class, asix_class->phy_addr, 4, 0x01e1);
usbh_asix_mdio_read(asix_class, asix_class->phy_addr, 1);
usbh_asix_mdio_write(asix_class, asix_class->phy_addr, 0, 0x3300);
usbh_asix_mdio_read(asix_class, asix_class->phy_addr, 0);
}
USB_LOG_INFO("Init %s done\r\n", asix_class->name);
strncpy(hport->config.intf[intf].devname, DEV_FORMAT, CONFIG_USBHOST_DEV_NAMELEN);
USB_LOG_INFO("Register ASIX Class:%s\r\n", hport->config.intf[intf].devname);
usbh_asix_run(asix_class);
return ret;
}
static int usbh_asix_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
int ret = 0;
struct usbh_asix *asix_class = (struct usbh_asix *)hport->config.intf[intf].priv;
if (asix_class) {
if (asix_class->bulkin) {
usbh_kill_urb(&asix_class->bulkin_urb);
}
if (asix_class->bulkout) {
usbh_kill_urb(&asix_class->bulkout_urb);
}
if (asix_class->intin) {
usbh_kill_urb(&asix_class->intin_urb);
}
if (hport->config.intf[intf].devname[0] != '\0') {
USB_LOG_INFO("Unregister ASIX Class:%s\r\n", hport->config.intf[intf].devname);
usbh_asix_stop(asix_class);
}
memset(asix_class, 0, sizeof(struct usbh_asix));
}
return ret;
}
int usbh_asix_get_connect_status(struct usbh_asix *asix_class)
{
int ret;
usbh_int_urb_fill(&asix_class->intin_urb, asix_class->hport, asix_class->intin, g_asix_inttx_buffer, 8, USB_OSAL_WAITING_FOREVER, NULL, NULL);
ret = usbh_submit_urb(&asix_class->intin_urb);
if (ret < 0) {
return ret;
}
if (g_asix_inttx_buffer[1] == 0x00) {
if (g_asix_inttx_buffer[2] & 0x01) {
asix_class->connect_status = true;
usbh_ax88772_mac_link_up(asix_class, SPEED_100, 1, 1, 1);
usbh_asix_set_multicast(asix_class);
} else {
asix_class->connect_status = false;
usbh_ax88772_mac_link_down(asix_class);
}
}
return 0;
}
void usbh_asix_rx_thread(void *argument)
{
uint32_t g_asix_rx_length;
int ret;
uint16_t len;
uint16_t len_crc;
uint32_t data_offset;
#if CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE <= (16 * 1024)
uint32_t transfer_size = CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE;
#else
uint32_t transfer_size = (16 * 1024);
#endif
USB_LOG_INFO("Create asix rx thread\r\n");
// clang-format off
find_class:
// clang-format on
g_asix_class.connect_status = false;
if (usbh_find_class_instance("/dev/asix") == NULL) {
goto delete;
}
while (g_asix_class.connect_status == false) {
ret = usbh_asix_get_connect_status(&g_asix_class);
if (ret < 0) {
usb_osal_msleep(100);
goto find_class;
}
usb_osal_msleep(128);
}
g_asix_rx_length = 0;
while (1) {
usbh_bulk_urb_fill(&g_asix_class.bulkin_urb, g_asix_class.hport, g_asix_class.bulkin, &g_asix_rx_buffer[g_asix_rx_length], transfer_size, USB_OSAL_WAITING_FOREVER, NULL, NULL);
ret = usbh_submit_urb(&g_asix_class.bulkin_urb);
if (ret < 0) {
goto find_class;
}
g_asix_rx_length += g_asix_class.bulkin_urb.actual_length;
/* A transfer is complete because last packet is a short packet.
* Short packet is not zero, match g_asix_rx_length % USB_GET_MAXPACKETSIZE(g_asix_class.bulkin->wMaxPacketSize).
* Short packet is zero, check if g_asix_class.bulkin_urb.actual_length < transfer_size, for example transfer is complete with size is 1024 < 2048.
*/
if (g_asix_rx_length % USB_GET_MAXPACKETSIZE(g_asix_class.bulkin->wMaxPacketSize) ||
(g_asix_class.bulkin_urb.actual_length < transfer_size)) {
USB_LOG_DBG("rxlen:%d\r\n", g_asix_rx_length);
data_offset = 0;
while (g_asix_rx_length > 0) {
len = ((uint16_t)g_asix_rx_buffer[data_offset + 0] | ((uint16_t)(g_asix_rx_buffer[data_offset + 1]) << 8)) & 0x7ff;
len_crc = g_asix_rx_buffer[data_offset + 2] | ((uint16_t)(g_asix_rx_buffer[data_offset + 3]) << 8);
if (len != (~len_crc & 0x7ff)) {
USB_LOG_ERR("rx header error\r\n");
g_asix_rx_length = 0;
continue;
}
uint8_t *buf = (uint8_t *)&g_asix_rx_buffer[data_offset + 4];
usbh_asix_eth_input(buf, len);
g_asix_rx_length -= (len + 4);
data_offset += (len + 4);
if (g_asix_rx_length < 4) {
g_asix_rx_length = 0;
}
}
} else {
#if CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE <= (16 * 1024)
if (g_asix_rx_length == CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE) {
#else
if ((g_asix_rx_length + (16 * 1024)) > CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE) {
#endif
USB_LOG_ERR("Rx packet is overflow, please ruduce tcp window size or increase CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE\r\n");
while (1) {
}
}
}
}
// clang-format off
delete:
USB_LOG_INFO("Delete asix rx thread\r\n");
usb_osal_thread_delete(NULL);
// clang-format on
}
uint8_t *usbh_asix_get_eth_txbuf(void)
{
return &g_asix_tx_buffer[4];
}
int usbh_asix_eth_output(uint32_t buflen)
{
uint16_t actual_len;
if (g_asix_class.connect_status == false) {
return -USB_ERR_NOTCONN;
}
g_asix_tx_buffer[0] = buflen & 0xff;
g_asix_tx_buffer[1] = (buflen >> 8) & 0xff;
g_asix_tx_buffer[2] = ~g_asix_tx_buffer[0];
g_asix_tx_buffer[3] = ~g_asix_tx_buffer[1];
if (!(buflen + 4) % USB_GET_MAXPACKETSIZE(g_asix_class.bulkout->wMaxPacketSize)) {
USB_LOG_DBG("txlen:%d\r\n", buflen + 8);
g_asix_tx_buffer[buflen + 4 + 0] = 0x00;
g_asix_tx_buffer[buflen + 4 + 1] = 0x00;
g_asix_tx_buffer[buflen + 4 + 2] = 0xff;
g_asix_tx_buffer[buflen + 4 + 3] = 0xff;
actual_len = buflen + 8;
} else {
USB_LOG_DBG("txlen:%d\r\n", buflen + 4);
actual_len = buflen + 4;
}
usbh_bulk_urb_fill(&g_asix_class.bulkout_urb, g_asix_class.hport, g_asix_class.bulkout, g_asix_tx_buffer, actual_len, USB_OSAL_WAITING_FOREVER, NULL, NULL);
return usbh_submit_urb(&g_asix_class.bulkout_urb);
}
__WEAK void usbh_asix_run(struct usbh_asix *asix_class)
{
}
__WEAK void usbh_asix_stop(struct usbh_asix *asix_class)
{
}
static const uint16_t asix_id_table[][2] = {
{ 0x0B95, 0x772B },
{ 0x0B95, 0x7720 },
{ 0, 0 },
};
static const struct usbh_class_driver asix_class_driver = {
.driver_name = "asix",
.connect = usbh_asix_connect,
.disconnect = usbh_asix_disconnect
};
CLASS_INFO_DEFINE const struct usbh_class_info asix_class_info = {
.match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
.class = 0xff,
.subclass = 0x00,
.protocol = 0x00,
.id_table = asix_id_table,
.class_driver = &asix_class_driver
};

View File

@@ -0,0 +1,177 @@
/*
* Copyright (c) 2024, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_ASIX_H
#define USBH_ASIX_H
/* ASIX AX8817X based USB 2.0 Ethernet Devices */
#define AX_CMD_SET_SW_MII 0x06
#define AX_CMD_READ_MII_REG 0x07
#define AX_CMD_WRITE_MII_REG 0x08
#define AX_CMD_STATMNGSTS_REG 0x09
#define AX_CMD_SET_HW_MII 0x0a
#define AX_CMD_READ_EEPROM 0x0b
#define AX_CMD_WRITE_EEPROM 0x0c
#define AX_CMD_WRITE_ENABLE 0x0d
#define AX_CMD_WRITE_DISABLE 0x0e
#define AX_CMD_READ_RX_CTL 0x0f
#define AX_CMD_WRITE_RX_CTL 0x10
#define AX_CMD_READ_IPG012 0x11
#define AX_CMD_WRITE_IPG0 0x12
#define AX_CMD_WRITE_IPG1 0x13
#define AX_CMD_READ_NODE_ID 0x13
#define AX_CMD_WRITE_NODE_ID 0x14
#define AX_CMD_WRITE_IPG2 0x14
#define AX_CMD_WRITE_MULTI_FILTER 0x16
#define AX88172_CMD_READ_NODE_ID 0x17
#define AX_CMD_READ_PHY_ID 0x19
#define AX_CMD_READ_MEDIUM_STATUS 0x1a
#define AX_CMD_WRITE_MEDIUM_MODE 0x1b
#define AX_CMD_READ_MONITOR_MODE 0x1c
#define AX_CMD_WRITE_MONITOR_MODE 0x1d
#define AX_CMD_READ_GPIOS 0x1e
#define AX_CMD_WRITE_GPIOS 0x1f
#define AX_CMD_SW_RESET 0x20
#define AX_CMD_SW_PHY_STATUS 0x21
#define AX_CMD_SW_PHY_SELECT 0x22
#define AX_QCTCTRL 0x2A
#define AX_CHIPCODE_MASK 0x70
#define AX_AX88772_CHIPCODE 0x00
#define AX_AX88772A_CHIPCODE 0x10
#define AX_AX88772B_CHIPCODE 0x20
#define AX_HOST_EN 0x01
#define AX_PHYSEL_PSEL 0x01
#define AX_PHYSEL_SSMII 0
#define AX_PHYSEL_SSEN 0x10
#define AX_PHY_SELECT_MASK (BIT(3) | BIT(2))
#define AX_PHY_SELECT_INTERNAL 0
#define AX_PHY_SELECT_EXTERNAL BIT(2)
#define AX_MONITOR_MODE 0x01
#define AX_MONITOR_LINK 0x02
#define AX_MONITOR_MAGIC 0x04
#define AX_MONITOR_HSFS 0x10
/* AX88172 Medium Status Register values */
#define AX88172_MEDIUM_FD 0x02
#define AX88172_MEDIUM_TX 0x04
#define AX88172_MEDIUM_FC 0x10
#define AX88172_MEDIUM_DEFAULT \
(AX88172_MEDIUM_FD | AX88172_MEDIUM_TX | AX88172_MEDIUM_FC)
#define AX_MCAST_FILTER_SIZE 8
#define AX_MAX_MCAST 64
#define AX_SWRESET_CLEAR 0x00
#define AX_SWRESET_RR 0x01
#define AX_SWRESET_RT 0x02
#define AX_SWRESET_PRTE 0x04
#define AX_SWRESET_PRL 0x08
#define AX_SWRESET_BZ 0x10
#define AX_SWRESET_IPRL 0x20
#define AX_SWRESET_IPPD 0x40
#define AX88772_IPG0_DEFAULT 0x15
#define AX88772_IPG1_DEFAULT 0x0c
#define AX88772_IPG2_DEFAULT 0x12
/* AX88772 & AX88178 Medium Mode Register */
#define AX_MEDIUM_PF 0x0080
#define AX_MEDIUM_JFE 0x0040
#define AX_MEDIUM_TFC 0x0020
#define AX_MEDIUM_RFC 0x0010
#define AX_MEDIUM_ENCK 0x0008
#define AX_MEDIUM_AC 0x0004
#define AX_MEDIUM_FD 0x0002
#define AX_MEDIUM_GM 0x0001
#define AX_MEDIUM_SM 0x1000
#define AX_MEDIUM_SBP 0x0800
#define AX_MEDIUM_PS 0x0200
#define AX_MEDIUM_RE 0x0100
#define AX88178_MEDIUM_DEFAULT \
(AX_MEDIUM_PS | AX_MEDIUM_FD | AX_MEDIUM_AC | \
AX_MEDIUM_RFC | AX_MEDIUM_TFC | AX_MEDIUM_JFE | \
AX_MEDIUM_RE)
#define AX88772_MEDIUM_DEFAULT \
(AX_MEDIUM_FD | AX_MEDIUM_PS | \
AX_MEDIUM_AC | AX_MEDIUM_RE)
/* AX88772 & AX88178 RX_CTL values */
#define AX_RX_CTL_SO 0x0080
#define AX_RX_CTL_AP 0x0020
#define AX_RX_CTL_AM 0x0010
#define AX_RX_CTL_AB 0x0008
#define AX_RX_CTL_SEP 0x0004
#define AX_RX_CTL_AMALL 0x0002
#define AX_RX_CTL_PRO 0x0001
#define AX_RX_CTL_MFB_2048 0x0000
#define AX_RX_CTL_MFB_4096 0x0100
#define AX_RX_CTL_MFB_8192 0x0200
#define AX_RX_CTL_MFB_16384 0x0300
#define AX_DEFAULT_RX_CTL (AX_RX_CTL_SO | AX_RX_CTL_AB)
/* GPIO 0 .. 2 toggles */
#define AX_GPIO_GPO0EN 0x01 /* GPIO0 Output enable */
#define AX_GPIO_GPO_0 0x02 /* GPIO0 Output value */
#define AX_GPIO_GPO1EN 0x04 /* GPIO1 Output enable */
#define AX_GPIO_GPO_1 0x08 /* GPIO1 Output value */
#define AX_GPIO_GPO2EN 0x10 /* GPIO2 Output enable */
#define AX_GPIO_GPO_2 0x20 /* GPIO2 Output value */
#define AX_GPIO_RESERVED 0x40 /* Reserved */
#define AX_GPIO_RSE 0x80 /* Reload serial EEPROM */
#define AX_EEPROM_MAGIC 0xdeadbeef
#define AX_EEPROM_LEN 0x200
#define AX_EMBD_PHY_ADDR 0x10
struct usbh_asix {
struct usbh_hubport *hport;
struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
struct usb_endpoint_descriptor *intin; /* INTR IN endpoint */
struct usbh_urb bulkout_urb;
struct usbh_urb bulkin_urb;
struct usbh_urb intin_urb;
uint8_t intf;
char *name;
uint8_t phy_addr;
uint8_t embd_phy;
uint8_t chipcode;
uint16_t mac_capabilities;
bool connect_status;
uint8_t mac[6];
void *user_data;
};
#ifdef __cplusplus
extern "C" {
#endif
int usbh_asix_get_connect_status(struct usbh_asix *asix_class);
void usbh_asix_run(struct usbh_asix *asix_class);
void usbh_asix_stop(struct usbh_asix *asix_class);
uint8_t *usbh_asix_get_eth_txbuf(void);
int usbh_asix_eth_output(uint32_t buflen);
void usbh_asix_eth_input(uint8_t *buf, uint32_t buflen);
void usbh_asix_rx_thread(void *argument);
#ifdef __cplusplus
}
#endif
#endif /* USBH_ASIX_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) 2024, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_RTL8152_H
#define USBH_RTL8152_H
struct usbh_rtl8152 {
struct usbh_hubport *hport;
struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
struct usb_endpoint_descriptor *intin; /* INTR IN endpoint */
struct usbh_urb bulkout_urb;
struct usbh_urb bulkin_urb;
struct usbh_urb intin_urb;
uint8_t intf;
uint8_t mac[6];
bool connect_status;
uint32_t speed[2];
uint8_t version;
uint8_t eee_adv;
uint8_t eee_en;
uint8_t supports_gmii;
uint16_t min_mtu;
uint16_t max_mtu;
uint16_t ocp_base;
uint32_t saved_wolopts;
uint32_t rx_buf_sz;
struct rtl_ops {
void (*init)(struct usbh_rtl8152 *tp);
int (*enable)(struct usbh_rtl8152 *tp);
void (*disable)(struct usbh_rtl8152 *tp);
void (*up)(struct usbh_rtl8152 *tp);
void (*down)(struct usbh_rtl8152 *tp);
void (*unload)(struct usbh_rtl8152 *tp);
bool (*in_nway)(struct usbh_rtl8152 *tp);
void (*hw_phy_cfg)(struct usbh_rtl8152 *tp);
void (*autosuspend_en)(struct usbh_rtl8152 *tp, bool enable);
void (*change_mtu)(struct usbh_rtl8152 *tp);
} rtl_ops;
void *user_data;
};
#ifdef __cplusplus
extern "C" {
#endif
int usbh_rtl8152_get_connect_status(struct usbh_rtl8152 *rtl8152_class);
void usbh_rtl8152_run(struct usbh_rtl8152 *rtl8152_class);
void usbh_rtl8152_stop(struct usbh_rtl8152 *rtl8152_class);
uint8_t *usbh_rtl8152_get_eth_txbuf(void);
int usbh_rtl8152_eth_output(uint32_t buflen);
void usbh_rtl8152_eth_input(uint8_t *buf, uint32_t buflen);
void usbh_rtl8152_rx_thread(void *argument);
#ifdef __cplusplus
}
#endif
#endif /* USBH_RTL8152_H */

View File

@@ -0,0 +1,376 @@
/*
* Copyright (c) 2024, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbh_core.h"
#include "usbh_ch34x.h"
#define DEV_FORMAT "/dev/ttyUSB%d"
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_ch34x_buf[64];
#define CONFIG_USBHOST_MAX_CP210X_CLASS 1
static struct usbh_ch34x g_ch34x_class[CONFIG_USBHOST_MAX_CP210X_CLASS];
static uint32_t g_devinuse = 0;
static struct usbh_ch34x *usbh_ch34x_class_alloc(void)
{
int devno;
for (devno = 0; devno < CONFIG_USBHOST_MAX_CP210X_CLASS; devno++) {
if ((g_devinuse & (1 << devno)) == 0) {
g_devinuse |= (1 << devno);
memset(&g_ch34x_class[devno], 0, sizeof(struct usbh_ch34x));
g_ch34x_class[devno].minor = devno;
return &g_ch34x_class[devno];
}
}
return NULL;
}
static void usbh_ch34x_class_free(struct usbh_ch34x *ch34x_class)
{
int devno = ch34x_class->minor;
if (devno >= 0 && devno < 32) {
g_devinuse &= ~(1 << devno);
}
memset(ch34x_class, 0, sizeof(struct usbh_ch34x));
}
static int usbh_ch34x_get_baudrate_div(uint32_t baudrate, uint8_t *factor, uint8_t *divisor)
{
uint8_t a;
uint8_t b;
uint32_t c;
switch (baudrate) {
case 921600:
a = 0xf3;
b = 7;
break;
case 307200:
a = 0xd9;
b = 7;
break;
default:
if (baudrate > 6000000 / 255) {
b = 3;
c = 6000000;
} else if (baudrate > 750000 / 255) {
b = 2;
c = 750000;
} else if (baudrate > 93750 / 255) {
b = 1;
c = 93750;
} else {
b = 0;
c = 11719;
}
a = (uint8_t)(c / baudrate);
if (a == 0 || a == 0xFF) {
return -USB_ERR_INVAL;
}
if ((c / a - baudrate) > (baudrate - c / (a + 1))) {
a++;
}
a = (uint8_t)(256 - a);
break;
}
*factor = a;
*divisor = b;
return 0;
}
static int usbh_ch34x_get_version(struct usbh_ch34x *ch34x_class)
{
struct usb_setup_packet *setup;
int ret;
if (!ch34x_class || !ch34x_class->hport) {
return -USB_ERR_INVAL;
}
setup = ch34x_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = CH34X_READ_VERSION;
setup->wValue = 0;
setup->wIndex = 0;
setup->wLength = 2;
ret = usbh_control_transfer(ch34x_class->hport, setup, g_ch34x_buf);
if (ret < 0) {
return ret;
}
USB_LOG_INFO("Ch34x chip version %02x:%02x\r\n", g_ch34x_buf[0], g_ch34x_buf[1]);
return ret;
}
static int usbh_ch34x_flow_ctrl(struct usbh_ch34x *ch34x_class)
{
struct usb_setup_packet *setup;
if (!ch34x_class || !ch34x_class->hport) {
return -USB_ERR_INVAL;
}
setup = ch34x_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = CH34X_WRITE_REG;
setup->wValue = 0x2727;
setup->wIndex = 0;
setup->wLength = 0;
return usbh_control_transfer(ch34x_class->hport, setup, NULL);
}
int usbh_ch34x_set_line_coding(struct usbh_ch34x *ch34x_class, struct cdc_line_coding *line_coding)
{
struct usb_setup_packet *setup;
uint16_t reg_value = 0;
uint16_t value = 0;
uint8_t factor = 0;
uint8_t divisor = 0;
if (!ch34x_class || !ch34x_class->hport) {
return -USB_ERR_INVAL;
}
setup = ch34x_class->hport->setup;
memcpy((uint8_t *)&ch34x_class->line_coding, line_coding, sizeof(struct cdc_line_coding));
/* refer to https://github.com/WCHSoftGroup/ch341ser_linux/blob/main/driver/ch341.c */
switch (line_coding->bParityType) {
case 0:
break;
case 1:
reg_value |= CH341_L_PO;
break;
case 2:
reg_value |= CH341_L_PE;
break;
case 3:
reg_value |= CH341_L_PM;
break;
case 4:
reg_value |= CH341_L_PS;
break;
default:
return -USB_ERR_INVAL;
}
switch (line_coding->bDataBits) {
case 5:
reg_value |= CH341_L_D5;
break;
case 6:
reg_value |= CH341_L_D6;
break;
case 7:
reg_value |= CH341_L_D7;
break;
case 8:
reg_value |= CH341_L_D8;
break;
default:
return -USB_ERR_INVAL;
}
if (line_coding->bCharFormat == 2) {
reg_value |= CH341_L_SB;
}
reg_value |= 0xC0;
value |= 0x9c;
value |= reg_value << 8;
usbh_ch34x_get_baudrate_div(line_coding->dwDTERate, &factor, &divisor);
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = CH34X_SERIAL_INIT;
setup->wValue = value;
setup->wIndex = (factor << 8) | 0x80 | divisor;
setup->wLength = 0;
return usbh_control_transfer(ch34x_class->hport, setup, NULL);
}
int usbh_ch34x_get_line_coding(struct usbh_ch34x *ch34x_class, struct cdc_line_coding *line_coding)
{
memcpy(line_coding, (uint8_t *)&ch34x_class->line_coding, sizeof(struct cdc_line_coding));
return 0;
}
int usbh_ch34x_set_line_state(struct usbh_ch34x *ch34x_class, bool dtr, bool rts)
{
struct usb_setup_packet *setup;
if (!ch34x_class || !ch34x_class->hport) {
return -USB_ERR_INVAL;
}
setup = ch34x_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = CH34X_MODEM_CTRL;
setup->wValue = 0x0f | (dtr << 5) | (rts << 6);
setup->wIndex = 0;
setup->wLength = 0;
return usbh_control_transfer(ch34x_class->hport, setup, NULL);
}
static int usbh_ch34x_connect(struct usbh_hubport *hport, uint8_t intf)
{
struct usb_endpoint_descriptor *ep_desc;
int ret = 0;
struct usbh_ch34x *ch34x_class = usbh_ch34x_class_alloc();
if (ch34x_class == NULL) {
USB_LOG_ERR("Fail to alloc ch34x_class\r\n");
return -USB_ERR_NOMEM;
}
ch34x_class->hport = hport;
ch34x_class->intf = intf;
hport->config.intf[intf].priv = ch34x_class;
usbh_ch34x_get_version(ch34x_class);
usbh_ch34x_flow_ctrl(ch34x_class);
for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
if (USB_GET_ENDPOINT_TYPE(ep_desc->bmAttributes) == USB_ENDPOINT_TYPE_INTERRUPT) {
continue;
} else {
if (ep_desc->bEndpointAddress & 0x80) {
USBH_EP_INIT(ch34x_class->bulkin, ep_desc);
} else {
USBH_EP_INIT(ch34x_class->bulkout, ep_desc);
}
}
}
snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, ch34x_class->minor);
USB_LOG_INFO("Register CH34X Class:%s\r\n", hport->config.intf[intf].devname);
#if 0
USB_LOG_INFO("Test ch34x rx and tx and rx for 5 times, baudrate is 115200\r\n");
struct cdc_line_coding linecoding;
uint8_t count = 5;
linecoding.dwDTERate = 115200;
linecoding.bDataBits = 8;
linecoding.bParityType = 0;
linecoding.bCharFormat = 0;
usbh_ch34x_set_line_coding(ch34x_class, &linecoding);
usbh_ch34x_set_line_state(ch34x_class, true, false);
memset(g_ch34x_buf, 'a', sizeof(g_ch34x_buf));
ret = usbh_ch34x_bulk_out_transfer(ch34x_class, g_ch34x_buf, sizeof(g_ch34x_buf), 0xfffffff);
USB_LOG_RAW("out ret:%d\r\n", ret);
while (count--) {
ret = usbh_ch34x_bulk_in_transfer(ch34x_class, g_ch34x_buf, sizeof(g_ch34x_buf), 0xfffffff);
USB_LOG_RAW("in ret:%d\r\n", ret);
if (ret > 0) {
for (uint32_t i = 0; i < ret; i++) {
USB_LOG_RAW("%02x ", g_ch34x_buf[i]);
}
USB_LOG_RAW("\r\n");
}
}
#endif
usbh_ch34x_run(ch34x_class);
return ret;
}
static int usbh_ch34x_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
int ret = 0;
struct usbh_ch34x *ch34x_class = (struct usbh_ch34x *)hport->config.intf[intf].priv;
if (ch34x_class) {
if (ch34x_class->bulkin) {
usbh_kill_urb(&ch34x_class->bulkin_urb);
}
if (ch34x_class->bulkout) {
usbh_kill_urb(&ch34x_class->bulkout_urb);
}
if (hport->config.intf[intf].devname[0] != '\0') {
USB_LOG_INFO("Unregister CH34X Class:%s\r\n", hport->config.intf[intf].devname);
usbh_ch34x_stop(ch34x_class);
}
usbh_ch34x_class_free(ch34x_class);
}
return ret;
}
int usbh_ch34x_bulk_in_transfer(struct usbh_ch34x *ch34x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
{
int ret;
struct usbh_urb *urb = &ch34x_class->bulkin_urb;
usbh_bulk_urb_fill(urb, ch34x_class->hport, ch34x_class->bulkin, buffer, buflen, timeout, NULL, NULL);
ret = usbh_submit_urb(urb);
if (ret == 0) {
ret = urb->actual_length;
}
return ret;
}
int usbh_ch34x_bulk_out_transfer(struct usbh_ch34x *ch34x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
{
int ret;
struct usbh_urb *urb = &ch34x_class->bulkout_urb;
usbh_bulk_urb_fill(urb, ch34x_class->hport, ch34x_class->bulkout, buffer, buflen, timeout, NULL, NULL);
ret = usbh_submit_urb(urb);
if (ret == 0) {
ret = urb->actual_length;
}
return ret;
}
__WEAK void usbh_ch34x_run(struct usbh_ch34x *ch34x_class)
{
}
__WEAK void usbh_ch34x_stop(struct usbh_ch34x *ch34x_class)
{
}
static const uint16_t ch34x_id_table[][2] = {
{ 0x1A86, 0x7523 },
{ 0, 0 },
};
const struct usbh_class_driver ch34x_class_driver = {
.driver_name = "ch34x",
.connect = usbh_ch34x_connect,
.disconnect = usbh_ch34x_disconnect
};
CLASS_INFO_DEFINE const struct usbh_class_info ch34x_class_info = {
.match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
.class = 0xff,
.subclass = 0x00,
.protocol = 0x00,
.id_table = ch34x_id_table,
.class_driver = &ch34x_class_driver
};

View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2024, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_CH34X_H
#define USBH_CH34X_H
#include "usb_cdc.h"
/* Requests */
#define CH34X_READ_VERSION 0x5F
#define CH34X_WRITE_REG 0x9A
#define CH34X_READ_REG 0x95
#define CH34X_SERIAL_INIT 0xA1
#define CH34X_MODEM_CTRL 0xA4
// modem control bits
#define CH34X_BIT_RTS (1 << 6)
#define CH34X_BIT_DTR (1 << 5)
#define CH341_CTO_O 0x10
#define CH341_CTO_D 0x20
#define CH341_CTO_R 0x40
#define CH341_CTI_C 0x01
#define CH341_CTI_DS 0x02
#define CH341_CTRL_RI 0x04
#define CH341_CTI_DC 0x08
#define CH341_CTI_ST 0x0f
#define CH341_L_ER 0x80
#define CH341_L_ET 0x40
#define CH341_L_PS 0x38
#define CH341_L_PM 0x28
#define CH341_L_PE 0x18
#define CH341_L_PO 0x08
#define CH341_L_SB 0x04
#define CH341_L_D8 0x03
#define CH341_L_D7 0x02
#define CH341_L_D6 0x01
#define CH341_L_D5 0x00
struct usbh_ch34x {
struct usbh_hubport *hport;
struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
struct usbh_urb bulkout_urb;
struct usbh_urb bulkin_urb;
struct cdc_line_coding line_coding;
uint8_t intf;
uint8_t minor;
void *user_data;
};
#ifdef __cplusplus
extern "C" {
#endif
int usbh_ch34x_set_line_coding(struct usbh_ch34x *ch34x_class, struct cdc_line_coding *line_coding);
int usbh_ch34x_get_line_coding(struct usbh_ch34x *ch34x_class, struct cdc_line_coding *line_coding);
int usbh_ch34x_set_line_state(struct usbh_ch34x *ch34x_class, bool dtr, bool rts);
int usbh_ch34x_bulk_in_transfer(struct usbh_ch34x *ch34x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
int usbh_ch34x_bulk_out_transfer(struct usbh_ch34x *ch34x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
void usbh_ch34x_run(struct usbh_ch34x *ch34x_class);
void usbh_ch34x_stop(struct usbh_ch34x *ch34x_class);
#ifdef __cplusplus
}
#endif
#endif /* USBH_CH34X_H */

View File

@@ -0,0 +1,325 @@
/*
* Copyright (c) 2024, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbh_core.h"
#include "usbh_cp210x.h"
#define DEV_FORMAT "/dev/ttyUSB%d"
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cp210x_buf[64];
#define CONFIG_USBHOST_MAX_CP210X_CLASS 4
static struct usbh_cp210x g_cp210x_class[CONFIG_USBHOST_MAX_CP210X_CLASS];
static uint32_t g_devinuse = 0;
static struct usbh_cp210x *usbh_cp210x_class_alloc(void)
{
int devno;
for (devno = 0; devno < CONFIG_USBHOST_MAX_CP210X_CLASS; devno++) {
if ((g_devinuse & (1 << devno)) == 0) {
g_devinuse |= (1 << devno);
memset(&g_cp210x_class[devno], 0, sizeof(struct usbh_cp210x));
g_cp210x_class[devno].minor = devno;
return &g_cp210x_class[devno];
}
}
return NULL;
}
static void usbh_cp210x_class_free(struct usbh_cp210x *cp210x_class)
{
int devno = cp210x_class->minor;
if (devno >= 0 && devno < 32) {
g_devinuse &= ~(1 << devno);
}
memset(cp210x_class, 0, sizeof(struct usbh_cp210x));
}
static int usbh_cp210x_enable(struct usbh_cp210x *cp210x_class)
{
struct usb_setup_packet *setup;
if (!cp210x_class || !cp210x_class->hport) {
return -USB_ERR_INVAL;
}
setup = cp210x_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CP210X_IFC_ENABLE;
setup->wValue = 1;
setup->wIndex = cp210x_class->intf;
setup->wLength = 0;
return usbh_control_transfer(cp210x_class->hport, setup, NULL);
}
static int usbh_cp210x_set_flow(struct usbh_cp210x *cp210x_class)
{
struct usb_setup_packet *setup;
if (!cp210x_class || !cp210x_class->hport) {
return -USB_ERR_INVAL;
}
setup = cp210x_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CP210X_SET_FLOW;
setup->wValue = 0;
setup->wIndex = cp210x_class->intf;
setup->wLength = 16;
memset(g_cp210x_buf, 0, 16);
g_cp210x_buf[13] = 0x20;
return usbh_control_transfer(cp210x_class->hport, setup, g_cp210x_buf);
}
static int usbh_cp210x_set_chars(struct usbh_cp210x *cp210x_class)
{
struct usb_setup_packet *setup;
if (!cp210x_class || !cp210x_class->hport) {
return -USB_ERR_INVAL;
}
setup = cp210x_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CP210X_SET_CHARS;
setup->wValue = 0;
setup->wIndex = cp210x_class->intf;
setup->wLength = 6;
memset(g_cp210x_buf, 0, 6);
g_cp210x_buf[0] = 0x80;
g_cp210x_buf[4] = 0x88;
g_cp210x_buf[5] = 0x28;
return usbh_control_transfer(cp210x_class->hport, setup, g_cp210x_buf);
}
static int usbh_cp210x_set_baudrate(struct usbh_cp210x *cp210x_class, uint32_t baudrate)
{
struct usb_setup_packet *setup;
if (!cp210x_class || !cp210x_class->hport) {
return -USB_ERR_INVAL;
}
setup = cp210x_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CP210X_SET_BAUDRATE;
setup->wValue = 0;
setup->wIndex = cp210x_class->intf;
setup->wLength = 4;
memcpy(g_cp210x_buf, (uint8_t *)&baudrate, 4);
return usbh_control_transfer(cp210x_class->hport, setup, g_cp210x_buf);
}
static int usbh_cp210x_set_data_format(struct usbh_cp210x *cp210x_class, uint8_t databits, uint8_t parity, uint8_t stopbits)
{
struct usb_setup_packet *setup;
uint16_t value;
if (!cp210x_class || !cp210x_class->hport) {
return -USB_ERR_INVAL;
}
setup = cp210x_class->hport->setup;
value = ((databits & 0x0F) << 8) | ((parity & 0x0f) << 4) | ((stopbits & 0x03) << 0);
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CP210X_SET_LINE_CTL;
setup->wValue = value;
setup->wIndex = cp210x_class->intf;
setup->wLength = 0;
return usbh_control_transfer(cp210x_class->hport, setup, NULL);
}
static int usbh_cp210x_set_mhs(struct usbh_cp210x *cp210x_class, uint8_t dtr, uint8_t rts, uint8_t dtr_mask, uint8_t rts_mask)
{
struct usb_setup_packet *setup;
uint16_t value;
if (!cp210x_class || !cp210x_class->hport) {
return -USB_ERR_INVAL;
}
setup = cp210x_class->hport->setup;
value = ((dtr & 0x01) << 0) | ((rts & 0x01) << 1) | ((dtr_mask & 0x01) << 8) | ((rts_mask & 0x01) << 9);
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CP210X_SET_MHS;
setup->wValue = value;
setup->wIndex = cp210x_class->intf;
setup->wLength = 0;
return usbh_control_transfer(cp210x_class->hport, setup, NULL);
}
int usbh_cp210x_set_line_coding(struct usbh_cp210x *cp210x_class, struct cdc_line_coding *line_coding)
{
memcpy((uint8_t *)&cp210x_class->line_coding, line_coding, sizeof(struct cdc_line_coding));
usbh_cp210x_set_baudrate(cp210x_class, line_coding->dwDTERate);
return usbh_cp210x_set_data_format(cp210x_class, line_coding->bDataBits, line_coding->bParityType, line_coding->bCharFormat);
}
int usbh_cp210x_get_line_coding(struct usbh_cp210x *cp210x_class, struct cdc_line_coding *line_coding)
{
memcpy(line_coding, (uint8_t *)&cp210x_class->line_coding, sizeof(struct cdc_line_coding));
return 0;
}
int usbh_cp210x_set_line_state(struct usbh_cp210x *cp210x_class, bool dtr, bool rts)
{
return usbh_cp210x_set_mhs(cp210x_class, dtr, rts, 1, 1);
}
static int usbh_cp210x_connect(struct usbh_hubport *hport, uint8_t intf)
{
struct usb_endpoint_descriptor *ep_desc;
int ret = 0;
struct usbh_cp210x *cp210x_class = usbh_cp210x_class_alloc();
if (cp210x_class == NULL) {
USB_LOG_ERR("Fail to alloc cp210x_class\r\n");
return -USB_ERR_NOMEM;
}
cp210x_class->hport = hport;
cp210x_class->intf = intf;
hport->config.intf[intf].priv = cp210x_class;
usbh_cp210x_enable(cp210x_class);
usbh_cp210x_set_flow(cp210x_class);
usbh_cp210x_set_chars(cp210x_class);
for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
if (ep_desc->bEndpointAddress & 0x80) {
USBH_EP_INIT(cp210x_class->bulkin, ep_desc);
} else {
USBH_EP_INIT(cp210x_class->bulkout, ep_desc);
}
}
snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, cp210x_class->minor);
USB_LOG_INFO("Register CP210X Class:%s\r\n", hport->config.intf[intf].devname);
#if 0
USB_LOG_INFO("Test cp2102 rx and tx and rx for 5 times, baudrate is 115200\r\n");
struct cdc_line_coding linecoding;
uint8_t count = 5;
linecoding.dwDTERate = 115200;
linecoding.bDataBits = 8;
linecoding.bParityType = 0;
linecoding.bCharFormat = 0;
usbh_cp210x_set_line_coding(cp210x_class, &linecoding);
usbh_cp210x_set_line_state(cp210x_class, true, false);
memset(g_cp210x_buf, 'a', sizeof(g_cp210x_buf));
ret = usbh_cp210x_bulk_out_transfer(cp210x_class, g_cp210x_buf, sizeof(g_cp210x_buf), 0xfffffff);
USB_LOG_RAW("out ret:%d\r\n", ret);
while (count--) {
ret = usbh_cp210x_bulk_in_transfer(cp210x_class, g_cp210x_buf, sizeof(g_cp210x_buf), 0xfffffff);
USB_LOG_RAW("in ret:%d\r\n", ret);
if (ret > 0) {
for (uint32_t i = 0; i < ret; i++) {
USB_LOG_RAW("%02x ", g_cp210x_buf[i]);
}
USB_LOG_RAW("\r\n");
}
}
#endif
usbh_cp210x_run(cp210x_class);
return ret;
}
static int usbh_cp210x_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
int ret = 0;
struct usbh_cp210x *cp210x_class = (struct usbh_cp210x *)hport->config.intf[intf].priv;
if (cp210x_class) {
if (cp210x_class->bulkin) {
usbh_kill_urb(&cp210x_class->bulkin_urb);
}
if (cp210x_class->bulkout) {
usbh_kill_urb(&cp210x_class->bulkout_urb);
}
if (hport->config.intf[intf].devname[0] != '\0') {
USB_LOG_INFO("Unregister CP210X Class:%s\r\n", hport->config.intf[intf].devname);
usbh_cp210x_stop(cp210x_class);
}
usbh_cp210x_class_free(cp210x_class);
}
return ret;
}
int usbh_cp210x_bulk_in_transfer(struct usbh_cp210x *cp210x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
{
int ret;
struct usbh_urb *urb = &cp210x_class->bulkin_urb;
usbh_bulk_urb_fill(urb, cp210x_class->hport, cp210x_class->bulkin, buffer, buflen, timeout, NULL, NULL);
ret = usbh_submit_urb(urb);
if (ret == 0) {
ret = urb->actual_length;
}
return ret;
}
int usbh_cp210x_bulk_out_transfer(struct usbh_cp210x *cp210x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
{
int ret;
struct usbh_urb *urb = &cp210x_class->bulkout_urb;
usbh_bulk_urb_fill(urb, cp210x_class->hport, cp210x_class->bulkout, buffer, buflen, timeout, NULL, NULL);
ret = usbh_submit_urb(urb);
if (ret == 0) {
ret = urb->actual_length;
}
return ret;
}
__WEAK void usbh_cp210x_run(struct usbh_cp210x *cp210x_class)
{
}
__WEAK void usbh_cp210x_stop(struct usbh_cp210x *cp210x_class)
{
}
static const uint16_t cp210x_id_table[][2] = {
{ 0x10C4, 0xEA60 },
{ 0, 0 },
};
const struct usbh_class_driver cp210x_class_driver = {
.driver_name = "cp210x",
.connect = usbh_cp210x_connect,
.disconnect = usbh_cp210x_disconnect
};
CLASS_INFO_DEFINE const struct usbh_class_info cp210x_class_info = {
.match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
.class = 0xff,
.subclass = 0x00,
.protocol = 0x00,
.id_table = cp210x_id_table,
.class_driver = &cp210x_class_driver
};

View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 2024, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_CP210X_H
#define USBH_CP210X_H
#include "usb_cdc.h"
/* Requests */
#define CP210X_IFC_ENABLE 0x00
#define CP210X_SET_BAUDDIV 0x01
#define CP210X_GET_BAUDDIV 0x02
#define CP210X_SET_LINE_CTL 0x03 // Set parity, data bits, stop bits
#define CP210X_GET_LINE_CTL 0x04
#define CP210X_SET_BREAK 0x05
#define CP210X_IMM_CHAR 0x06
#define CP210X_SET_MHS 0x07 // Set DTR, RTS
#define CP210X_GET_MDMSTS 0x08
#define CP210X_SET_XON 0x09
#define CP210X_SET_XOFF 0x0A
#define CP210X_SET_EVENTMASK 0x0B
#define CP210X_GET_EVENTMASK 0x0C
#define CP210X_SET_CHAR 0x0D
#define CP210X_GET_CHARS 0x0E
#define CP210X_GET_PROPS 0x0F
#define CP210X_GET_COMM_STATUS 0x10
#define CP210X_RESET 0x11
#define CP210X_PURGE 0x12
#define CP210X_SET_FLOW 0x13
#define CP210X_GET_FLOW 0x14
#define CP210X_EMBED_EVENTS 0x15
#define CP210X_GET_EVENTSTATE 0x16
#define CP210X_SET_CHARS 0x19
#define CP210X_GET_BAUDRATE 0x1D
#define CP210X_SET_BAUDRATE 0x1E // Set baudrate
#define CP210X_VENDOR_SPECIFIC 0xFF
struct usbh_cp210x {
struct usbh_hubport *hport;
struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
struct usbh_urb bulkout_urb;
struct usbh_urb bulkin_urb;
struct cdc_line_coding line_coding;
uint8_t intf;
uint8_t minor;
void *user_data;
};
#ifdef __cplusplus
extern "C" {
#endif
int usbh_cp210x_set_line_coding(struct usbh_cp210x *ftdi_class, struct cdc_line_coding *line_coding);
int usbh_cp210x_get_line_coding(struct usbh_cp210x *ftdi_class, struct cdc_line_coding *line_coding);
int usbh_cp210x_set_line_state(struct usbh_cp210x *ftdi_class, bool dtr, bool rts);
int usbh_cp210x_bulk_in_transfer(struct usbh_cp210x *cp210x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
int usbh_cp210x_bulk_out_transfer(struct usbh_cp210x *cp210x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
void usbh_cp210x_run(struct usbh_cp210x *cp210x_class);
void usbh_cp210x_stop(struct usbh_cp210x *cp210x_class);
#ifdef __cplusplus
}
#endif
#endif /* USBH_CP210X_H */

View File

@@ -0,0 +1,398 @@
/*
* Copyright (c) 2024, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbh_core.h"
#include "usbh_ftdi.h"
#define DEV_FORMAT "/dev/ttyUSB%d"
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_ftdi_buf[64];
#define CONFIG_USBHOST_MAX_FTDI_CLASS 4
static struct usbh_ftdi g_ftdi_class[CONFIG_USBHOST_MAX_FTDI_CLASS];
static uint32_t g_devinuse = 0;
static struct usbh_ftdi *usbh_ftdi_class_alloc(void)
{
int devno;
for (devno = 0; devno < CONFIG_USBHOST_MAX_FTDI_CLASS; devno++) {
if ((g_devinuse & (1 << devno)) == 0) {
g_devinuse |= (1 << devno);
memset(&g_ftdi_class[devno], 0, sizeof(struct usbh_ftdi));
g_ftdi_class[devno].minor = devno;
return &g_ftdi_class[devno];
}
}
return NULL;
}
static void usbh_ftdi_class_free(struct usbh_ftdi *ftdi_class)
{
int devno = ftdi_class->minor;
if (devno >= 0 && devno < 32) {
g_devinuse &= ~(1 << devno);
}
memset(ftdi_class, 0, sizeof(struct usbh_ftdi));
}
static void usbh_ftdi_caculate_baudrate(uint32_t *itdf_divisor, uint32_t actual_baudrate)
{
#define FTDI_USB_CLK 48000000
int baudrate;
uint8_t frac[] = { 0, 8, 4, 2, 6, 10, 12, 14 };
if (actual_baudrate == 2000000) {
*itdf_divisor = 0x01;
} else if (actual_baudrate == 3000000) {
*itdf_divisor = 0x00;
} else {
baudrate = actual_baudrate;
if (baudrate > 100000 && baudrate < 12000000) {
baudrate = (baudrate / 100000) + 100000;
}
int divisor = FTDI_USB_CLK / baudrate;
int frac_bits = 0;
for (int i = 0; i < sizeof(frac) / sizeof(frac[0]); i++) {
if ((divisor & 0xF) == frac[i]) {
frac_bits = i;
break;
}
}
divisor >>= 4;
divisor &= 0x3FFF;
*itdf_divisor = (divisor << 14) | (frac_bits << 8);
}
}
int usbh_ftdi_reset(struct usbh_ftdi *ftdi_class)
{
struct usb_setup_packet *setup;
if (!ftdi_class || !ftdi_class->hport) {
return -USB_ERR_INVAL;
}
setup = ftdi_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = SIO_RESET_REQUEST;
setup->wValue = 0;
setup->wIndex = ftdi_class->intf;
setup->wLength = 0;
return usbh_control_transfer(ftdi_class->hport, setup, NULL);
}
static int usbh_ftdi_set_modem(struct usbh_ftdi *ftdi_class, uint16_t value)
{
struct usb_setup_packet *setup;
if (!ftdi_class || !ftdi_class->hport) {
return -USB_ERR_INVAL;
}
setup = ftdi_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = SIO_SET_MODEM_CTRL_REQUEST;
setup->wValue = value;
setup->wIndex = ftdi_class->intf;
setup->wLength = 0;
return usbh_control_transfer(ftdi_class->hport, setup, NULL);
}
static int usbh_ftdi_set_baudrate(struct usbh_ftdi *ftdi_class, uint32_t baudrate)
{
struct usb_setup_packet *setup;
uint32_t itdf_divisor;
uint16_t value;
uint8_t baudrate_high;
if (!ftdi_class || !ftdi_class->hport) {
return -USB_ERR_INVAL;
}
setup = ftdi_class->hport->setup;
usbh_ftdi_caculate_baudrate(&itdf_divisor, baudrate);
value = itdf_divisor & 0xFFFF;
baudrate_high = (itdf_divisor >> 16) & 0xff;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = SIO_SET_BAUDRATE_REQUEST;
setup->wValue = value;
setup->wIndex = (baudrate_high << 8) | ftdi_class->intf;
setup->wLength = 0;
return usbh_control_transfer(ftdi_class->hport, setup, NULL);
}
static int usbh_ftdi_set_data_format(struct usbh_ftdi *ftdi_class, uint8_t databits, uint8_t parity, uint8_t stopbits, uint8_t isbreak)
{
/**
* D0-D7 databits BITS_7=7, BITS_8=8
* D8-D10 parity NONE=0, ODD=1, EVEN=2, MARK=3, SPACE=4
* D11-D12 STOP_BIT_1=0, STOP_BIT_15=1, STOP_BIT_2=2
* D14 BREAK_OFF=0, BREAK_ON=1
**/
struct usb_setup_packet *setup;
uint16_t value;
if (!ftdi_class || !ftdi_class->hport) {
return -USB_ERR_INVAL;
}
setup = ftdi_class->hport->setup;
value = ((isbreak & 0x01) << 14) | ((stopbits & 0x03) << 11) | ((parity & 0x0f) << 8) | (databits & 0x0f);
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = SIO_SET_DATA_REQUEST;
setup->wValue = value;
setup->wIndex = ftdi_class->intf;
setup->wLength = 0;
return usbh_control_transfer(ftdi_class->hport, setup, NULL);
}
static int usbh_ftdi_set_latency_timer(struct usbh_ftdi *ftdi_class, uint16_t value)
{
struct usb_setup_packet *setup;
if (!ftdi_class || !ftdi_class->hport) {
return -USB_ERR_INVAL;
}
setup = ftdi_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = SIO_SET_LATENCY_TIMER_REQUEST;
setup->wValue = value;
setup->wIndex = ftdi_class->intf;
setup->wLength = 0;
return usbh_control_transfer(ftdi_class->hport, setup, NULL);
}
static int usbh_ftdi_set_flow_ctrl(struct usbh_ftdi *ftdi_class, uint16_t value)
{
struct usb_setup_packet *setup;
if (!ftdi_class || !ftdi_class->hport) {
return -USB_ERR_INVAL;
}
setup = ftdi_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = SIO_SET_FLOW_CTRL_REQUEST;
setup->wValue = value;
setup->wIndex = ftdi_class->intf;
setup->wLength = 0;
return usbh_control_transfer(ftdi_class->hport, setup, NULL);
}
static int usbh_ftdi_read_modem_status(struct usbh_ftdi *ftdi_class)
{
struct usb_setup_packet *setup;
int ret;
if (!ftdi_class || !ftdi_class->hport) {
return -USB_ERR_INVAL;
}
setup = ftdi_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = SIO_POLL_MODEM_STATUS_REQUEST;
setup->wValue = 0x0000;
setup->wIndex = ftdi_class->intf;
setup->wLength = 2;
ret = usbh_control_transfer(ftdi_class->hport, setup, g_ftdi_buf);
if (ret < 0) {
return ret;
}
memcpy(ftdi_class->modem_status, g_ftdi_buf, 2);
return ret;
}
int usbh_ftdi_set_line_coding(struct usbh_ftdi *ftdi_class, struct cdc_line_coding *line_coding)
{
memcpy((uint8_t *)&ftdi_class->line_coding, line_coding, sizeof(struct cdc_line_coding));
usbh_ftdi_set_baudrate(ftdi_class, line_coding->dwDTERate);
return usbh_ftdi_set_data_format(ftdi_class, line_coding->bDataBits, line_coding->bParityType, line_coding->bCharFormat, 0);
}
int usbh_ftdi_get_line_coding(struct usbh_ftdi *ftdi_class, struct cdc_line_coding *line_coding)
{
memcpy(line_coding, (uint8_t *)&ftdi_class->line_coding, sizeof(struct cdc_line_coding));
return 0;
}
int usbh_ftdi_set_line_state(struct usbh_ftdi *ftdi_class, bool dtr, bool rts)
{
int ret;
if (dtr) {
usbh_ftdi_set_modem(ftdi_class, SIO_SET_DTR_HIGH);
} else {
usbh_ftdi_set_modem(ftdi_class, SIO_SET_DTR_LOW);
}
if (rts) {
ret = usbh_ftdi_set_modem(ftdi_class, SIO_SET_RTS_HIGH);
} else {
ret = usbh_ftdi_set_modem(ftdi_class, SIO_SET_RTS_LOW);
}
return ret;
}
static int usbh_ftdi_connect(struct usbh_hubport *hport, uint8_t intf)
{
struct usb_endpoint_descriptor *ep_desc;
int ret = 0;
struct usbh_ftdi *ftdi_class = usbh_ftdi_class_alloc();
if (ftdi_class == NULL) {
USB_LOG_ERR("Fail to alloc ftdi_class\r\n");
return -USB_ERR_NOMEM;
}
ftdi_class->hport = hport;
ftdi_class->intf = intf;
hport->config.intf[intf].priv = ftdi_class;
usbh_ftdi_reset(ftdi_class);
usbh_ftdi_set_flow_ctrl(ftdi_class, SIO_DISABLE_FLOW_CTRL);
usbh_ftdi_set_latency_timer(ftdi_class, 0x10);
usbh_ftdi_read_modem_status(ftdi_class);
USB_LOG_INFO("modem status:%02x:%02x\r\n", ftdi_class->modem_status[0], ftdi_class->modem_status[1]);
for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
if (ep_desc->bEndpointAddress & 0x80) {
USBH_EP_INIT(ftdi_class->bulkin, ep_desc);
} else {
USBH_EP_INIT(ftdi_class->bulkout, ep_desc);
}
}
snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, ftdi_class->minor);
USB_LOG_INFO("Register FTDI Class:%s\r\n", hport->config.intf[intf].devname);
#if 0
USB_LOG_INFO("Test ftdi rx and tx and rx for 5 times, baudrate is 115200\r\n");
struct cdc_line_coding linecoding;
uint8_t count = 5;
linecoding.dwDTERate = 115200;
linecoding.bDataBits = 8;
linecoding.bParityType = 0;
linecoding.bCharFormat = 0;
usbh_ftdi_set_line_coding(ftdi_class, &linecoding);
usbh_ftdi_set_line_state(ftdi_class, true, false);
memset(g_ftdi_buf, 'a', sizeof(g_ftdi_buf));
ret = usbh_ftdi_bulk_out_transfer(ftdi_class, g_ftdi_buf, sizeof(g_ftdi_buf), 0xfffffff);
USB_LOG_RAW("out ret:%d\r\n", ret);
while (count--) {
ret = usbh_ftdi_bulk_in_transfer(ftdi_class, g_ftdi_buf, sizeof(g_ftdi_buf), 0xfffffff);
USB_LOG_RAW("in ret:%d\r\n", ret);
if (ret > 0) {
for (uint32_t i = 0; i < ret; i++) {
USB_LOG_RAW("%02x ", g_ftdi_buf[i]);
}
}
USB_LOG_RAW("\r\n");
}
#endif
usbh_ftdi_run(ftdi_class);
return ret;
}
static int usbh_ftdi_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
int ret = 0;
struct usbh_ftdi *ftdi_class = (struct usbh_ftdi *)hport->config.intf[intf].priv;
if (ftdi_class) {
if (ftdi_class->bulkin) {
usbh_kill_urb(&ftdi_class->bulkin_urb);
}
if (ftdi_class->bulkout) {
usbh_kill_urb(&ftdi_class->bulkout_urb);
}
if (hport->config.intf[intf].devname[0] != '\0') {
USB_LOG_INFO("Unregister FTDI Class:%s\r\n", hport->config.intf[intf].devname);
usbh_ftdi_stop(ftdi_class);
}
usbh_ftdi_class_free(ftdi_class);
}
return ret;
}
int usbh_ftdi_bulk_in_transfer(struct usbh_ftdi *ftdi_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
{
int ret;
struct usbh_urb *urb = &ftdi_class->bulkin_urb;
usbh_bulk_urb_fill(urb, ftdi_class->hport, ftdi_class->bulkin, buffer, buflen, timeout, NULL, NULL);
ret = usbh_submit_urb(urb);
if (ret == 0) {
ret = urb->actual_length;
}
return ret;
}
int usbh_ftdi_bulk_out_transfer(struct usbh_ftdi *ftdi_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
{
int ret;
struct usbh_urb *urb = &ftdi_class->bulkout_urb;
usbh_bulk_urb_fill(urb, ftdi_class->hport, ftdi_class->bulkout, buffer, buflen, timeout, NULL, NULL);
ret = usbh_submit_urb(urb);
if (ret == 0) {
ret = urb->actual_length;
}
return ret;
}
__WEAK void usbh_ftdi_run(struct usbh_ftdi *ftdi_class)
{
}
__WEAK void usbh_ftdi_stop(struct usbh_ftdi *ftdi_class)
{
}
static const uint16_t ftdi_id_table[][2] = {
{ 0x0403, 0x6001 },
{ 0x0403, 0x6010 },
{ 0, 0 },
};
const struct usbh_class_driver ftdi_class_driver = {
.driver_name = "ftdi",
.connect = usbh_ftdi_connect,
.disconnect = usbh_ftdi_disconnect
};
CLASS_INFO_DEFINE const struct usbh_class_info ftdi_class_info = {
.match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
.class = 0xff,
.subclass = 0x00,
.protocol = 0x00,
.id_table = ftdi_id_table,
.class_driver = &ftdi_class_driver
};

View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2024, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_FTDI_H
#define USBH_FTDI_H
#include "usb_cdc.h"
/* Requests */
#define SIO_RESET_REQUEST 0x00 /* Reset the port */
#define SIO_SET_MODEM_CTRL_REQUEST 0x01 /* Set the modem control register */
#define SIO_SET_FLOW_CTRL_REQUEST 0x02 /* Set flow control register */
#define SIO_SET_BAUDRATE_REQUEST 0x03 /* Set baud rate */
#define SIO_SET_DATA_REQUEST 0x04 /* Set the data characteristics of the port */
#define SIO_POLL_MODEM_STATUS_REQUEST 0x05
#define SIO_SET_EVENT_CHAR_REQUEST 0x06
#define SIO_SET_ERROR_CHAR_REQUEST 0x07
#define SIO_SET_LATENCY_TIMER_REQUEST 0x09
#define SIO_GET_LATENCY_TIMER_REQUEST 0x0A
#define SIO_SET_BITMODE_REQUEST 0x0B
#define SIO_READ_PINS_REQUEST 0x0C
#define SIO_READ_EEPROM_REQUEST 0x90
#define SIO_WRITE_EEPROM_REQUEST 0x91
#define SIO_ERASE_EEPROM_REQUEST 0x92
#define SIO_DISABLE_FLOW_CTRL 0x0
#define SIO_RTS_CTS_HS (0x1 << 8)
#define SIO_DTR_DSR_HS (0x2 << 8)
#define SIO_XON_XOFF_HS (0x4 << 8)
#define SIO_SET_DTR_MASK 0x1
#define SIO_SET_DTR_HIGH (1 | (SIO_SET_DTR_MASK << 8))
#define SIO_SET_DTR_LOW (0 | (SIO_SET_DTR_MASK << 8))
#define SIO_SET_RTS_MASK 0x2
#define SIO_SET_RTS_HIGH (2 | (SIO_SET_RTS_MASK << 8))
#define SIO_SET_RTS_LOW (0 | (SIO_SET_RTS_MASK << 8))
#define SIO_RTS_CTS_HS (0x1 << 8)
struct usbh_ftdi {
struct usbh_hubport *hport;
struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
struct usbh_urb bulkout_urb;
struct usbh_urb bulkin_urb;
struct cdc_line_coding line_coding;
uint8_t intf;
uint8_t minor;
uint8_t modem_status[2];
void *user_data;
};
#ifdef __cplusplus
extern "C" {
#endif
int usbh_ftdi_set_line_coding(struct usbh_ftdi *ftdi_class, struct cdc_line_coding *line_coding);
int usbh_ftdi_get_line_coding(struct usbh_ftdi *ftdi_class, struct cdc_line_coding *line_coding);
int usbh_ftdi_set_line_state(struct usbh_ftdi *ftdi_class, bool dtr, bool rts);
int usbh_ftdi_bulk_in_transfer(struct usbh_ftdi *ftdi_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
int usbh_ftdi_bulk_out_transfer(struct usbh_ftdi *ftdi_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
void usbh_ftdi_run(struct usbh_ftdi *ftdi_class);
void usbh_ftdi_stop(struct usbh_ftdi *ftdi_class);
#ifdef __cplusplus
}
#endif
#endif /* USBH_FTDI_H */

View File

@@ -0,0 +1,446 @@
/*
* Copyright (c) 2024, sakumisu
* Copyright (c) 2024, Derek Konigsberg
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbh_core.h"
#include "usbh_pl2303.h"
#undef USB_DBG_TAG
#define USB_DBG_TAG "usbh_pl2303"
#include "usb_log.h"
#define DEV_FORMAT "/dev/ttyUSB%d"
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_pl2303_buf[64];
#define CONFIG_USBHOST_MAX_PL2303_CLASS 4
#define UT_WRITE_VENDOR_DEVICE (USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE)
#define UT_READ_VENDOR_DEVICE (USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE)
static struct usbh_pl2303 g_pl2303_class[CONFIG_USBHOST_MAX_PL2303_CLASS];
static uint32_t g_devinuse = 0;
static struct usbh_pl2303 *usbh_pl2303_class_alloc(void)
{
int devno;
for (devno = 0; devno < CONFIG_USBHOST_MAX_PL2303_CLASS; devno++) {
if ((g_devinuse & (1 << devno)) == 0) {
g_devinuse |= (1 << devno);
memset(&g_pl2303_class[devno], 0, sizeof(struct usbh_pl2303));
g_pl2303_class[devno].minor = devno;
return &g_pl2303_class[devno];
}
}
return NULL;
}
static void usbh_pl2303_class_free(struct usbh_pl2303 *pl2303_class)
{
int devno = pl2303_class->minor;
if (devno >= 0 && devno < 32) {
g_devinuse &= ~(1 << devno);
}
memset(pl2303_class, 0, sizeof(struct usbh_pl2303));
}
static int usbh_pl2303_get_chiptype(struct usbh_pl2303 *pl2303_class)
{
int ret = 0;
switch (pl2303_class->hport->device_desc.bcdDevice) {
case 0x0300:
pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HX;
/* or TA, that is HX with external crystal */
break;
case 0x0400:
pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HXD;
/* or EA, that is HXD with ESD protection */
/* or RA, that has internal voltage level converter that works only up to 1Mbaud (!) */
break;
case 0x0500:
pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HXD;
/* in fact it's TB, that is HXD with external crystal */
break;
default:
/* NOTE: I have no info about the bcdDevice for the base PL2303 (up to 1.2Mbaud,
only fixed rates) and for PL2303SA (8-pin chip, up to 115200 baud */
/* Determine the chip type. This algorithm is taken from Linux. */
if (pl2303_class->hport->device_desc.bDeviceClass == 0x02) {
pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303;
} else if (pl2303_class->hport->device_desc.bMaxPacketSize0 == 0x40) {
pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HX;
} else {
pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303;
}
break;
}
/*
* The new chip revision PL2303HXN is only compatible with the new
* PLCOM_SET_REQUEST_PL2303HXN command. Issuing the old command
* PLCOM_SET_REQUEST to the new chip raises an error. Thus, PL2303HX
* and PL2303HXN can be distinguished by issuing an old-style request
* (on a status register) to the new chip and checking the error.
*/
if (pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303HX) {
struct usb_setup_packet *setup = pl2303_class->hport->setup;
setup->bmRequestType = UT_READ_VENDOR_DEVICE;
setup->bRequest = PL2303_SET_REQUEST;
setup->wValue = PL2303_STATUS_REG_PL2303HX;
setup->wIndex = 0;
setup->wLength = 1;
ret = usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
if (ret == -USB_ERR_STALL) {
pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HXN;
ret = 0;
} else if (ret < 0) {
USB_LOG_WRN("Error checking chip type: %d\r\n", ret);
return ret;
}
}
switch (pl2303_class->chiptype) {
case USBH_PL2303_TYPE_PL2303:
USB_LOG_INFO("chiptype = 2303\r\n");
break;
case USBH_PL2303_TYPE_PL2303HX:
USB_LOG_INFO("chiptype = 2303HX/TA\r\n");
break;
case USBH_PL2303_TYPE_PL2303HXN:
USB_LOG_INFO("chiptype = 2303HXN\r\n");
break;
case USBH_PL2303_TYPE_PL2303HXD:
USB_LOG_INFO("chiptype = 2303HXD/TB/RA/EA\r\n");
break;
default:
USB_LOG_INFO("chiptype = [%d]\r\n", pl2303_class->chiptype);
break;
}
return ret;
}
static int usbh_pl2303_do(struct usbh_pl2303 *pl2303_class,
uint8_t req_type, uint8_t request, uint16_t value, uint16_t index,
uint16_t length)
{
struct usb_setup_packet *setup;
if (!pl2303_class || !pl2303_class->hport) {
return -USB_ERR_INVAL;
}
setup = pl2303_class->hport->setup;
setup->bmRequestType = req_type;
setup->bRequest = request;
setup->wValue = value;
setup->wIndex = index;
setup->wLength = length;
return usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
}
int usbh_pl2303_set_line_coding(struct usbh_pl2303 *pl2303_class, struct cdc_line_coding *line_coding)
{
struct usb_setup_packet *setup;
if (!pl2303_class || !pl2303_class->hport) {
return -USB_ERR_INVAL;
}
setup = pl2303_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CDC_REQUEST_SET_LINE_CODING;
setup->wValue = 0;
setup->wIndex = pl2303_class->intf;
setup->wLength = 7;
memcpy(g_pl2303_buf, line_coding, sizeof(struct cdc_line_coding));
return usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
}
int usbh_pl2303_get_line_coding(struct usbh_pl2303 *pl2303_class, struct cdc_line_coding *line_coding)
{
struct usb_setup_packet *setup;
int ret;
if (!pl2303_class || !pl2303_class->hport) {
return -USB_ERR_INVAL;
}
setup = pl2303_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CDC_REQUEST_GET_LINE_CODING;
setup->wValue = 0;
setup->wIndex = pl2303_class->intf;
setup->wLength = 7;
ret = usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
if (ret < 0) {
return ret;
}
memcpy(line_coding, g_pl2303_buf, sizeof(struct cdc_line_coding));
return ret;
}
int usbh_pl2303_set_line_state(struct usbh_pl2303 *pl2303_class, bool dtr, bool rts)
{
struct usb_setup_packet *setup;
if (!pl2303_class || !pl2303_class->hport) {
return -USB_ERR_INVAL;
}
setup = pl2303_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE;
setup->wValue = (dtr << 0) | (rts << 1);
setup->wIndex = pl2303_class->intf;
setup->wLength = 0;
return usbh_control_transfer(pl2303_class->hport, setup, NULL);
}
static int usbh_pl2303_connect(struct usbh_hubport *hport, uint8_t intf)
{
struct usb_endpoint_descriptor *ep_desc;
int ret = 0;
struct usbh_pl2303 *pl2303_class = usbh_pl2303_class_alloc();
if (pl2303_class == NULL) {
USB_LOG_ERR("Fail to alloc pl2303_class\r\n");
return -USB_ERR_NOMEM;
}
pl2303_class->hport = hport;
pl2303_class->intf = intf;
hport->config.intf[intf].priv = pl2303_class;
do {
ret = usbh_pl2303_get_chiptype(pl2303_class);
if (ret < 0) {
break;
}
/* Startup reset sequence, if necessary for the chip type */
if (pl2303_class->chiptype != USBH_PL2303_TYPE_PL2303HXN) {
struct usb_setup_packet *setup = pl2303_class->hport->setup;
setup->bmRequestType = UT_WRITE_VENDOR_DEVICE;
setup->bRequest = PL2303_SET_REQUEST;
setup->wValue = 0;
setup->wIndex = pl2303_class->intf;
setup->wLength = 0;
ret = usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
if (ret < 0) {
USB_LOG_WRN("Initialization reset failed: %d\r\n", ret);
break;
}
}
if (pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303) {
/* HX variants seem to lock up after a clear stall request. */
/*
* The FreeBSD code sets the stall flags on the in and out pipes
* here. Have no idea exactly how to do this, or if it is necessary.
* May just leave this code unwritten until test hardware is available.
*/
} else if (pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303HX || pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303HXD) {
/* Reset upstream data pipes */
ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 8, 0, 0);
if (ret < 0) {
USB_LOG_WRN("Could not reset upstream data pipes (8,0): %d\r\n", ret);
break;
}
ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 9, 0, 0);
if (ret < 0) {
USB_LOG_WRN("Could not reset upstream data pipes (9,0): %d\r\n", ret);
break;
}
} else if (pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303HXN) {
/* Reset upstream data pipes */
ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST_PL2303HXN, 0x07, 0x03, 0);
if (ret < 0) {
USB_LOG_WRN("Could not reset upstream data pipes (7,3): %d\r\n", ret);
break;
}
}
/* Final device initialization, if necessary for the chip type */
if (pl2303_class->chiptype != USBH_PL2303_TYPE_PL2303HXN) {
if (usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8484, 0, 1) < 0 ||
usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x0404, 0, 0) < 0 ||
usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8484, 0, 1) < 0 ||
usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8383, 0, 1) < 0 ||
usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8484, 0, 1) < 0 ||
usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x0404, 1, 0) < 0 ||
usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8484, 0, 1) < 0 ||
usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8383, 0, 1) < 0 ||
usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 0, 1, 0) < 0 ||
usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 1, 0, 0) < 0) {
USB_LOG_WRN("Could not complete init sequence\r\n");
ret = -USB_ERR_INVAL;
break;
}
if (pl2303_class->chiptype != USBH_PL2303_TYPE_PL2303) {
ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 2, 0x44, 0);
} else {
ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 2, 0x24, 0);
}
if (ret < 0) {
USB_LOG_WRN("Could not complete final init request: %d\r\n", ret);
break;
}
}
} while (0);
if (ret < 0) {
USB_LOG_ERR("Failed to initialize PL2303 device: %d\r\n", ret);
return ret;
}
for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
if (USB_GET_ENDPOINT_TYPE(ep_desc->bmAttributes) == USB_ENDPOINT_TYPE_INTERRUPT) {
continue;
} else {
if (ep_desc->bEndpointAddress & 0x80) {
USBH_EP_INIT(pl2303_class->bulkin, ep_desc);
} else {
USBH_EP_INIT(pl2303_class->bulkout, ep_desc);
}
}
}
snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, pl2303_class->minor);
USB_LOG_INFO("Register PL2303 Class:%s\r\n", hport->config.intf[intf].devname);
#if 0
USB_LOG_INFO("Test pl2303 rx and tx and rx for 5 times, baudrate is 115200\r\n");
struct cdc_line_coding linecoding;
uint8_t count = 5;
linecoding.dwDTERate = 115200;
linecoding.bDataBits = 8;
linecoding.bParityType = 0;
linecoding.bCharFormat = 0;
usbh_pl2303_set_line_coding(pl2303_class, &linecoding);
usbh_pl2303_set_line_state(pl2303_class, true, false);
memset(g_pl2303_buf, 'a', sizeof(g_pl2303_buf));
ret = usbh_pl2303_bulk_out_transfer(pl2303_class, g_pl2303_buf, sizeof(g_pl2303_buf), 0xfffffff);
USB_LOG_RAW("out ret:%d\r\n", ret);
while (count--) {
ret = usbh_pl2303_bulk_in_transfer(pl2303_class, g_pl2303_buf, sizeof(g_pl2303_buf), 0xfffffff);
USB_LOG_RAW("in ret:%d\r\n", ret);
if (ret > 0) {
for (uint32_t i = 0; i < ret; i++) {
USB_LOG_RAW("%02x ", g_pl2303_buf[i]);
}
}
USB_LOG_RAW("\r\n");
}
#endif
usbh_pl2303_run(pl2303_class);
return ret;
}
static int usbh_pl2303_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
int ret = 0;
struct usbh_pl2303 *pl2303_class = (struct usbh_pl2303 *)hport->config.intf[intf].priv;
if (pl2303_class) {
if (pl2303_class->bulkin) {
usbh_kill_urb(&pl2303_class->bulkin_urb);
}
if (pl2303_class->bulkout) {
usbh_kill_urb(&pl2303_class->bulkout_urb);
}
if (hport->config.intf[intf].devname[0] != '\0') {
USB_LOG_INFO("Unregister PL2303 Class:%s\r\n", hport->config.intf[intf].devname);
usbh_pl2303_stop(pl2303_class);
}
usbh_pl2303_class_free(pl2303_class);
}
return ret;
}
int usbh_pl2303_bulk_in_transfer(struct usbh_pl2303 *pl2303_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
{
int ret;
struct usbh_urb *urb = &pl2303_class->bulkin_urb;
usbh_bulk_urb_fill(urb, pl2303_class->hport, pl2303_class->bulkin, buffer, buflen, timeout, NULL, NULL);
ret = usbh_submit_urb(urb);
if (ret == 0) {
ret = urb->actual_length;
}
return ret;
}
int usbh_pl2303_bulk_out_transfer(struct usbh_pl2303 *pl2303_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
{
int ret;
struct usbh_urb *urb = &pl2303_class->bulkout_urb;
usbh_bulk_urb_fill(urb, pl2303_class->hport, pl2303_class->bulkout, buffer, buflen, timeout, NULL, NULL);
ret = usbh_submit_urb(urb);
if (ret == 0) {
ret = urb->actual_length;
}
return ret;
}
__WEAK void usbh_pl2303_run(struct usbh_pl2303 *pl2303_class)
{
}
__WEAK void usbh_pl2303_stop(struct usbh_pl2303 *pl2303_class)
{
}
static const uint16_t pl2303_id_table[][2] = {
{ 0x067B, 0x2303 }, // PL2303 Serial (ATEN/IOGEAR UC232A)
{ 0x067B, 0x23A3 }, // PL2303HXN Serial, type GC
{ 0x067B, 0x23B3 }, // PL2303HXN Serial, type GB
{ 0x067B, 0x23C3 }, // PL2303HXN Serial, type GT
{ 0x067B, 0x23D3 }, // PL2303HXN Serial, type GL
{ 0x067B, 0x23E3 }, // PL2303HXN Serial, type GE
{ 0x067B, 0x23F3 }, // PL2303HXN Serial, type GS
{ 0, 0 },
};
const struct usbh_class_driver pl2303_class_driver = {
.driver_name = "pl2303",
.connect = usbh_pl2303_connect,
.disconnect = usbh_pl2303_disconnect
};
CLASS_INFO_DEFINE const struct usbh_class_info pl2303_class_info = {
.match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
.class = 0xff,
.subclass = 0x00,
.protocol = 0x00,
.id_table = pl2303_id_table,
.class_driver = &pl2303_class_driver
};

View File

@@ -0,0 +1,62 @@
/*
* Copyright (c) 2024, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_PL2303_H
#define USBH_PL2303_H
#include "usb_cdc.h"
#define PL2303_SET_REQUEST 0x01
#define PL2303_SET_REQUEST_PL2303HXN 0x80
#define PL2303_SET_CRTSCTS 0x41
#define PL2303_SET_CRTSCTS_PL2303X 0x61
#define PL2303_SET_CRTSCTS_PL2303HXN 0xFA
#define PL2303_CLEAR_CRTSCTS_PL2303HXN 0xFF
#define PL2303_CRTSCTS_REG_PL2303HXN 0x0A
#define PL2303_STATUS_REG_PL2303HX 0x8080
/* Different PL2303 IC types */
#define USBH_PL2303_TYPE_UNKNOWN 0
#define USBH_PL2303_TYPE_PL2303 1
#define USBH_PL2303_TYPE_PL2303HX 2
#define USBH_PL2303_TYPE_PL2303HXD 3
#define USBH_PL2303_TYPE_PL2303HXN 4
struct usbh_pl2303 {
struct usbh_hubport *hport;
struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
struct usbh_urb bulkout_urb;
struct usbh_urb bulkin_urb;
struct cdc_line_coding linecoding;
uint8_t intf;
uint8_t minor;
uint8_t chiptype;
void *user_data;
};
#ifdef __cplusplus
extern "C" {
#endif
int usbh_pl2303_set_line_coding(struct usbh_pl2303 *pl2303_class, struct cdc_line_coding *line_coding);
int usbh_pl2303_get_line_coding(struct usbh_pl2303 *pl2303_class, struct cdc_line_coding *line_coding);
int usbh_pl2303_set_line_state(struct usbh_pl2303 *pl2303_class, bool dtr, bool rts);
int usbh_pl2303_bulk_in_transfer(struct usbh_pl2303 *pl2303_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
int usbh_pl2303_bulk_out_transfer(struct usbh_pl2303 *pl2303_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
void usbh_pl2303_run(struct usbh_pl2303 *pl2303_class);
void usbh_pl2303_stop(struct usbh_pl2303 *pl2303_class);
#ifdef __cplusplus
}
#endif
#endif /* USBH_PL2303_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,788 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbd_core.h"
#include "usbd_video.h"
struct video_entity_info {
uint8_t bDescriptorSubtype;
uint8_t bEntityId;
uint16_t wTerminalType;
};
struct usbd_video_priv {
struct video_probe_and_commit_controls probe;
struct video_probe_and_commit_controls commit;
uint8_t power_mode;
uint8_t error_code;
struct video_entity_info info[3];
} g_usbd_video[CONFIG_USBDEV_MAX_BUS];
static int usbd_video_control_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
{
uint8_t control_selector = (uint8_t)(setup->wValue >> 8);
switch (control_selector) {
case VIDEO_VC_VIDEO_POWER_MODE_CONTROL:
switch (setup->bRequest) {
case VIDEO_REQUEST_SET_CUR:
break;
case VIDEO_REQUEST_GET_CUR:
break;
case VIDEO_REQUEST_GET_INFO:
break;
default:
USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
break;
case VIDEO_VC_REQUEST_ERROR_CODE_CONTROL:
switch (setup->bRequest) {
case VIDEO_REQUEST_GET_CUR:
(*data)[0] = 0x06;
*len = 1;
break;
case VIDEO_REQUEST_GET_INFO:
break;
default:
USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
break;
default:
break;
}
return 0;
}
static int usbd_video_control_unit_terminal_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
{
uint8_t entity_id = (uint8_t)(setup->wIndex >> 8);
uint8_t control_selector = (uint8_t)(setup->wValue >> 8);
for (uint8_t i = 0; i < 3; i++) {
struct video_entity_info *entity_info = &g_usbd_video[busid].info[i];
if (entity_info->bEntityId == entity_id) {
switch (entity_info->bDescriptorSubtype) {
case VIDEO_VC_HEADER_DESCRIPTOR_SUBTYPE:
break;
case VIDEO_VC_INPUT_TERMINAL_DESCRIPTOR_SUBTYPE:
if (entity_info->wTerminalType == VIDEO_ITT_CAMERA) {
switch (control_selector) {
case VIDEO_CT_AE_MODE_CONTROL:
switch (setup->bRequest) {
case VIDEO_REQUEST_GET_CUR:
(*data)[0] = 0x08;
*len = 1;
break;
default:
USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
break;
case VIDEO_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL:
switch (setup->bRequest) {
case VIDEO_REQUEST_GET_CUR: {
uint32_t dwExposureTimeAbsolute = 2500;
memcpy(*data, (uint8_t *)&dwExposureTimeAbsolute, 4);
*len = 4;
} break;
case VIDEO_REQUEST_GET_MIN: {
uint32_t dwExposureTimeAbsolute = 5; //0.0005sec
memcpy(*data, (uint8_t *)&dwExposureTimeAbsolute, 4);
*len = 4;
} break;
case VIDEO_REQUEST_GET_MAX: {
uint32_t dwExposureTimeAbsolute = 2500; //0.2500sec
memcpy(*data, (uint8_t *)&dwExposureTimeAbsolute, 4);
*len = 4;
} break;
case VIDEO_REQUEST_GET_RES: {
uint32_t dwExposureTimeAbsolute = 5; //0.0005sec
memcpy(*data, (uint8_t *)&dwExposureTimeAbsolute, 4);
*len = 4;
} break;
case VIDEO_REQUEST_GET_INFO:
(*data)[0] = 0x03; //struct video_camera_capabilities
*len = 1;
break;
case VIDEO_REQUEST_GET_DEF: {
uint32_t dwExposureTimeAbsolute = 2500; //0.2500sec
memcpy(*data, (uint8_t *)&dwExposureTimeAbsolute, 4);
*len = 4;
} break;
default:
USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
break;
case VIDEO_CT_FOCUS_ABSOLUTE_CONTROL:
switch (setup->bRequest) {
case VIDEO_REQUEST_GET_CUR: {
uint16_t wFocusAbsolute = 0x0080;
memcpy(*data, (uint8_t *)&wFocusAbsolute, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_MIN: {
uint16_t wFocusAbsolute = 0;
memcpy(*data, (uint8_t *)&wFocusAbsolute, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_MAX: {
uint16_t wFocusAbsolute = 0x00ff;
memcpy(*data, (uint8_t *)&wFocusAbsolute, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_RES: {
uint16_t wFocusAbsolute = 0x0001;
memcpy(*data, (uint8_t *)&wFocusAbsolute, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_INFO:
(*data)[0] = 0x03; //struct video_camera_capabilities
*len = 1;
break;
case VIDEO_REQUEST_GET_DEF: {
uint16_t wFocusAbsolute = 0x0080;
memcpy(*data, (uint8_t *)&wFocusAbsolute, 2);
*len = 2;
} break;
default:
USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
break;
case VIDEO_CT_ZOOM_ABSOLUTE_CONTROL:
switch (setup->bRequest) {
case VIDEO_REQUEST_GET_CUR: {
uint16_t wObjectiveFocalLength = 0x0064;
memcpy(*data, (uint8_t *)&wObjectiveFocalLength, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_MIN: {
uint16_t wObjectiveFocalLength = 0x0064;
memcpy(*data, (uint8_t *)&wObjectiveFocalLength, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_MAX: {
uint16_t wObjectiveFocalLength = 0x00c8;
memcpy(*data, (uint8_t *)&wObjectiveFocalLength, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_RES: {
uint16_t wObjectiveFocalLength = 0x0001;
memcpy(*data, (uint8_t *)&wObjectiveFocalLength, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_INFO:
(*data)[0] = 0x03; //struct video_camera_capabilities
*len = 1;
break;
case VIDEO_REQUEST_GET_DEF: {
uint16_t wObjectiveFocalLength = 0x0064;
memcpy(*data, (uint8_t *)&wObjectiveFocalLength, 2);
*len = 2;
} break;
default:
USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
break;
case VIDEO_CT_ROLL_ABSOLUTE_CONTROL:
switch (setup->bRequest) {
case VIDEO_REQUEST_GET_CUR: {
uint16_t wRollAbsolute = 0x0000;
memcpy(*data, (uint8_t *)&wRollAbsolute, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_MIN: {
uint16_t wRollAbsolute = 0x0000;
memcpy(*data, (uint8_t *)&wRollAbsolute, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_MAX: {
uint16_t wRollAbsolute = 0x00ff;
memcpy(*data, (uint8_t *)&wRollAbsolute, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_RES: {
uint16_t wRollAbsolute = 0x0001;
memcpy(*data, (uint8_t *)&wRollAbsolute, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_INFO:
(*data)[0] = 0x03; //struct video_camera_capabilities
*len = 1;
break;
case VIDEO_REQUEST_GET_DEF: {
uint16_t wRollAbsolute = 0x0000;
memcpy(*data, (uint8_t *)&wRollAbsolute, 2);
*len = 2;
} break;
default:
USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
break;
case VIDEO_CT_FOCUS_AUTO_CONTROL:
switch (setup->bRequest) {
case VIDEO_REQUEST_GET_CUR: {
uint16_t wFocusAuto = 0x0000;
memcpy(*data, (uint8_t *)&wFocusAuto, 2);
*len = 2;
} break;
default:
USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
break;
default:
USB_LOG_WRN("Unhandled Video Class control selector 0x%02x\r\n", control_selector);
return -1;
}
} else {
USB_LOG_WRN("Unhandled Video Class wTerminalType 0x%02x\r\n", entity_info->wTerminalType);
return -2;
}
break;
case VIDEO_VC_OUTPUT_TERMINAL_DESCRIPTOR_SUBTYPE:
break;
case VIDEO_VC_SELECTOR_UNIT_DESCRIPTOR_SUBTYPE:
break;
case VIDEO_VC_PROCESSING_UNIT_DESCRIPTOR_SUBTYPE:
switch (control_selector) {
case VIDEO_PU_BACKLIGHT_COMPENSATION_CONTROL:
switch (setup->bRequest) {
case VIDEO_REQUEST_GET_CUR: {
uint16_t wBacklightCompensation = 0x0004;
memcpy(*data, (uint8_t *)&wBacklightCompensation, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_MIN: {
uint16_t wBacklightCompensation = 0;
memcpy(*data, (uint8_t *)&wBacklightCompensation, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_MAX: {
uint16_t wBacklightCompensation = 8;
memcpy(*data, (uint8_t *)&wBacklightCompensation, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_RES: {
uint16_t wBacklightCompensation = 1;
memcpy(*data, (uint8_t *)&wBacklightCompensation, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_INFO:
(*data)[0] = 0x03; //struct video_camera_capabilities
*len = 1;
break;
case VIDEO_REQUEST_GET_DEF: {
uint16_t wBacklightCompensation = 4;
memcpy(*data, (uint8_t *)&wBacklightCompensation, 2);
*len = 2;
} break;
default:
USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
break;
case VIDEO_PU_BRIGHTNESS_CONTROL:
switch (setup->bRequest) {
case VIDEO_REQUEST_SET_CUR: {
//uint16_t wBrightness = (uint16_t)(*data)[1] << 8 | (uint16_t)(*data)[0];
} break;
case VIDEO_REQUEST_GET_CUR: {
uint16_t wBrightness = 0x0080;
memcpy(*data, (uint8_t *)&wBrightness, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_MIN: {
uint16_t wBrightness = 0x0001;
memcpy(*data, (uint8_t *)&wBrightness, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_MAX: {
uint16_t wBrightness = 0x00ff;
memcpy(*data, (uint8_t *)&wBrightness, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_RES: {
uint16_t wBrightness = 0x0001;
memcpy(*data, (uint8_t *)&wBrightness, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_INFO:
(*data)[0] = 0x03; //struct video_camera_capabilities
*len = 1;
break;
case VIDEO_REQUEST_GET_DEF: {
uint16_t wBrightness = 0x0080;
memcpy(*data, (uint8_t *)&wBrightness, 2);
*len = 2;
} break;
default:
USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
break;
case VIDEO_PU_CONTRAST_CONTROL:
switch (setup->bRequest) {
case VIDEO_REQUEST_GET_CUR: {
uint16_t wContrast = 0x0080;
memcpy(*data, (uint8_t *)&wContrast, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_MIN: {
uint16_t wContrast = 0x0001;
memcpy(*data, (uint8_t *)&wContrast, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_MAX: {
uint16_t wContrast = 0x00ff;
memcpy(*data, (uint8_t *)&wContrast, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_RES: {
uint16_t wContrast = 0x0001;
memcpy(*data, (uint8_t *)&wContrast, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_INFO:
(*data)[0] = 0x03; //struct video_camera_capabilities
*len = 1;
break;
case VIDEO_REQUEST_GET_DEF: {
uint16_t wContrast = 0x0080;
memcpy(*data, (uint8_t *)&wContrast, 2);
*len = 2;
} break;
default:
USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
break;
case VIDEO_PU_HUE_CONTROL:
switch (setup->bRequest) {
case VIDEO_REQUEST_GET_CUR: {
uint16_t wHue = 0x0080;
memcpy(*data, (uint8_t *)&wHue, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_MIN: {
uint16_t wHue = 0x0001;
memcpy(*data, (uint8_t *)&wHue, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_MAX: {
uint16_t wHue = 0x00ff;
memcpy(*data, (uint8_t *)&wHue, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_RES: {
uint16_t wHue = 0x0001;
memcpy(*data, (uint8_t *)&wHue, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_INFO:
(*data)[0] = 0x03; //struct video_camera_capabilities
*len = 1;
break;
case VIDEO_REQUEST_GET_DEF: {
uint16_t wHue = 0x0080;
memcpy(*data, (uint8_t *)&wHue, 2);
*len = 2;
} break;
default:
USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
break;
case VIDEO_PU_SATURATION_CONTROL:
switch (setup->bRequest) {
case VIDEO_REQUEST_GET_MIN: {
uint16_t wSaturation = 0x0001;
memcpy(*data, (uint8_t *)&wSaturation, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_MAX: {
uint16_t wSaturation = 0x00ff;
memcpy(*data, (uint8_t *)&wSaturation, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_RES: {
uint16_t wSaturation = 0x0001;
memcpy(*data, (uint8_t *)&wSaturation, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_INFO:
(*data)[0] = 0x03; //struct video_camera_capabilities
*len = 1;
break;
case VIDEO_REQUEST_GET_DEF: {
uint16_t wSaturation = 0x0080;
memcpy(*data, (uint8_t *)&wSaturation, 2);
*len = 2;
} break;
default:
USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
break;
case VIDEO_PU_SHARPNESS_CONTROL:
switch (setup->bRequest) {
case VIDEO_REQUEST_GET_MIN: {
uint16_t wSharpness = 0x0001;
memcpy(*data, (uint8_t *)&wSharpness, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_MAX: {
uint16_t wSharpness = 0x00ff;
memcpy(*data, (uint8_t *)&wSharpness, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_RES: {
uint16_t wSharpness = 0x0001;
memcpy(*data, (uint8_t *)&wSharpness, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_INFO:
(*data)[0] = 0x03; //struct video_camera_capabilities
*len = 1;
break;
case VIDEO_REQUEST_GET_DEF: {
uint16_t wSharpness = 0x0080;
memcpy(*data, (uint8_t *)&wSharpness, 2);
*len = 2;
} break;
default:
USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
break;
case VIDEO_PU_GAIN_CONTROL:
switch (setup->bRequest) {
case VIDEO_REQUEST_GET_MIN: {
uint16_t wGain = 0;
memcpy(*data, (uint8_t *)&wGain, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_MAX: {
uint16_t wGain = 255;
memcpy(*data, (uint8_t *)&wGain, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_RES: {
uint16_t wGain = 1;
memcpy(*data, (uint8_t *)&wGain, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_INFO:
(*data)[0] = 0x03; //struct video_camera_capabilities
*len = 1;
break;
case VIDEO_REQUEST_GET_DEF: {
uint16_t wGain = 255;
memcpy(*data, (uint8_t *)&wGain, 2);
*len = 2;
} break;
default:
USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
break;
case VIDEO_PU_WHITE_BALANCE_TEMPERATURE_CONTROL:
switch (setup->bRequest) {
case VIDEO_REQUEST_GET_CUR: {
uint16_t wWhiteBalance_Temprature = 417;
memcpy(*data, (uint8_t *)&wWhiteBalance_Temprature, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_MIN: {
uint16_t wWhiteBalance_Temprature = 300;
memcpy(*data, (uint8_t *)&wWhiteBalance_Temprature, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_MAX: {
uint16_t wWhiteBalance_Temprature = 600;
memcpy(*data, (uint8_t *)&wWhiteBalance_Temprature, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_RES: {
uint16_t wWhiteBalance_Temprature = 1;
memcpy(*data, (uint8_t *)&wWhiteBalance_Temprature, 2);
*len = 2;
} break;
case VIDEO_REQUEST_GET_INFO:
(*data)[0] = 0x03; //struct video_camera_capabilities
*len = 1;
break;
case VIDEO_REQUEST_GET_DEF: {
uint16_t wWhiteBalance_Temprature = 417;
memcpy(*data, (uint8_t *)&wWhiteBalance_Temprature, 2);
*len = 2;
} break;
default:
USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
break;
case VIDEO_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL:
switch (setup->bRequest) {
case VIDEO_REQUEST_GET_CUR: {
uint16_t wWhiteBalance_Temprature_Auto = 1;
memcpy(*data, (uint8_t *)&wWhiteBalance_Temprature_Auto, 1);
*len = 1;
} break;
default:
USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
break;
default:
g_usbd_video[busid].error_code = 0x06;
USB_LOG_WRN("Unhandled Video Class control selector 0x%02x\r\n", control_selector);
return -1;
}
break;
case VIDEO_VC_EXTENSION_UNIT_DESCRIPTOR_SUBTYPE:
break;
case VIDEO_VC_ENCODING_UNIT_DESCRIPTOR_SUBTYPE:
break;
default:
break;
}
}
}
return 0;
}
static int usbd_video_stream_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
{
uint8_t control_selector = (uint8_t)(setup->wValue >> 8);
switch (control_selector) {
case VIDEO_VS_PROBE_CONTROL:
switch (setup->bRequest) {
case VIDEO_REQUEST_SET_CUR:
//memcpy((uint8_t *)&g_usbd_video[busid].probe, *data, setup->wLength);
break;
case VIDEO_REQUEST_GET_CUR:
memcpy(*data, (uint8_t *)&g_usbd_video[busid].probe, setup->wLength);
*len = sizeof(struct video_probe_and_commit_controls);
break;
case VIDEO_REQUEST_GET_MIN:
case VIDEO_REQUEST_GET_MAX:
case VIDEO_REQUEST_GET_RES:
case VIDEO_REQUEST_GET_DEF:
memcpy(*data, (uint8_t *)&g_usbd_video[busid].probe, setup->wLength);
*len = sizeof(struct video_probe_and_commit_controls);
break;
case VIDEO_REQUEST_GET_LEN:
(*data)[0] = sizeof(struct video_probe_and_commit_controls);
*len = 1;
break;
case VIDEO_REQUEST_GET_INFO:
(*data)[0] = 0x03;
*len = 1;
break;
default:
USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
break;
case VIDEO_VS_COMMIT_CONTROL:
switch (setup->bRequest) {
case VIDEO_REQUEST_SET_CUR:
//memcpy((uint8_t *)&g_usbd_video[busid].commit, *data, setup->wLength);
break;
case VIDEO_REQUEST_GET_CUR:
memcpy(*data, (uint8_t *)&g_usbd_video[busid].commit, setup->wLength);
*len = sizeof(struct video_probe_and_commit_controls);
break;
case VIDEO_REQUEST_GET_MIN:
case VIDEO_REQUEST_GET_MAX:
case VIDEO_REQUEST_GET_RES:
case VIDEO_REQUEST_GET_DEF:
memcpy(*data, (uint8_t *)&g_usbd_video[busid].commit, setup->wLength);
*len = sizeof(struct video_probe_and_commit_controls);
break;
case VIDEO_REQUEST_GET_LEN:
(*data)[0] = sizeof(struct video_probe_and_commit_controls);
*len = 1;
break;
case VIDEO_REQUEST_GET_INFO:
(*data)[0] = 0x03;
*len = 1;
break;
default:
USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
break;
case VIDEO_VS_STREAM_ERROR_CODE_CONTROL:
switch (setup->bRequest) {
case VIDEO_REQUEST_GET_CUR:
(*data)[0] = g_usbd_video[busid].error_code;
*len = 1;
break;
case VIDEO_REQUEST_GET_INFO:
(*data)[0] = 0x01;
*len = 1;
break;
default:
USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
return -1;
}
break;
default:
break;
}
return 0;
}
static int video_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
{
USB_LOG_DBG("Video Class request: "
"bRequest 0x%02x\r\n",
setup->bRequest);
uint8_t intf_num = (uint8_t)setup->wIndex;
uint8_t entity_id = (uint8_t)(setup->wIndex >> 8);
if (intf_num == 0) { /* Video Control Interface */
if (entity_id == 0) {
return usbd_video_control_request_handler(busid, setup, data, len); /* Interface Control Requests */
} else {
return usbd_video_control_unit_terminal_request_handler(busid, setup, data, len); /* Unit and Terminal Requests */
}
} else if (intf_num == 1) { /* Video Stream Inteface */
return usbd_video_stream_request_handler(busid, setup, data, len); /* Interface Stream Requests */
}
return -1;
}
static void video_notify_handler(uint8_t busid, uint8_t event, void *arg)
{
switch (event) {
case USBD_EVENT_RESET:
g_usbd_video[busid].error_code = 0;
g_usbd_video[busid].power_mode = 0;
break;
case USBD_EVENT_SET_INTERFACE: {
struct usb_interface_descriptor *intf = (struct usb_interface_descriptor *)arg;
if (intf->bAlternateSetting == 1) {
usbd_video_open(busid, intf->bInterfaceNumber);
} else {
usbd_video_close(busid, intf->bInterfaceNumber);
}
}
break;
default:
break;
}
}
void usbd_video_probe_and_commit_controls_init(uint8_t busid, uint32_t dwFrameInterval, uint32_t dwMaxVideoFrameSize, uint32_t dwMaxPayloadTransferSize)
{
g_usbd_video[busid].probe.hintUnion.bmHint = 0x01;
g_usbd_video[busid].probe.hintUnion1.bmHint = 0;
g_usbd_video[busid].probe.bFormatIndex = 1;
g_usbd_video[busid].probe.bFrameIndex = 1;
g_usbd_video[busid].probe.dwFrameInterval = dwFrameInterval;
g_usbd_video[busid].probe.wKeyFrameRate = 0;
g_usbd_video[busid].probe.wPFrameRate = 0;
g_usbd_video[busid].probe.wCompQuality = 0;
g_usbd_video[busid].probe.wCompWindowSize = 0;
g_usbd_video[busid].probe.wDelay = 0;
g_usbd_video[busid].probe.dwMaxVideoFrameSize = dwMaxVideoFrameSize;
g_usbd_video[busid].probe.dwMaxPayloadTransferSize = dwMaxPayloadTransferSize;
g_usbd_video[busid].probe.dwClockFrequency = 0;
g_usbd_video[busid].probe.bmFramingInfo = 0;
g_usbd_video[busid].probe.bPreferedVersion = 0;
g_usbd_video[busid].probe.bMinVersion = 0;
g_usbd_video[busid].probe.bMaxVersion = 0;
g_usbd_video[busid].commit.hintUnion.bmHint = 0x01;
g_usbd_video[busid].commit.hintUnion1.bmHint = 0;
g_usbd_video[busid].commit.bFormatIndex = 1;
g_usbd_video[busid].commit.bFrameIndex = 1;
g_usbd_video[busid].commit.dwFrameInterval = dwFrameInterval;
g_usbd_video[busid].commit.wKeyFrameRate = 0;
g_usbd_video[busid].commit.wPFrameRate = 0;
g_usbd_video[busid].commit.wCompQuality = 0;
g_usbd_video[busid].commit.wCompWindowSize = 0;
g_usbd_video[busid].commit.wDelay = 0;
g_usbd_video[busid].commit.dwMaxVideoFrameSize = dwMaxVideoFrameSize;
g_usbd_video[busid].commit.dwMaxPayloadTransferSize = dwMaxPayloadTransferSize;
g_usbd_video[busid].commit.dwClockFrequency = 0;
g_usbd_video[busid].commit.bmFramingInfo = 0;
g_usbd_video[busid].commit.bPreferedVersion = 0;
g_usbd_video[busid].commit.bMinVersion = 0;
g_usbd_video[busid].commit.bMaxVersion = 0;
}
struct usbd_interface *usbd_video_init_intf(uint8_t busid, struct usbd_interface *intf,
uint32_t dwFrameInterval,
uint32_t dwMaxVideoFrameSize,
uint32_t dwMaxPayloadTransferSize)
{
intf->class_interface_handler = video_class_interface_request_handler;
intf->class_endpoint_handler = NULL;
intf->vendor_handler = NULL;
intf->notify_handler = video_notify_handler;
g_usbd_video[busid].info[0].bDescriptorSubtype = VIDEO_VC_INPUT_TERMINAL_DESCRIPTOR_SUBTYPE;
g_usbd_video[busid].info[0].bEntityId = 0x01;
g_usbd_video[busid].info[0].wTerminalType = VIDEO_ITT_CAMERA;
g_usbd_video[busid].info[1].bDescriptorSubtype = VIDEO_VC_OUTPUT_TERMINAL_DESCRIPTOR_SUBTYPE;
g_usbd_video[busid].info[1].bEntityId = 0x03;
g_usbd_video[busid].info[1].wTerminalType = 0x00;
g_usbd_video[busid].info[2].bDescriptorSubtype = VIDEO_VC_PROCESSING_UNIT_DESCRIPTOR_SUBTYPE;
g_usbd_video[busid].info[2].bEntityId = 0x02;
g_usbd_video[busid].info[2].wTerminalType = 0x00;
usbd_video_probe_and_commit_controls_init(busid, dwFrameInterval, dwMaxVideoFrameSize, dwMaxPayloadTransferSize);
return intf;
}
uint32_t usbd_video_payload_fill(uint8_t busid, uint8_t *input, uint32_t input_len, uint8_t *output, uint32_t *out_len)
{
uint32_t packets;
uint32_t last_packet_size;
uint32_t picture_pos = 0;
static uint8_t uvc_header[2] = { 0x02, 0x80 };
packets = (input_len + (g_usbd_video[busid].probe.dwMaxPayloadTransferSize - 2) ) / (g_usbd_video[busid].probe.dwMaxPayloadTransferSize - 2);
last_packet_size = input_len - ((packets - 1) * (g_usbd_video[busid].probe.dwMaxPayloadTransferSize - 2));
for (size_t i = 0; i < packets; i++) {
output[g_usbd_video[busid].probe.dwMaxPayloadTransferSize * i] = uvc_header[0];
output[g_usbd_video[busid].probe.dwMaxPayloadTransferSize * i + 1] = uvc_header[1];
if (i == (packets - 1)) {
memcpy(&output[2 + g_usbd_video[busid].probe.dwMaxPayloadTransferSize * i], &input[picture_pos], last_packet_size);
output[g_usbd_video[busid].probe.dwMaxPayloadTransferSize * i + 1] |= (1 << 1);
} else {
memcpy(&output[2 + g_usbd_video[busid].probe.dwMaxPayloadTransferSize * i], &input[picture_pos], g_usbd_video[busid].probe.dwMaxPayloadTransferSize - 2);
picture_pos += g_usbd_video[busid].probe.dwMaxPayloadTransferSize - 2;
}
}
uvc_header[1] ^= 1;
*out_len = (input_len + 2 * packets);
return packets;
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBD_VIDEO_H
#define USBD_VIDEO_H
#include "usb_video.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Init video interface driver */
struct usbd_interface *usbd_video_init_intf(uint8_t busid, struct usbd_interface *intf,
uint32_t dwFrameInterval,
uint32_t dwMaxVideoFrameSize,
uint32_t dwMaxPayloadTransferSize);
void usbd_video_open(uint8_t busid, uint8_t intf);
void usbd_video_close(uint8_t busid, uint8_t intf);
uint32_t usbd_video_payload_fill(uint8_t busid, uint8_t *input, uint32_t input_len, uint8_t *output, uint32_t *out_len);
#ifdef __cplusplus
}
#endif
#endif /* USBD_VIDEO_H */

View File

@@ -0,0 +1,542 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbh_core.h"
#include "usbh_video.h"
#undef USB_DBG_TAG
#define USB_DBG_TAG "usbh_video"
#include "usb_log.h"
#define DEV_FORMAT "/dev/video%d"
/* general descriptor field offsets */
#define DESC_bLength 0 /** Length offset */
#define DESC_bDescriptorType 1 /** Descriptor type offset */
#define DESC_bDescriptorSubType 2 /** Descriptor subtype offset */
#define DESC_bNumFormats 3 /** Descriptor numformat offset */
#define DESC_bNumFrameDescriptors 4 /** Descriptor numframe offset */
#define DESC_bFormatIndex 3 /** Descriptor format index offset */
#define DESC_bFrameIndex 3 /** Descriptor frame index offset */
/* interface descriptor field offsets */
#define INTF_DESC_bInterfaceNumber 2 /** Interface number offset */
#define INTF_DESC_bAlternateSetting 3 /** Alternate setting offset */
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_video_buf[128];
static const char *format_type[] = { "uncompressed", "mjpeg" };
static struct usbh_video g_video_class[CONFIG_USBHOST_MAX_VIDEO_CLASS];
static uint32_t g_devinuse = 0;
static struct usbh_video *usbh_video_class_alloc(void)
{
int devno;
for (devno = 0; devno < CONFIG_USBHOST_MAX_VIDEO_CLASS; devno++) {
if ((g_devinuse & (1 << devno)) == 0) {
g_devinuse |= (1 << devno);
memset(&g_video_class[devno], 0, sizeof(struct usbh_video));
g_video_class[devno].minor = devno;
return &g_video_class[devno];
}
}
return NULL;
}
static void usbh_video_class_free(struct usbh_video *video_class)
{
int devno = video_class->minor;
if (devno >= 0 && devno < 32) {
g_devinuse &= ~(1 << devno);
}
memset(video_class, 0, sizeof(struct usbh_video));
}
int usbh_video_get(struct usbh_video *video_class, uint8_t request, uint8_t intf, uint8_t entity_id, uint8_t cs, uint8_t *buf, uint16_t len)
{
struct usb_setup_packet *setup;
int ret;
uint8_t retry;
if (!video_class || !video_class->hport) {
return -USB_ERR_INVAL;
}
setup = video_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = request;
setup->wValue = cs << 8;
setup->wIndex = (entity_id << 8) | intf;
setup->wLength = len;
retry = 0;
while (1) {
ret = usbh_control_transfer(video_class->hport, setup, g_video_buf);
if (ret > 0) {
break;
}
retry++;
if (retry == 3) {
return ret;
}
}
if (buf) {
memcpy(buf, g_video_buf, len);
}
return ret;
}
int usbh_video_set(struct usbh_video *video_class, uint8_t request, uint8_t intf, uint8_t entity_id, uint8_t cs, uint8_t *buf, uint16_t len)
{
struct usb_setup_packet *setup;
int ret;
if (!video_class || !video_class->hport) {
return -USB_ERR_INVAL;
}
setup = video_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = request;
setup->wValue = cs << 8;
setup->wIndex = (entity_id << 8) | intf;
setup->wLength = len;
memcpy(g_video_buf, buf, len);
ret = usbh_control_transfer(video_class->hport, setup, g_video_buf);
usb_osal_msleep(50);
return ret;
}
int usbh_videostreaming_get_cur_probe(struct usbh_video *video_class)
{
return usbh_video_get(video_class, VIDEO_REQUEST_GET_CUR, video_class->data_intf, 0x00, VIDEO_VS_PROBE_CONTROL, (uint8_t *)&video_class->probe, 26);
}
int usbh_videostreaming_set_cur_probe(struct usbh_video *video_class, uint8_t formatindex, uint8_t frameindex)
{
video_class->probe.bFormatIndex = formatindex;
video_class->probe.bFrameIndex = frameindex;
video_class->probe.dwMaxPayloadTransferSize = 0;
video_class->probe.dwFrameInterval = 333333;
return usbh_video_set(video_class, VIDEO_REQUEST_SET_CUR, video_class->data_intf, 0x00, VIDEO_VS_PROBE_CONTROL, (uint8_t *)&video_class->probe, 26);
}
int usbh_videostreaming_set_cur_commit(struct usbh_video *video_class, uint8_t formatindex, uint8_t frameindex)
{
memcpy(&video_class->commit, &video_class->probe, sizeof(struct video_probe_and_commit_controls));
video_class->commit.bFormatIndex = formatindex;
video_class->commit.bFrameIndex = frameindex;
video_class->commit.dwFrameInterval = 333333;
return usbh_video_set(video_class, VIDEO_REQUEST_SET_CUR, video_class->data_intf, 0x00, VIDEO_VS_COMMIT_CONTROL, (uint8_t *)&video_class->commit, 26);
}
int usbh_video_open(struct usbh_video *video_class,
uint8_t format_type,
uint16_t wWidth,
uint16_t wHeight,
uint8_t altsetting)
{
struct usb_setup_packet *setup;
struct usb_endpoint_descriptor *ep_desc;
uint8_t mult;
uint16_t mps;
int ret;
bool found = false;
uint8_t formatidx = 0;
uint8_t frameidx = 0;
uint8_t step;
if (!video_class || !video_class->hport) {
return -USB_ERR_INVAL;
}
setup = video_class->hport->setup;
if (video_class->is_opened) {
return 0;
}
for (uint8_t i = 0; i < video_class->num_of_formats; i++) {
if (format_type == video_class->format[i].format_type) {
formatidx = i + 1;
for (uint8_t j = 0; j < video_class->format[i].num_of_frames; j++) {
if ((wWidth == video_class->format[i].frame[j].wWidth) &&
(wHeight == video_class->format[i].frame[j].wHeight)) {
frameidx = j + 1;
found = true;
break;
}
}
}
}
if (found == false) {
return -USB_ERR_NODEV;
}
if (altsetting > (video_class->num_of_intf_altsettings - 1)) {
return -USB_ERR_INVAL;
}
/* Open video step:
* Get CUR request (probe)
* Set CUR request (probe)
* Get CUR request (probe)
* Get MAX request (probe)
* Get MIN request (probe)
* Get CUR request (probe)
* Set CUR request (commit)
*
*/
step = 0;
ret = usbh_videostreaming_get_cur_probe(video_class);
if (ret < 0) {
goto errout;
}
step = 1;
ret = usbh_videostreaming_set_cur_probe(video_class, formatidx, frameidx);
if (ret < 0) {
goto errout;
}
step = 2;
ret = usbh_videostreaming_get_cur_probe(video_class);
if (ret < 0) {
goto errout;
}
step = 3;
ret = usbh_video_get(video_class, VIDEO_REQUEST_GET_MAX, video_class->data_intf, 0x00, VIDEO_VS_PROBE_CONTROL, NULL, 26);
if (ret < 0) {
goto errout;
}
step = 4;
ret = usbh_video_get(video_class, VIDEO_REQUEST_GET_MIN, video_class->data_intf, 0x00, VIDEO_VS_PROBE_CONTROL, NULL, 26);
if (ret < 0) {
goto errout;
}
step = 5;
ret = usbh_videostreaming_set_cur_probe(video_class, formatidx, frameidx);
if (ret < 0) {
goto errout;
}
step = 6;
ret = usbh_videostreaming_get_cur_probe(video_class);
if (ret < 0) {
goto errout;
}
step = 7;
ret = usbh_videostreaming_set_cur_commit(video_class, formatidx, frameidx);
if (ret < 0) {
goto errout;
}
step = 8;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = USB_REQUEST_SET_INTERFACE;
setup->wValue = altsetting;
setup->wIndex = video_class->data_intf;
setup->wLength = 0;
ret = usbh_control_transfer(video_class->hport, setup, NULL);
if (ret < 0) {
goto errout;
}
ep_desc = &video_class->hport->config.intf[video_class->data_intf].altsetting[altsetting].ep[0].ep_desc;
mult = (ep_desc->wMaxPacketSize & USB_MAXPACKETSIZE_ADDITIONAL_TRANSCATION_MASK) >> USB_MAXPACKETSIZE_ADDITIONAL_TRANSCATION_SHIFT;
mps = ep_desc->wMaxPacketSize & USB_MAXPACKETSIZE_MASK;
if (ep_desc->bEndpointAddress & 0x80) {
video_class->isoin_mps = mps * (mult + 1);
USBH_EP_INIT(video_class->isoin, ep_desc);
} else {
video_class->isoout_mps = mps * (mult + 1);
USBH_EP_INIT(video_class->isoout, ep_desc);
}
USB_LOG_INFO("Open video and select formatidx:%u, frameidx:%u, altsetting:%u\r\n", formatidx, frameidx, altsetting);
video_class->is_opened = true;
video_class->current_format = format_type;
return ret;
errout:
USB_LOG_ERR("Fail to open video in step %u\r\n", step);
return ret;
}
int usbh_video_close(struct usbh_video *video_class)
{
struct usb_setup_packet *setup;
int ret = 0;
if (!video_class || !video_class->hport) {
return -USB_ERR_INVAL;
}
setup = video_class->hport->setup;
USB_LOG_INFO("Close video device\r\n");
video_class->is_opened = false;
if (video_class->isoin) {
video_class->isoin = NULL;
}
if (video_class->isoout) {
video_class->isoout = NULL;
}
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = USB_REQUEST_SET_INTERFACE;
setup->wValue = 0;
setup->wIndex = video_class->data_intf;
setup->wLength = 0;
ret = usbh_control_transfer(video_class->hport, setup, NULL);
if (ret < 0) {
return ret;
}
return ret;
}
void usbh_video_list_info(struct usbh_video *video_class)
{
struct usb_endpoint_descriptor *ep_desc;
uint8_t mult;
uint16_t mps;
USB_LOG_INFO("============= Video device information ===================\r\n");
USB_LOG_RAW("bcdVDC:%04x\r\n", video_class->bcdVDC);
USB_LOG_RAW("Num of altsettings:%u\r\n", video_class->num_of_intf_altsettings);
for (uint8_t i = 0; i < video_class->num_of_intf_altsettings; i++) {
if (i == 0) {
USB_LOG_RAW("Ingore altsetting 0\r\n");
continue;
}
ep_desc = &video_class->hport->config.intf[video_class->data_intf].altsetting[i].ep[0].ep_desc;
mult = (ep_desc->wMaxPacketSize & USB_MAXPACKETSIZE_ADDITIONAL_TRANSCATION_MASK) >> USB_MAXPACKETSIZE_ADDITIONAL_TRANSCATION_SHIFT;
mps = ep_desc->wMaxPacketSize & USB_MAXPACKETSIZE_MASK;
USB_LOG_RAW("Altsetting:%u, Ep=%02x Attr=%02u Mps=%d Interval=%02u Mult=%02u\r\n",
i,
ep_desc->bEndpointAddress,
ep_desc->bmAttributes,
mps,
ep_desc->bInterval,
mult);
}
USB_LOG_RAW("bNumFormats:%u\r\n", video_class->num_of_formats);
for (uint8_t i = 0; i < video_class->num_of_formats; i++) {
USB_LOG_RAW(" FormatIndex:%u\r\n", i + 1);
USB_LOG_RAW(" FormatType:%s\r\n", format_type[video_class->format[i].format_type]);
USB_LOG_RAW(" bNumFrames:%u\r\n", video_class->format[i].num_of_frames);
USB_LOG_RAW(" Resolution:\r\n");
for (uint8_t j = 0; j < video_class->format[i].num_of_frames; j++) {
USB_LOG_RAW(" FrameIndex:%u\r\n", j + 1);
USB_LOG_RAW(" wWidth: %d, wHeight: %d\r\n",
video_class->format[i].frame[j].wWidth,
video_class->format[i].frame[j].wHeight);
}
}
USB_LOG_INFO("============= Video device information ===================\r\n");
}
static int usbh_video_ctrl_connect(struct usbh_hubport *hport, uint8_t intf)
{
int ret;
uint8_t cur_iface = 0xff;
// uint8_t cur_alt_setting = 0xff;
uint8_t frame_index = 0xff;
uint8_t format_index = 0xff;
uint8_t num_of_frames = 0xff;
uint8_t *p;
struct usbh_video *video_class = usbh_video_class_alloc();
if (video_class == NULL) {
USB_LOG_ERR("Fail to alloc video_class\r\n");
return -USB_ERR_NOMEM;
}
video_class->hport = hport;
video_class->ctrl_intf = intf;
video_class->data_intf = intf + 1;
video_class->num_of_intf_altsettings = hport->config.intf[intf + 1].altsetting_num;
hport->config.intf[intf].priv = video_class;
ret = usbh_video_close(video_class);
if (ret < 0) {
USB_LOG_ERR("Fail to close video device\r\n");
return ret;
}
p = hport->raw_config_desc;
while (p[DESC_bLength]) {
switch (p[DESC_bDescriptorType]) {
case USB_DESCRIPTOR_TYPE_INTERFACE:
cur_iface = p[INTF_DESC_bInterfaceNumber];
//cur_alt_setting = p[INTF_DESC_bAlternateSetting];
break;
case USB_DESCRIPTOR_TYPE_ENDPOINT:
//ep_desc = (struct usb_endpoint_descriptor *)p;
break;
case VIDEO_CS_INTERFACE_DESCRIPTOR_TYPE:
if (cur_iface == video_class->ctrl_intf) {
switch (p[DESC_bDescriptorSubType]) {
case VIDEO_VC_HEADER_DESCRIPTOR_SUBTYPE:
video_class->bcdVDC = ((uint16_t)p[4] << 8) | (uint16_t)p[3];
break;
case VIDEO_VC_INPUT_TERMINAL_DESCRIPTOR_SUBTYPE:
break;
case VIDEO_VC_OUTPUT_TERMINAL_DESCRIPTOR_SUBTYPE:
break;
case VIDEO_VC_PROCESSING_UNIT_DESCRIPTOR_SUBTYPE:
break;
default:
break;
}
} else if (cur_iface == video_class->data_intf) {
switch (p[DESC_bDescriptorSubType]) {
case VIDEO_VS_INPUT_HEADER_DESCRIPTOR_SUBTYPE:
video_class->num_of_formats = p[DESC_bNumFormats];
break;
case VIDEO_VS_FORMAT_UNCOMPRESSED_DESCRIPTOR_SUBTYPE:
format_index = p[DESC_bFormatIndex];
num_of_frames = p[DESC_bNumFrameDescriptors];
video_class->format[format_index - 1].num_of_frames = num_of_frames;
video_class->format[format_index - 1].format_type = USBH_VIDEO_FORMAT_UNCOMPRESSED;
break;
case VIDEO_VS_FORMAT_MJPEG_DESCRIPTOR_SUBTYPE:
format_index = p[DESC_bFormatIndex];
num_of_frames = p[DESC_bNumFrameDescriptors];
video_class->format[format_index - 1].num_of_frames = num_of_frames;
video_class->format[format_index - 1].format_type = USBH_VIDEO_FORMAT_MJPEG;
break;
case VIDEO_VS_FRAME_UNCOMPRESSED_DESCRIPTOR_SUBTYPE:
frame_index = p[DESC_bFrameIndex];
video_class->format[format_index - 1].frame[frame_index - 1].wWidth = ((struct video_cs_if_vs_frame_uncompressed_descriptor *)p)->wWidth;
video_class->format[format_index - 1].frame[frame_index - 1].wHeight = ((struct video_cs_if_vs_frame_uncompressed_descriptor *)p)->wHeight;
break;
case VIDEO_VS_FRAME_MJPEG_DESCRIPTOR_SUBTYPE:
frame_index = p[DESC_bFrameIndex];
video_class->format[format_index - 1].frame[frame_index - 1].wWidth = ((struct video_cs_if_vs_frame_mjpeg_descriptor *)p)->wWidth;
video_class->format[format_index - 1].frame[frame_index - 1].wHeight = ((struct video_cs_if_vs_frame_mjpeg_descriptor *)p)->wHeight;
break;
default:
break;
}
}
break;
default:
break;
}
/* skip to next descriptor */
p += p[DESC_bLength];
}
usbh_video_list_info(video_class);
snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, video_class->minor);
USB_LOG_INFO("Register Video Class:%s\r\n", hport->config.intf[intf].devname);
usbh_video_run(video_class);
return ret;
}
static int usbh_video_ctrl_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
int ret = 0;
struct usbh_video *video_class = (struct usbh_video *)hport->config.intf[intf].priv;
if (video_class) {
if (video_class->isoin) {
}
if (video_class->isoout) {
}
if (hport->config.intf[intf].devname[0] != '\0') {
USB_LOG_INFO("Unregister Video Class:%s\r\n", hport->config.intf[intf].devname);
usbh_video_stop(video_class);
}
usbh_video_class_free(video_class);
}
return ret;
}
static int usbh_video_streaming_connect(struct usbh_hubport *hport, uint8_t intf)
{
return 0;
}
static int usbh_video_streaming_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
return 0;
}
__WEAK void usbh_video_run(struct usbh_video *video_class)
{
}
__WEAK void usbh_video_stop(struct usbh_video *video_class)
{
}
const struct usbh_class_driver video_ctrl_class_driver = {
.driver_name = "video_ctrl",
.connect = usbh_video_ctrl_connect,
.disconnect = usbh_video_ctrl_disconnect
};
const struct usbh_class_driver video_streaming_class_driver = {
.driver_name = "video_streaming",
.connect = usbh_video_streaming_connect,
.disconnect = usbh_video_streaming_disconnect
};
CLASS_INFO_DEFINE const struct usbh_class_info video_ctrl_class_info = {
.match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
.class = USB_DEVICE_CLASS_VIDEO,
.subclass = VIDEO_SC_VIDEOCONTROL,
.protocol = VIDEO_PC_PROTOCOL_UNDEFINED,
.id_table = NULL,
.class_driver = &video_ctrl_class_driver
};
CLASS_INFO_DEFINE const struct usbh_class_info video_streaming_class_info = {
.match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
.class = USB_DEVICE_CLASS_VIDEO,
.subclass = VIDEO_SC_VIDEOSTREAMING,
.protocol = VIDEO_PC_PROTOCOL_UNDEFINED,
.id_table = NULL,
.class_driver = &video_streaming_class_driver
};

View File

@@ -0,0 +1,85 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_VIDEO_H
#define USBH_VIDEO_H
#include "usb_video.h"
#define USBH_VIDEO_FORMAT_UNCOMPRESSED 0
#define USBH_VIDEO_FORMAT_MJPEG 1
struct usbh_video_resolution {
uint16_t wWidth;
uint16_t wHeight;
};
struct usbh_video_format {
struct usbh_video_resolution frame[12];
uint8_t format_type;
uint8_t num_of_frames;
};
struct usbh_videoframe {
uint8_t *frame_buf;
uint32_t frame_bufsize;
uint32_t frame_format;
uint32_t frame_size;
};
struct usbh_videostreaming {
struct usbh_videoframe *frame;
uint32_t frame_format;
uint32_t bufoffset;
uint16_t width;
uint16_t height;
};
struct usbh_video {
struct usbh_hubport *hport;
struct usb_endpoint_descriptor *isoin; /* ISO IN endpoint */
struct usb_endpoint_descriptor *isoout; /* ISO OUT endpoint */
uint8_t ctrl_intf; /* interface number */
uint8_t data_intf; /* interface number */
uint8_t minor;
struct video_probe_and_commit_controls probe;
struct video_probe_and_commit_controls commit;
uint16_t isoin_mps;
uint16_t isoout_mps;
bool is_opened;
uint8_t current_format;
uint16_t bcdVDC;
uint8_t num_of_intf_altsettings;
uint8_t num_of_formats;
struct usbh_video_format format[3];
void *user_data;
};
#ifdef __cplusplus
extern "C" {
#endif
int usbh_video_get(struct usbh_video *video_class, uint8_t request, uint8_t intf, uint8_t entity_id, uint8_t cs, uint8_t *buf, uint16_t len);
int usbh_video_set(struct usbh_video *video_class, uint8_t request, uint8_t intf, uint8_t entity_id, uint8_t cs, uint8_t *buf, uint16_t len);
int usbh_video_open(struct usbh_video *video_class,
uint8_t format_type,
uint16_t wWidth,
uint16_t wHeight,
uint8_t altsetting);
int usbh_video_close(struct usbh_video *video_class);
void usbh_video_list_info(struct usbh_video *video_class);
void usbh_video_run(struct usbh_video *video_class);
void usbh_video_stop(struct usbh_video *video_class);
#ifdef __cplusplus
}
#endif
#endif /* USBH_VIDEO_H */

View File

@@ -0,0 +1,270 @@
/* This file has been prepared for Doxygen automatic documentation generation.*/
/*! \file ndis.h ***************************************************************
*
* \brief
* This file contains the possible external configuration of the USB.
*
* \addtogroup usbstick
*
*
******************************************************************************/
/**
\ingroup usbstick
\defgroup RNDIS RNDIS Support
@{
*/
/*
* ndis.h
*
* Modified by Colin O'Flynn <coflynn@newae.com>
* ntddndis.h modified by Benedikt Spranger <b.spranger@pengutronix.de>
*
* Thanks to the cygwin development team,
* espacially to Casper S. Hornstrup <chorns@users.sourceforge.net>
*
* THIS SOFTWARE IS NOT COPYRIGHTED
*
* This source code is offered for use in the public domain. You may
* use, modify or distribute it freely.
*
* This code is distributed in the hope that it will be useful but
* WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
* DISCLAIMED. This includes but is not limited to warranties of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
*/
#ifndef _LINUX_NDIS_H
#define _LINUX_NDIS_H
#define NDIS_STATUS_MULTICAST_FULL 0xC0010009
#define NDIS_STATUS_MULTICAST_EXISTS 0xC001000A
#define NDIS_STATUS_MULTICAST_NOT_FOUND 0xC001000B
/* from drivers/net/sk98lin/h/skgepnmi.h */
#define OID_PNP_CAPABILITIES 0xFD010100
#define OID_PNP_SET_POWER 0xFD010101
#define OID_PNP_QUERY_POWER 0xFD010102
#define OID_PNP_ADD_WAKE_UP_PATTERN 0xFD010103
#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xFD010104
#define OID_PNP_ENABLE_WAKE_UP 0xFD010106
enum NDIS_DEVICE_POWER_STATE {
NdisDeviceStateUnspecified = 0,
NdisDeviceStateD0,
NdisDeviceStateD1,
NdisDeviceStateD2,
NdisDeviceStateD3,
NdisDeviceStateMaximum
};
struct NDIS_PM_WAKE_UP_CAPABILITIES {
enum NDIS_DEVICE_POWER_STATE MinMagicPacketWakeUp;
enum NDIS_DEVICE_POWER_STATE MinPatternWakeUp;
enum NDIS_DEVICE_POWER_STATE MinLinkChangeWakeUp;
};
/* NDIS_PNP_CAPABILITIES.Flags constants */
#define NDIS_DEVICE_WAKE_UP_ENABLE 0x00000001
#define NDIS_DEVICE_WAKE_ON_PATTERN_MATCH_ENABLE 0x00000002
#define NDIS_DEVICE_WAKE_ON_MAGIC_PACKET_ENABLE 0x00000004
/*
struct NDIS_PNP_CAPABILITIES {
__le32 Flags;
struct NDIS_PM_WAKE_UP_CAPABILITIES WakeUpCapabilities;
};
struct NDIS_PM_PACKET_PATTERN {
__le32 Priority;
__le32 Reserved;
__le32 MaskSize;
__le32 PatternOffset;
__le32 PatternSize;
__le32 PatternFlags;
};
*/
/* Required Object IDs (OIDs) */
#define OID_GEN_SUPPORTED_LIST 0x00010101
#define OID_GEN_HARDWARE_STATUS 0x00010102
#define OID_GEN_MEDIA_SUPPORTED 0x00010103
#define OID_GEN_MEDIA_IN_USE 0x00010104
#define OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105
#define OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106
#define OID_GEN_LINK_SPEED 0x00010107
#define OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108
#define OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109
#define OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A
#define OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B
#define OID_GEN_VENDOR_ID 0x0001010C
#define OID_GEN_VENDOR_DESCRIPTION 0x0001010D
#define OID_GEN_CURRENT_PACKET_FILTER 0x0001010E
#define OID_GEN_CURRENT_LOOKAHEAD 0x0001010F
#define OID_GEN_DRIVER_VERSION 0x00010110
#define OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111
#define OID_GEN_PROTOCOL_OPTIONS 0x00010112
#define OID_GEN_MAC_OPTIONS 0x00010113
#define OID_GEN_MEDIA_CONNECT_STATUS 0x00010114
#define OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115
#define OID_GEN_VENDOR_DRIVER_VERSION 0x00010116
#define OID_GEN_SUPPORTED_GUIDS 0x00010117
#define OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118
#define OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119
#define OID_GEN_MACHINE_NAME 0x0001021A
#define OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B
#define OID_GEN_VLAN_ID 0x0001021C
/* Optional OIDs */
#define OID_GEN_MEDIA_CAPABILITIES 0x00010201
#define OID_GEN_PHYSICAL_MEDIUM 0x00010202
/* Required statistics OIDs */
#define OID_GEN_XMIT_OK 0x00020101
#define OID_GEN_RCV_OK 0x00020102
#define OID_GEN_XMIT_ERROR 0x00020103
#define OID_GEN_RCV_ERROR 0x00020104
#define OID_GEN_RCV_NO_BUFFER 0x00020105
/* Optional statistics OIDs */
#define OID_GEN_DIRECTED_BYTES_XMIT 0x00020201
#define OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202
#define OID_GEN_MULTICAST_BYTES_XMIT 0x00020203
#define OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204
#define OID_GEN_BROADCAST_BYTES_XMIT 0x00020205
#define OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206
#define OID_GEN_DIRECTED_BYTES_RCV 0x00020207
#define OID_GEN_DIRECTED_FRAMES_RCV 0x00020208
#define OID_GEN_MULTICAST_BYTES_RCV 0x00020209
#define OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A
#define OID_GEN_BROADCAST_BYTES_RCV 0x0002020B
#define OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C
#define OID_GEN_RCV_CRC_ERROR 0x0002020D
#define OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E
#define OID_GEN_GET_TIME_CAPS 0x0002020F
#define OID_GEN_GET_NETCARD_TIME 0x00020210
#define OID_GEN_NETCARD_LOAD 0x00020211
#define OID_GEN_DEVICE_PROFILE 0x00020212
#define OID_GEN_INIT_TIME_MS 0x00020213
#define OID_GEN_RESET_COUNTS 0x00020214
#define OID_GEN_MEDIA_SENSE_COUNTS 0x00020215
#define OID_GEN_FRIENDLY_NAME 0x00020216
#define OID_GEN_MINIPORT_INFO 0x00020217
#define OID_GEN_RESET_VERIFY_PARAMETERS 0x00020218
/* IEEE 802.3 (Ethernet) OIDs */
#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001
#define OID_802_3_PERMANENT_ADDRESS 0x01010101
#define OID_802_3_CURRENT_ADDRESS 0x01010102
#define OID_802_3_MULTICAST_LIST 0x01010103
#define OID_802_3_MAXIMUM_LIST_SIZE 0x01010104
#define OID_802_3_MAC_OPTIONS 0x01010105
#define OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101
#define OID_802_3_XMIT_ONE_COLLISION 0x01020102
#define OID_802_3_XMIT_MORE_COLLISIONS 0x01020103
#define OID_802_3_XMIT_DEFERRED 0x01020201
#define OID_802_3_XMIT_MAX_COLLISIONS 0x01020202
#define OID_802_3_RCV_OVERRUN 0x01020203
#define OID_802_3_XMIT_UNDERRUN 0x01020204
#define OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205
#define OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206
#define OID_802_3_XMIT_LATE_COLLISIONS 0x01020207
/* Wireless LAN OIDs */
/* Mandatory */
#define OID_802_11_BSSID 0x0D010101 /* Q S */
#define OID_802_11_SSID 0x0D010102 /* Q S */
#define OID_802_11_NETWORK_TYPE_IN_USE 0x0D010204 /* Q S */
#define OID_802_11_RSSI 0x0D010206 /* Q I */
#define OID_802_11_BSSID_LIST 0x0D010217 /* Q */
#define OID_802_11_BSSID_LIST_SCAN 0x0D01011A /* S */
#define OID_802_11_INFRASTRUCTURE_MODE 0x0D010108 /* Q S */
#define OID_802_11_SUPPORTED_RATES 0x0D01020E /* Q */
#define OID_802_11_CONFIGURATION 0x0D010211 /* Q S */
#define OID_802_11_ADD_WEP 0x0D010113 /* S */
#define OID_802_11_WEP_STATUS 0x0D01011B /* Q S */
#define OID_802_11_REMOVE_WEP 0x0D010114 /* S */
#define OID_802_11_DISASSOCIATE 0x0D010115 /* S */
#define OID_802_11_AUTHENTICATION_MODE 0x0D010118 /* Q S */
#define OID_802_11_RELOAD_DEFAULTS 0x0D01011C /* S */
/* OID_GEN_MINIPORT_INFO constants */
#define NDIS_MINIPORT_BUS_MASTER 0x00000001
#define NDIS_MINIPORT_WDM_DRIVER 0x00000002
#define NDIS_MINIPORT_SG_LIST 0x00000004
#define NDIS_MINIPORT_SUPPORTS_MEDIA_QUERY 0x00000008
#define NDIS_MINIPORT_INDICATES_PACKETS 0x00000010
#define NDIS_MINIPORT_IGNORE_PACKET_QUEUE 0x00000020
#define NDIS_MINIPORT_IGNORE_REQUEST_QUEUE 0x00000040
#define NDIS_MINIPORT_IGNORE_TOKEN_RING_ERRORS 0x00000080
#define NDIS_MINIPORT_INTERMEDIATE_DRIVER 0x00000100
#define NDIS_MINIPORT_IS_NDIS_5 0x00000200
#define NDIS_MINIPORT_IS_CO 0x00000400
#define NDIS_MINIPORT_DESERIALIZE 0x00000800
#define NDIS_MINIPORT_REQUIRES_MEDIA_POLLING 0x00001000
#define NDIS_MINIPORT_SUPPORTS_MEDIA_SENSE 0x00002000
#define NDIS_MINIPORT_NETBOOT_CARD 0x00004000
#define NDIS_MINIPORT_PM_SUPPORTED 0x00008000
#define NDIS_MINIPORT_SUPPORTS_MAC_ADDRESS_OVERWRITE 0x00010000
#define NDIS_MINIPORT_USES_SAFE_BUFFER_APIS 0x00020000
#define NDIS_MINIPORT_HIDDEN 0x00040000
#define NDIS_MINIPORT_SWENUM 0x00080000
#define NDIS_MINIPORT_SURPRISE_REMOVE_OK 0x00100000
#define NDIS_MINIPORT_NO_HALT_ON_SUSPEND 0x00200000
#define NDIS_MINIPORT_HARDWARE_DEVICE 0x00400000
#define NDIS_MINIPORT_SUPPORTS_CANCEL_SEND_PACKETS 0x00800000
#define NDIS_MINIPORT_64BITS_DMA 0x01000000
#define NDIS_MEDIUM_802_3 0x00000000
#define NDIS_MEDIUM_802_5 0x00000001
#define NDIS_MEDIUM_FDDI 0x00000002
#define NDIS_MEDIUM_WAN 0x00000003
#define NDIS_MEDIUM_LOCAL_TALK 0x00000004
#define NDIS_MEDIUM_DIX 0x00000005
#define NDIS_MEDIUM_ARCENT_RAW 0x00000006
#define NDIS_MEDIUM_ARCENT_878_2 0x00000007
#define NDIS_MEDIUM_ATM 0x00000008
#define NDIS_MEDIUM_WIRELESS_LAN 0x00000009
#define NDIS_MEDIUM_IRDA 0x0000000A
#define NDIS_MEDIUM_BPC 0x0000000B
#define NDIS_MEDIUM_CO_WAN 0x0000000C
#define NDIS_MEDIUM_1394 0x0000000D
#define NDIS_PACKET_TYPE_DIRECTED 0x00000001
#define NDIS_PACKET_TYPE_MULTICAST 0x00000002
#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004
#define NDIS_PACKET_TYPE_BROADCAST 0x00000008
#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010
#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020
#define NDIS_PACKET_TYPE_SMT 0x00000040
#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080
#define NDIS_PACKET_TYPE_GROUP 0x00000100
#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200
#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400
#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800
#define NDIS_MEDIA_STATE_CONNECTED 0x00000000
#define NDIS_MEDIA_STATE_DISCONNECTED 0x00000001
#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA 0x00000001
#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED 0x00000002
#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND 0x00000004
#define NDIS_MAC_OPTION_NO_LOOPBACK 0x00000008
#define NDIS_MAC_OPTION_FULL_DUPLEX 0x00000010
#define NDIS_MAC_OPTION_EOTX_INDICATION 0x00000020
#define NDIS_MAC_OPTION_8021P_PRIORITY 0x00000040
#define NDIS_MAC_OPTION_RESERVED 0x80000000
/** Hardware status of the underlying NIC */
#define NDIS_HW_STS_READY 0x00000000UL
#define NDIS_HW_STS_INITIALIZING 0x00000001UL
#define NDIS_HW_STS_RESET 0x00000002UL
#define NDIS_HW_STS_CLOSING 0x00000003UL
#define NDIS_HW_STS_NOT_READY 0x00000004UL
#endif /* _LINUX_NDIS_H */
/** @} */

View File

@@ -0,0 +1,302 @@
/**
* \file rndis_protocol.h
* RNDIS Defines
*
* \author
* Colin O'Flynn <coflynn@newae.com>
*
* \addtogroup usbstick
*/
/* Copyright (c) 2008 Colin O'Flynn
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of the copyright holders nor the names of
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _RNDIS_H
#define _RNDIS_H
/**
\addtogroup RNDIS
@{
*/
#include <stdint.h>
#define RNDIS_MAJOR_VERSION 1
#define RNDIS_MINOR_VERSION 0
#define RNDIS_STATUS_SUCCESS 0X00000000
#define RNDIS_STATUS_FAILURE 0XC0000001
#define RNDIS_STATUS_INVALID_DATA 0XC0010015
#define RNDIS_STATUS_NOT_SUPPORTED 0XC00000BB
#define RNDIS_STATUS_MEDIA_CONNECT 0X4001000B
#define RNDIS_STATUS_MEDIA_DISCONNECT 0X4001000C
/* Message set for Connectionless (802.3) Devices */
#define REMOTE_NDIS_PACKET_MSG 0x00000001
#define REMOTE_NDIS_INITIALIZE_MSG 0X00000002
#define REMOTE_NDIS_HALT_MSG 0X00000003
#define REMOTE_NDIS_QUERY_MSG 0X00000004
#define REMOTE_NDIS_SET_MSG 0X00000005
#define REMOTE_NDIS_RESET_MSG 0X00000006
#define REMOTE_NDIS_INDICATE_STATUS_MSG 0X00000007
#define REMOTE_NDIS_KEEPALIVE_MSG 0X00000008
#define REMOTE_NDIS_INITIALIZE_CMPLT 0X80000002
#define REMOTE_NDIS_QUERY_CMPLT 0X80000004
#define REMOTE_NDIS_SET_CMPLT 0X80000005
#define REMOTE_NDIS_RESET_CMPLT 0X80000006
#define REMOTE_NDIS_KEEPALIVE_CMPLT 0X80000008
typedef uint32_t rndis_MessageType_t;
typedef uint32_t rndis_MessageLength_t;
typedef uint32_t rndis_RequestId_t;
typedef uint32_t rndis_MajorVersion_t;
typedef uint32_t rndis_MinorVersion_t;
typedef uint32_t rndis_MaxTransferSize_t;
typedef uint32_t rndis_Status_t;
/* Device Flags */
#define RNDIS_DF_CONNECTIONLESS 0x00000001
#define RNDIS_DF_CONNECTION_ORIENTED 0x00000002
typedef uint32_t rndis_DeviceFlags_t;
/* Mediums */
#define RNDIS_MEDIUM_802_3 0x00000000
typedef uint32_t rndis_Medium_t;
typedef uint32_t rndis_MaxPacketsPerTransfer_t;
typedef uint32_t rndis_PacketAlignmentFactor_t;
typedef uint32_t rndis_AfListOffset_t;
typedef uint32_t rndis_AfListSize_t;
/*** Remote NDIS Generic Message type ***/
typedef struct {
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
} rndis_generic_msg_t;
/*** Remote NDIS Initialize Message ***/
typedef struct {
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_RequestId_t RequestId;
rndis_MajorVersion_t MajorVersion;
rndis_MinorVersion_t MinorVersion;
rndis_MaxTransferSize_t MaxTransferSize;
} rndis_initialize_msg_t;
/* Response: */
typedef struct {
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_RequestId_t RequestId;
rndis_Status_t Status;
rndis_MajorVersion_t MajorVersion;
rndis_MinorVersion_t MinorVersion;
rndis_DeviceFlags_t DeviceFlags;
rndis_Medium_t Medium;
rndis_MaxPacketsPerTransfer_t MaxPacketsPerTransfer;
rndis_MaxTransferSize_t MaxTransferSize;
rndis_PacketAlignmentFactor_t PacketAlignmentFactor;
rndis_AfListOffset_t AfListOffset;
rndis_AfListSize_t AfListSize;
} rndis_initialize_cmplt_t;
/*** Remote NDIS Halt Message ***/
typedef struct {
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_RequestId_t RequestId;
} rndis_halt_msg_t;
typedef uint32_t rndis_Oid_t;
typedef uint32_t rndis_InformationBufferLength_t;
typedef uint32_t rndis_InformationBufferOffset_t;
typedef uint32_t rndis_DeviceVcHandle_t;
/*** Remote NDIS Query Message ***/
typedef struct {
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_RequestId_t RequestId;
rndis_Oid_t Oid;
rndis_InformationBufferLength_t InformationBufferLength;
rndis_InformationBufferOffset_t InformationBufferOffset;
rndis_DeviceVcHandle_t DeviceVcHandle;
} rndis_query_msg_t;
/* Response: */
typedef struct {
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_RequestId_t RequestId;
rndis_Status_t Status;
rndis_InformationBufferLength_t InformationBufferLength;
rndis_InformationBufferOffset_t InformationBufferOffset;
} rndis_query_cmplt_t;
/*** Remote NDIS Set Message ***/
typedef struct {
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_RequestId_t RequestId;
rndis_Oid_t Oid;
rndis_InformationBufferLength_t InformationBufferLength;
rndis_InformationBufferOffset_t InformationBufferOffset;
rndis_DeviceVcHandle_t DeviceVcHandle;
} rndis_set_msg_t;
/* Response */
typedef struct {
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_RequestId_t RequestId;
rndis_Status_t Status;
} rndis_set_cmplt_t;
/* Information buffer layout for OID_GEN_RNDIS_CONFIG_PARAMETER */
typedef uint32_t rndis_ParameterNameOffset_t;
typedef uint32_t rndis_ParameterNameLength_t;
typedef uint32_t rndis_ParameterType_t;
typedef uint32_t rndis_ParameterValueOffset_t;
typedef uint32_t rndis_ParameterValueLength_t;
#define PARAMETER_TYPE_STRING 2
#define PARAMETER_TYPE_NUMERICAL 0
typedef struct {
rndis_ParameterNameOffset_t ParameterNameOffset;
rndis_ParameterNameLength_t ParameterNameLength;
rndis_ParameterType_t ParameterType;
rndis_ParameterValueOffset_t ParameterValueOffset;
rndis_ParameterValueLength_t ParameterValueLength;
} rndis_config_parameter_t;
typedef uint32_t rndis_Reserved_t;
/*** Remote NDIS Soft Reset Message ***/
typedef struct {
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_Reserved_t Reserved;
} rndis_reset_msg_t;
typedef uint32_t rndis_AddressingReset_t;
/* Response: */
typedef struct {
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_Status_t Status;
rndis_AddressingReset_t AddressingReset;
} rndis_reset_cmplt_t;
/*** Remote NDIS Indicate Status Message ***/
typedef struct {
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_Status_t Status;
rndis_Status_t StatusBufferLength;
rndis_Status_t StatusBufferOffset;
} rndis_indicate_status_t;
typedef uint32_t rndis_DiagStatus_t;
typedef uint32_t rndis_ErrorOffset_t;
typedef struct {
rndis_DiagStatus_t DiagStatus;
rndis_ErrorOffset_t ErrorOffset;
} rndis_diagnostic_info_t;
/*** Remote NDIS Keepalive Message */
typedef struct {
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_RequestId_t RequestId;
} rndis_keepalive_msg_t;
/* Response: */
typedef struct {
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_RequestId_t RequestId;
rndis_Status_t Status;
} rndis_keepalive_cmplt_t;
/*** Remote NDIS Data Packet ***/
typedef uint32_t rndis_DataOffset_t;
typedef uint32_t rndis_DataLength_t;
typedef uint32_t rndis_OOBDataOffset_t;
typedef uint32_t rndis_OOBDataLength_t;
typedef uint32_t rndis_NumOOBDataElements_t;
typedef uint32_t rndis_PerPacketInfoOffset_t;
typedef uint32_t rndis_PerPacketInfoLength_t;
typedef struct {
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_DataOffset_t DataOffset;
rndis_DataLength_t DataLength;
rndis_OOBDataOffset_t OOBDataOffset;
rndis_OOBDataLength_t OOBDataLength;
rndis_NumOOBDataElements_t NumOOBDataElements;
rndis_PerPacketInfoOffset_t PerPacketInfoOffset;
rndis_PerPacketInfoLength_t PerPacketInfoLength;
rndis_DeviceVcHandle_t DeviceVcHandle;
rndis_Reserved_t Reserved;
} rndis_data_packet_t;
typedef uint32_t rndis_ClassInformationOffset_t;
typedef uint32_t rndis_Size_t;
typedef uint32_t rndis_Type_t;
typedef struct {
rndis_Size_t Size;
rndis_Type_t Type;
rndis_ClassInformationOffset_t ClassInformationType;
} rndis_OOB_packet_t;
#include "ndis.h"
typedef enum rnids_state_e {
rndis_uninitialized,
rndis_initialized,
rndis_data_initialized
} rndis_state_t;
typedef struct {
uint32_t txok;
uint32_t rxok;
uint32_t txbad;
uint32_t rxbad;
} usb_eth_stat_t;
#endif /* _RNDIS_H */
/** @} */

View File

@@ -0,0 +1,566 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbd_core.h"
#include "usbd_rndis.h"
#include "rndis_protocol.h"
#define RNDIS_OUT_EP_IDX 0
#define RNDIS_IN_EP_IDX 1
#define RNDIS_INT_EP_IDX 2
/* Describe EndPoints configuration */
static struct usbd_endpoint rndis_ep_data[3];
#define RNDIS_INQUIRY_PUT(src, len) (memcpy(infomation_buffer, src, len))
#define RNDIS_INQUIRY_PUT_LE32(value) (*(uint32_t *)infomation_buffer = (value))
/* Device data structure */
struct usbd_rndis_priv {
uint32_t drv_version;
uint32_t link_status;
uint32_t net_filter;
usb_eth_stat_t eth_state;
rndis_state_t init_state;
uint8_t mac[6];
} g_usbd_rndis;
#if CONFIG_USBDEV_RNDIS_RESP_BUFFER_SIZE < 140
#undef CONFIG_USBDEV_RNDIS_RESP_BUFFER_SIZE
#define CONFIG_USBDEV_RNDIS_RESP_BUFFER_SIZE 156
#endif
#if CONFIG_USBDEV_RNDIS_ETH_MAX_FRAME_SIZE < 1580
#undef CONFIG_USBDEV_RNDIS_ETH_MAX_FRAME_SIZE
#define CONFIG_USBDEV_RNDIS_ETH_MAX_FRAME_SIZE 1580
#endif
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rndis_rx_buffer[CONFIG_USBDEV_RNDIS_ETH_MAX_FRAME_SIZE];
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rndis_tx_buffer[CONFIG_USBDEV_RNDIS_ETH_MAX_FRAME_SIZE];
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t rndis_encapsulated_resp_buffer[CONFIG_USBDEV_RNDIS_RESP_BUFFER_SIZE];
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t NOTIFY_RESPONSE_AVAILABLE[8];
volatile uint8_t *g_rndis_rx_data_buffer;
volatile uint32_t g_rndis_rx_data_length;
volatile uint32_t g_rndis_tx_data_length;
/* RNDIS options list */
const uint32_t oid_supported_list[] = {
/* General OIDs */
OID_GEN_SUPPORTED_LIST,
OID_GEN_HARDWARE_STATUS,
OID_GEN_MEDIA_SUPPORTED,
OID_GEN_MEDIA_IN_USE,
OID_GEN_MAXIMUM_FRAME_SIZE,
OID_GEN_LINK_SPEED,
OID_GEN_TRANSMIT_BLOCK_SIZE,
OID_GEN_RECEIVE_BLOCK_SIZE,
OID_GEN_VENDOR_ID,
OID_GEN_VENDOR_DESCRIPTION,
OID_GEN_VENDOR_DRIVER_VERSION,
OID_GEN_CURRENT_PACKET_FILTER,
OID_GEN_MAXIMUM_TOTAL_SIZE,
OID_GEN_MEDIA_CONNECT_STATUS,
OID_GEN_PHYSICAL_MEDIUM,
/* General Statistic OIDs */
OID_GEN_XMIT_OK,
OID_GEN_RCV_OK,
OID_GEN_XMIT_ERROR,
OID_GEN_RCV_ERROR,
OID_GEN_RCV_NO_BUFFER,
/* Please configure us */
OID_GEN_RNDIS_CONFIG_PARAMETER,
/* 802.3 OIDs */
OID_802_3_PERMANENT_ADDRESS,
OID_802_3_CURRENT_ADDRESS,
OID_802_3_MULTICAST_LIST,
OID_802_3_MAXIMUM_LIST_SIZE,
/* 802.3 Statistic OIDs */
OID_802_3_RCV_ERROR_ALIGNMENT,
OID_802_3_XMIT_ONE_COLLISION,
OID_802_3_XMIT_MORE_COLLISIONS,
OID_802_3_MAC_OPTIONS,
};
static int rndis_encapsulated_cmd_handler(uint8_t *data, uint32_t len);
static void rndis_notify_rsp(void)
{
memset(NOTIFY_RESPONSE_AVAILABLE, 0, 8);
NOTIFY_RESPONSE_AVAILABLE[0] = 0x01;
usbd_ep_start_write(0, rndis_ep_data[RNDIS_INT_EP_IDX].ep_addr, NOTIFY_RESPONSE_AVAILABLE, 8);
}
static int rndis_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
{
switch (setup->bRequest) {
case CDC_REQUEST_SEND_ENCAPSULATED_COMMAND:
rndis_encapsulated_cmd_handler(*data, setup->wLength);
break;
case CDC_REQUEST_GET_ENCAPSULATED_RESPONSE:
*data = rndis_encapsulated_resp_buffer;
*len = ((rndis_generic_msg_t *)rndis_encapsulated_resp_buffer)->MessageLength;
break;
default:
return -1;
}
return 0;
}
static int rndis_init_cmd_handler(uint8_t *data, uint32_t len);
static int rndis_halt_cmd_handler(uint8_t *data, uint32_t len);
static int rndis_query_cmd_handler(uint8_t *data, uint32_t len);
static int rndis_set_cmd_handler(uint8_t *data, uint32_t len);
static int rndis_reset_cmd_handler(uint8_t *data, uint32_t len);
static int rndis_keepalive_cmd_handler(uint8_t *data, uint32_t len);
static int rndis_encapsulated_cmd_handler(uint8_t *data, uint32_t len)
{
switch (((rndis_generic_msg_t *)data)->MessageType) {
case REMOTE_NDIS_INITIALIZE_MSG:
return rndis_init_cmd_handler(data, len);
case REMOTE_NDIS_HALT_MSG:
return rndis_halt_cmd_handler(data, len);
case REMOTE_NDIS_QUERY_MSG:
return rndis_query_cmd_handler(data, len);
case REMOTE_NDIS_SET_MSG:
return rndis_set_cmd_handler(data, len);
case REMOTE_NDIS_RESET_MSG:
return rndis_reset_cmd_handler(data, len);
case REMOTE_NDIS_KEEPALIVE_MSG:
return rndis_keepalive_cmd_handler(data, len);
default:
break;
}
return -1;
}
static int rndis_init_cmd_handler(uint8_t *data, uint32_t len)
{
rndis_initialize_msg_t *cmd = (rndis_initialize_msg_t *)data;
rndis_initialize_cmplt_t *resp;
resp = ((rndis_initialize_cmplt_t *)rndis_encapsulated_resp_buffer);
resp->RequestId = cmd->RequestId;
resp->MessageType = REMOTE_NDIS_INITIALIZE_CMPLT;
resp->MessageLength = sizeof(rndis_initialize_cmplt_t);
resp->MajorVersion = RNDIS_MAJOR_VERSION;
resp->MinorVersion = RNDIS_MINOR_VERSION;
resp->Status = RNDIS_STATUS_SUCCESS;
resp->DeviceFlags = RNDIS_DF_CONNECTIONLESS;
resp->Medium = RNDIS_MEDIUM_802_3;
resp->MaxPacketsPerTransfer = CONFIG_USBDEV_RNDIS_ETH_MAX_FRAME_SIZE / 1580;
resp->MaxTransferSize = CONFIG_USBDEV_RNDIS_ETH_MAX_FRAME_SIZE;
resp->PacketAlignmentFactor = 0;
resp->AfListOffset = 0;
resp->AfListSize = 0;
g_usbd_rndis.init_state = rndis_initialized;
rndis_notify_rsp();
return 0;
}
static int rndis_halt_cmd_handler(uint8_t *data, uint32_t len)
{
rndis_halt_msg_t *resp;
resp = ((rndis_halt_msg_t *)rndis_encapsulated_resp_buffer);
resp->MessageLength = 0;
g_usbd_rndis.init_state = rndis_uninitialized;
return 0;
}
static int rndis_query_cmd_handler(uint8_t *data, uint32_t len)
{
rndis_query_msg_t *cmd = (rndis_query_msg_t *)data;
rndis_query_cmplt_t *resp;
uint8_t *infomation_buffer;
uint32_t infomation_len = 0;
resp = ((rndis_query_cmplt_t *)rndis_encapsulated_resp_buffer);
resp->MessageType = REMOTE_NDIS_QUERY_CMPLT;
resp->RequestId = cmd->RequestId;
resp->InformationBufferOffset = sizeof(rndis_query_cmplt_t) - sizeof(rndis_generic_msg_t);
resp->Status = RNDIS_STATUS_SUCCESS;
infomation_buffer = (uint8_t *)resp + sizeof(rndis_query_cmplt_t);
switch (cmd->Oid) {
case OID_GEN_SUPPORTED_LIST:
RNDIS_INQUIRY_PUT(oid_supported_list, sizeof(oid_supported_list));
infomation_len = sizeof(oid_supported_list);
break;
case OID_GEN_HARDWARE_STATUS:
RNDIS_INQUIRY_PUT_LE32(NDIS_HW_STS_READY);
infomation_len = 4;
break;
case OID_GEN_MEDIA_SUPPORTED:
case OID_GEN_MEDIA_IN_USE:
RNDIS_INQUIRY_PUT_LE32(NDIS_MEDIUM_802_3);
infomation_len = 4;
break;
case OID_GEN_MAXIMUM_FRAME_SIZE:
case OID_GEN_TRANSMIT_BLOCK_SIZE:
case OID_GEN_RECEIVE_BLOCK_SIZE:
RNDIS_INQUIRY_PUT_LE32(0x05DC); /* mtu 1500 */
infomation_len = 4;
break;
case OID_GEN_VENDOR_ID:
RNDIS_INQUIRY_PUT_LE32(CONFIG_USBDEV_RNDIS_VENDOR_ID);
infomation_len = 4;
break;
case OID_GEN_VENDOR_DRIVER_VERSION:
RNDIS_INQUIRY_PUT_LE32(0x0001);
infomation_len = 4;
break;
case OID_GEN_VENDOR_DESCRIPTION:
RNDIS_INQUIRY_PUT(CONFIG_USBDEV_RNDIS_VENDOR_DESC, strlen(CONFIG_USBDEV_RNDIS_VENDOR_DESC));
infomation_len = (strlen(CONFIG_USBDEV_RNDIS_VENDOR_DESC) + 1);
break;
case OID_802_3_CURRENT_ADDRESS:
case OID_802_3_PERMANENT_ADDRESS:
RNDIS_INQUIRY_PUT(g_usbd_rndis.mac, 6);
infomation_len = 6;
break;
case OID_GEN_PHYSICAL_MEDIUM:
RNDIS_INQUIRY_PUT_LE32(NDIS_MEDIUM_802_3);
infomation_len = 4;
break;
case OID_GEN_LINK_SPEED:
if (usbd_get_ep_mps(0, rndis_ep_data[RNDIS_OUT_EP_IDX].ep_addr) > 64) {
RNDIS_INQUIRY_PUT_LE32(480000000 / 100);
} else {
RNDIS_INQUIRY_PUT_LE32(12000000 / 100);
}
infomation_len = 4;
break;
case OID_GEN_CURRENT_PACKET_FILTER:
RNDIS_INQUIRY_PUT_LE32(g_usbd_rndis.net_filter);
infomation_len = 4;
break;
case OID_GEN_MAXIMUM_TOTAL_SIZE:
RNDIS_INQUIRY_PUT_LE32(0x0616); /* 1514 + 44 */
infomation_len = 4;
break;
case OID_GEN_MEDIA_CONNECT_STATUS:
RNDIS_INQUIRY_PUT_LE32(g_usbd_rndis.link_status);
infomation_len = 4;
break;
case OID_GEN_RNDIS_CONFIG_PARAMETER:
RNDIS_INQUIRY_PUT_LE32(0);
infomation_len = 4;
break;
case OID_802_3_MAXIMUM_LIST_SIZE:
RNDIS_INQUIRY_PUT_LE32(1); /* one address */
infomation_len = 4;
break;
case OID_802_3_MULTICAST_LIST:
//RNDIS_INQUIRY_PUT_LE32(0xE0000000); /* 224.0.0.0 */
resp->Status = RNDIS_STATUS_NOT_SUPPORTED;
RNDIS_INQUIRY_PUT_LE32(0);
infomation_len = 4;
break;
case OID_802_3_MAC_OPTIONS:
// infomation_len = 0;
resp->Status = RNDIS_STATUS_NOT_SUPPORTED;
RNDIS_INQUIRY_PUT_LE32(0);
infomation_len = 4;
break;
case OID_GEN_MAC_OPTIONS:
RNDIS_INQUIRY_PUT_LE32(0);
infomation_len = 4;
break;
case OID_802_3_RCV_ERROR_ALIGNMENT:
RNDIS_INQUIRY_PUT_LE32(0);
infomation_len = 4;
break;
case OID_802_3_XMIT_ONE_COLLISION:
RNDIS_INQUIRY_PUT_LE32(0);
infomation_len = 4;
break;
case OID_802_3_XMIT_MORE_COLLISIONS:
RNDIS_INQUIRY_PUT_LE32(0);
infomation_len = 4;
break;
case OID_GEN_XMIT_OK:
RNDIS_INQUIRY_PUT_LE32(g_usbd_rndis.eth_state.txok);
infomation_len = 4;
break;
case OID_GEN_RCV_OK:
RNDIS_INQUIRY_PUT_LE32(g_usbd_rndis.eth_state.rxok);
infomation_len = 4;
break;
case OID_GEN_RCV_ERROR:
RNDIS_INQUIRY_PUT_LE32(g_usbd_rndis.eth_state.rxbad);
infomation_len = 4;
break;
case OID_GEN_XMIT_ERROR:
RNDIS_INQUIRY_PUT_LE32(g_usbd_rndis.eth_state.txbad);
infomation_len = 4;
break;
case OID_GEN_RCV_NO_BUFFER:
RNDIS_INQUIRY_PUT_LE32(0);
infomation_len = 4;
break;
default:
resp->Status = RNDIS_STATUS_FAILURE;
infomation_len = 0;
USB_LOG_WRN("Unhandled query for Object ID 0x%x\r\n", cmd->Oid);
break;
}
resp->MessageLength = sizeof(rndis_query_cmplt_t) + infomation_len;
resp->InformationBufferLength = infomation_len;
rndis_notify_rsp();
return 0;
}
static int rndis_set_cmd_handler(uint8_t *data, uint32_t len)
{
rndis_set_msg_t *cmd = (rndis_set_msg_t *)data;
rndis_set_cmplt_t *resp;
rndis_config_parameter_t *param;
resp = ((rndis_set_cmplt_t *)rndis_encapsulated_resp_buffer);
resp->RequestId = cmd->RequestId;
resp->MessageType = REMOTE_NDIS_SET_CMPLT;
resp->MessageLength = sizeof(rndis_set_cmplt_t);
resp->Status = RNDIS_STATUS_SUCCESS;
switch (cmd->Oid) {
case OID_GEN_RNDIS_CONFIG_PARAMETER:
param = (rndis_config_parameter_t *)((uint8_t *)&(cmd->RequestId) + cmd->InformationBufferOffset);
USB_LOG_WRN("RNDIS cfg param: NameOfs=%d, NameLen=%d, ValueOfs=%d, ValueLen=%d\r\n",
param->ParameterNameOffset, param->ParameterNameLength,
param->ParameterValueOffset, param->ParameterValueLength);
break;
case OID_GEN_CURRENT_PACKET_FILTER:
if (cmd->InformationBufferLength < sizeof(g_usbd_rndis.net_filter)) {
USB_LOG_WRN("PACKET_FILTER!\r\n");
resp->Status = RNDIS_STATUS_INVALID_DATA;
} else {
uint32_t *filter;
/* Parameter starts at offset buf_offset of the req_id field */
filter = (uint32_t *)((uint8_t *)&(cmd->RequestId) + cmd->InformationBufferOffset);
//g_usbd_rndis.net_filter = param->ParameterNameOffset;
g_usbd_rndis.net_filter = *(uint32_t *)filter;
if (g_usbd_rndis.net_filter) {
g_usbd_rndis.init_state = rndis_data_initialized;
} else {
g_usbd_rndis.init_state = rndis_initialized;
}
}
break;
case OID_GEN_CURRENT_LOOKAHEAD:
break;
case OID_GEN_PROTOCOL_OPTIONS:
break;
case OID_802_3_MULTICAST_LIST:
break;
case OID_PNP_ADD_WAKE_UP_PATTERN:
case OID_PNP_REMOVE_WAKE_UP_PATTERN:
case OID_PNP_ENABLE_WAKE_UP:
default:
resp->Status = RNDIS_STATUS_FAILURE;
USB_LOG_WRN("Unhandled query for Object ID 0x%x\r\n", cmd->Oid);
break;
}
rndis_notify_rsp();
return 0;
}
static int rndis_reset_cmd_handler(uint8_t *data, uint32_t len)
{
// rndis_reset_msg_t *cmd = (rndis_reset_msg_t *)data;
rndis_reset_cmplt_t *resp;
resp = ((rndis_reset_cmplt_t *)rndis_encapsulated_resp_buffer);
resp->MessageType = REMOTE_NDIS_RESET_CMPLT;
resp->MessageLength = sizeof(rndis_reset_cmplt_t);
resp->Status = RNDIS_STATUS_SUCCESS;
resp->AddressingReset = 1;
g_usbd_rndis.init_state = rndis_uninitialized;
rndis_notify_rsp();
return 0;
}
static int rndis_keepalive_cmd_handler(uint8_t *data, uint32_t len)
{
rndis_keepalive_msg_t *cmd = (rndis_keepalive_msg_t *)data;
rndis_keepalive_cmplt_t *resp;
resp = ((rndis_keepalive_cmplt_t *)rndis_encapsulated_resp_buffer);
resp->RequestId = cmd->RequestId;
resp->MessageType = REMOTE_NDIS_KEEPALIVE_CMPLT;
resp->MessageLength = sizeof(rndis_keepalive_cmplt_t);
resp->Status = RNDIS_STATUS_SUCCESS;
rndis_notify_rsp();
return 0;
}
static void rndis_notify_handler(uint8_t busid, uint8_t event, void *arg)
{
switch (event) {
case USBD_EVENT_RESET:
g_usbd_rndis.link_status = NDIS_MEDIA_STATE_DISCONNECTED;
break;
case USBD_EVENT_CONFIGURED:
g_rndis_rx_data_length = 0;
g_rndis_tx_data_length = 0;
g_usbd_rndis.link_status = NDIS_MEDIA_STATE_CONNECTED;
usbd_ep_start_read(0, rndis_ep_data[RNDIS_OUT_EP_IDX].ep_addr, g_rndis_rx_buffer, sizeof(g_rndis_rx_buffer));
break;
default:
break;
}
}
void rndis_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
rndis_data_packet_t *hdr;
hdr = (rndis_data_packet_t *)g_rndis_rx_buffer;
g_rndis_rx_data_buffer = g_rndis_rx_buffer;
if ((hdr->MessageType != REMOTE_NDIS_PACKET_MSG) || (nbytes < hdr->MessageLength)) {
usbd_ep_start_read(0, rndis_ep_data[RNDIS_OUT_EP_IDX].ep_addr, g_rndis_rx_buffer, sizeof(g_rndis_rx_buffer));
return;
}
/* Point to the payload and update the message length */
g_rndis_rx_data_buffer += hdr->DataOffset + sizeof(rndis_generic_msg_t);
g_rndis_rx_data_length = hdr->DataLength;
usbd_rndis_data_recv_done();
}
void rndis_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
if ((nbytes % usbd_get_ep_mps(busid, ep)) == 0 && nbytes) {
/* send zlp */
usbd_ep_start_write(0, ep, NULL, 0);
} else {
g_rndis_tx_data_length = 0;
}
}
void rndis_int_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
//USB_LOG_DBG("len:%d\r\n", nbytes);
}
#ifdef CONFIG_USBDEV_RNDIS_USING_LWIP
#include <lwip/pbuf.h>
struct pbuf *usbd_rndis_eth_rx(void)
{
struct pbuf *p;
if (g_rndis_rx_data_length == 0) {
return NULL;
}
p = pbuf_alloc(PBUF_RAW, g_rndis_rx_data_length, PBUF_POOL);
if (p == NULL) {
return NULL;
}
usb_memcpy(p->payload, (uint8_t *)g_rndis_rx_data_buffer, g_rndis_rx_data_length);
p->len = g_rndis_rx_data_length;
USB_LOG_DBG("rxlen:%d\r\n", g_rndis_rx_data_length);
g_rndis_rx_data_length = 0;
usbd_ep_start_read(0, rndis_ep_data[RNDIS_OUT_EP_IDX].ep_addr, g_rndis_rx_buffer, sizeof(g_rndis_rx_buffer));
return p;
}
int usbd_rndis_eth_tx(struct pbuf *p)
{
struct pbuf *q;
uint8_t *buffer;
rndis_data_packet_t *hdr;
if (g_usbd_rndis.link_status == NDIS_MEDIA_STATE_DISCONNECTED) {
return -USB_ERR_NOTCONN;
}
if (g_rndis_tx_data_length > 0) {
return -USB_ERR_BUSY;
}
if (p->tot_len > sizeof(g_rndis_tx_buffer)) {
p->tot_len = sizeof(g_rndis_tx_buffer);
}
buffer = (uint8_t *)(g_rndis_tx_buffer + sizeof(rndis_data_packet_t));
for (q = p; q != NULL; q = q->next) {
usb_memcpy(buffer, q->payload, q->len);
buffer += q->len;
}
hdr = (rndis_data_packet_t *)g_rndis_tx_buffer;
memset(hdr, 0, sizeof(rndis_data_packet_t));
hdr->MessageType = REMOTE_NDIS_PACKET_MSG;
hdr->MessageLength = sizeof(rndis_data_packet_t) + p->tot_len;
hdr->DataOffset = sizeof(rndis_data_packet_t) - sizeof(rndis_generic_msg_t);
hdr->DataLength = p->tot_len;
g_rndis_tx_data_length = sizeof(rndis_data_packet_t) + p->tot_len;
USB_LOG_DBG("txlen:%d\r\n", g_rndis_tx_data_length);
return usbd_ep_start_write(0, rndis_ep_data[RNDIS_IN_EP_IDX].ep_addr, g_rndis_tx_buffer, g_rndis_tx_data_length);
}
#endif
struct usbd_interface *usbd_rndis_init_intf(struct usbd_interface *intf,
const uint8_t out_ep,
const uint8_t in_ep,
const uint8_t int_ep, uint8_t mac[6])
{
memcpy(g_usbd_rndis.mac, mac, 6);
g_usbd_rndis.drv_version = 0x0001;
g_usbd_rndis.link_status = NDIS_MEDIA_STATE_DISCONNECTED;
rndis_ep_data[RNDIS_OUT_EP_IDX].ep_addr = out_ep;
rndis_ep_data[RNDIS_OUT_EP_IDX].ep_cb = rndis_bulk_out;
rndis_ep_data[RNDIS_IN_EP_IDX].ep_addr = in_ep;
rndis_ep_data[RNDIS_IN_EP_IDX].ep_cb = rndis_bulk_in;
rndis_ep_data[RNDIS_INT_EP_IDX].ep_addr = int_ep;
rndis_ep_data[RNDIS_INT_EP_IDX].ep_cb = rndis_int_in;
usbd_add_endpoint(0, &rndis_ep_data[RNDIS_OUT_EP_IDX]);
usbd_add_endpoint(0, &rndis_ep_data[RNDIS_IN_EP_IDX]);
usbd_add_endpoint(0, &rndis_ep_data[RNDIS_INT_EP_IDX]);
intf->class_interface_handler = rndis_class_interface_request_handler;
intf->class_endpoint_handler = NULL;
intf->vendor_handler = NULL;
intf->notify_handler = rndis_notify_handler;
return intf;
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBD_RNDIS_H
#define USBD_RNDIS_H
#include "usb_cdc.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Init rndis interface driver */
struct usbd_interface *usbd_rndis_init_intf(struct usbd_interface *intf,
const uint8_t out_ep,
const uint8_t in_ep,
const uint8_t int_ep, uint8_t mac[6]);
void usbd_rndis_data_recv_done(void);
#ifdef CONFIG_USBDEV_RNDIS_USING_LWIP
struct pbuf *usbd_rndis_eth_rx(void);
int usbd_rndis_eth_tx(struct pbuf *p);
#endif
#ifdef __cplusplus
}
#endif
#endif /* USBD_RNDIS_H */

View File

@@ -0,0 +1,407 @@
/*
* Copyright (c) 2024, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbh_core.h"
#include "usbh_bluetooth.h"
#undef USB_DBG_TAG
#define USB_DBG_TAG "usbh_bluetooth"
#include "usb_log.h"
#define DEV_FORMAT "/dev/bluetooth"
static struct usbh_bluetooth g_bluetooth_class;
#ifdef CONFIG_USBHOST_BLUETOOTH_HCI_H4
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_bluetooth_tx_buf[1 + CONFIG_USBHOST_BLUETOOTH_TX_SIZE];
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_bluetooth_rx_buf[1 + CONFIG_USBHOST_BLUETOOTH_RX_SIZE];
#else
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_bluetooth_cmd_buf[1 + 256];
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_bluetooth_evt_buf[1 + 256];
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_bluetooth_tx_buf[1 + CONFIG_USBHOST_BLUETOOTH_TX_SIZE];
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_bluetooth_rx_buf[1 + CONFIG_USBHOST_BLUETOOTH_RX_SIZE];
#endif
static int usbh_bluetooth_connect(struct usbh_hubport *hport, uint8_t intf)
{
struct usb_endpoint_descriptor *ep_desc;
int ret = 0;
#ifndef CONFIG_USBHOST_BLUETOOTH_HCI_H4
uint8_t mult;
uint16_t mps;
#endif
struct usbh_bluetooth *bluetooth_class = &g_bluetooth_class;
#ifndef CONFIG_USBHOST_BLUETOOTH_HCI_H4
if (intf != 0) {
return 0;
}
#endif
memset(bluetooth_class, 0, sizeof(struct usbh_bluetooth));
bluetooth_class->hport = hport;
bluetooth_class->intf = intf;
#ifndef CONFIG_USBHOST_BLUETOOTH_HCI_H4
bluetooth_class->num_of_intf_altsettings = hport->config.intf[intf + 1].altsetting_num;
#endif
hport->config.intf[intf].priv = bluetooth_class;
for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
#ifndef CONFIG_USBHOST_BLUETOOTH_HCI_H4
if (USB_GET_ENDPOINT_TYPE(ep_desc->bmAttributes) == USB_ENDPOINT_TYPE_INTERRUPT) {
if (ep_desc->bEndpointAddress & 0x80) {
USBH_EP_INIT(bluetooth_class->intin, ep_desc);
} else {
return -USB_ERR_NOTSUPP;
}
} else {
#endif
if (ep_desc->bEndpointAddress & 0x80) {
USBH_EP_INIT(bluetooth_class->bulkin, ep_desc);
} else {
USBH_EP_INIT(bluetooth_class->bulkout, ep_desc);
}
#ifndef CONFIG_USBHOST_BLUETOOTH_HCI_H4
}
#endif
}
#ifndef CONFIG_USBHOST_BLUETOOTH_HCI_H4
USB_LOG_INFO("Num of altsettings:%u\r\n", bluetooth_class->num_of_intf_altsettings);
for (uint8_t i = 0; i < bluetooth_class->num_of_intf_altsettings; i++) {
USB_LOG_INFO("Altsetting:%u\r\n", i);
for (uint8_t j = 0; j < hport->config.intf[intf + 1].altsetting[i].intf_desc.bNumEndpoints; j++) {
ep_desc = &bluetooth_class->hport->config.intf[intf + 1].altsetting[i].ep[j].ep_desc;
mult = USB_GET_MULT(ep_desc->wMaxPacketSize);
mps = USB_GET_MAXPACKETSIZE(ep_desc->wMaxPacketSize);
USB_LOG_INFO("\tEp=%02x Attr=%02u Mps=%d Interval=%02u Mult=%02u\r\n",
ep_desc->bEndpointAddress,
ep_desc->bmAttributes,
mps,
ep_desc->bInterval,
mult);
}
}
ret = usbh_set_interface(hport, intf, 0);
if (ret < 0) {
return ret;
}
USB_LOG_INFO("Bluetooth select altsetting 0\r\n");
#endif
strncpy(hport->config.intf[intf].devname, DEV_FORMAT, CONFIG_USBHOST_DEV_NAMELEN);
USB_LOG_INFO("Register Bluetooth Class:%s\r\n", hport->config.intf[intf].devname);
usbh_bluetooth_run(bluetooth_class);
return ret;
}
static int usbh_bluetooth_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
int ret = 0;
struct usbh_bluetooth *bluetooth_class = (struct usbh_bluetooth *)hport->config.intf[intf].priv;
if (hport->config.config_desc.bNumInterfaces == (intf + 1)) {
return 0;
}
if (bluetooth_class) {
if (bluetooth_class->bulkin) {
usbh_kill_urb(&bluetooth_class->bulkin_urb);
}
if (bluetooth_class->bulkout) {
usbh_kill_urb(&bluetooth_class->bulkout_urb);
}
#ifndef CONFIG_USBHOST_BLUETOOTH_HCI_H4
if (bluetooth_class->intin) {
usbh_kill_urb(&bluetooth_class->intin_urb);
}
// if (bluetooth_class->isoin) {
// usbh_kill_urb(&bluetooth_class->isoin_urb);
// }
// if (bluetooth_class->isoin) {
// usbh_kill_urb(&bluetooth_class->isoinin_urb);
// }
#endif
if (hport->config.intf[intf].devname[0] != '\0') {
USB_LOG_INFO("Unregister Bluetooth Class:%s\r\n", hport->config.intf[intf].devname);
usbh_bluetooth_stop(bluetooth_class);
}
memset(bluetooth_class, 0, sizeof(struct usbh_bluetooth));
}
return ret;
}
#ifdef CONFIG_USBHOST_BLUETOOTH_HCI_LOG
static void usbh_bluetooth_hci_dump(uint8_t *data, uint32_t len)
{
uint32_t i = 0;
for (i = 0; i < len; i++) {
if (i % 16 == 0) {
USB_LOG_RAW("\r\n");
}
USB_LOG_RAW("%02x ", data[i]);
}
USB_LOG_RAW("\r\n");
}
#else
#define usbh_bluetooth_hci_dump(data, len)
#endif
static int usbh_bluetooth_hci_bulk_out(uint8_t *buffer, uint32_t buflen)
{
struct usbh_bluetooth *bluetooth_class = &g_bluetooth_class;
struct usbh_urb *urb = &bluetooth_class->bulkout_urb;
int ret;
usbh_bulk_urb_fill(urb, bluetooth_class->hport, bluetooth_class->bulkout, buffer, buflen, USB_OSAL_WAITING_FOREVER, NULL, NULL);
ret = usbh_submit_urb(urb);
if (ret == 0) {
ret = urb->actual_length;
}
return ret;
}
#ifdef CONFIG_USBHOST_BLUETOOTH_HCI_H4
int usbh_bluetooth_hci_write(uint8_t hci_type, uint8_t *buffer, uint32_t buflen)
{
int ret;
g_bluetooth_tx_buf[0] = hci_type;
memcpy(&g_bluetooth_tx_buf[1], buffer, buflen);
usbh_bluetooth_hci_dump(g_bluetooth_tx_buf, buflen + 1);
ret = usbh_bluetooth_hci_bulk_out(g_bluetooth_tx_buf, buflen + 1);
return ret;
}
void usbh_bluetooth_hci_rx_thread(void *argument)
{
int ret;
uint32_t ep_mps;
uint8_t retry = 0;
uint16_t actual_len = 0;
ep_mps = USB_GET_MAXPACKETSIZE(g_bluetooth_class.bulkin->wMaxPacketSize);
USB_LOG_INFO("Create hc rx thread\r\n");
while (1) {
usbh_bulk_urb_fill(&g_bluetooth_class.bulkin_urb, g_bluetooth_class.hport, g_bluetooth_class.bulkin, &g_bluetooth_rx_buf[actual_len], ep_mps, USB_OSAL_WAITING_FOREVER, NULL, NULL);
ret = usbh_submit_urb(&g_bluetooth_class.bulkin_urb);
if (ret < 0) {
if (ret == -USB_ERR_SHUTDOWN) {
goto delete;
} else {
retry++;
if (retry == 3) {
retry = 0;
goto delete;
}
continue;
}
}
actual_len += g_bluetooth_class.bulkin_urb.actual_length;
if (g_bluetooth_class.bulkin_urb.actual_length != ep_mps) {
usbh_bluetooth_hci_dump(g_bluetooth_rx_buf, actual_len);
usbh_bluetooth_hci_read_callback(g_bluetooth_rx_buf, actual_len);
actual_len = 0;
} else {
/* read continue util read short packet */
}
}
// clang-format off
delete :
USB_LOG_INFO("Delete hc acl rx thread\r\n");
usb_osal_thread_delete(NULL);
// clang-format on
}
#else
static int usbh_bluetooth_hci_cmd(uint8_t *buffer, uint32_t buflen)
{
struct usbh_bluetooth *bluetooth_class = &g_bluetooth_class;
struct usb_setup_packet *setup;
if (!bluetooth_class || !bluetooth_class->hport) {
return -USB_ERR_INVAL;
}
setup = bluetooth_class->hport->setup;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_DEVICE;
setup->bRequest = 0x00;
setup->wValue = 0;
setup->wIndex = bluetooth_class->intf;
setup->wLength = buflen;
return usbh_control_transfer(bluetooth_class->hport, setup, buffer);
}
int usbh_bluetooth_hci_write(uint8_t hci_type, uint8_t *buffer, uint32_t buflen)
{
int ret;
if (hci_type == USB_BLUETOOTH_HCI_CMD) {
g_bluetooth_cmd_buf[0] = USB_BLUETOOTH_HCI_CMD;
memcpy(&g_bluetooth_cmd_buf[1], buffer, buflen);
usbh_bluetooth_hci_dump(g_bluetooth_cmd_buf, buflen + 1);
ret = usbh_bluetooth_hci_cmd(&g_bluetooth_cmd_buf[1], buflen);
} else if (hci_type == USB_BLUETOOTH_HCI_ACL) {
g_bluetooth_tx_buf[0] = USB_BLUETOOTH_HCI_ACL;
memcpy(&g_bluetooth_tx_buf[1], buffer, buflen);
usbh_bluetooth_hci_dump(g_bluetooth_tx_buf, buflen + 1);
ret = usbh_bluetooth_hci_bulk_out(&g_bluetooth_tx_buf[1], buflen);
} else {
ret = -1;
}
return ret;
}
void usbh_bluetooth_hci_evt_rx_thread(void *argument)
{
int ret;
uint32_t ep_mps;
uint32_t interval;
uint8_t retry = 0;
uint16_t actual_len = 0;
ep_mps = USB_GET_MAXPACKETSIZE(g_bluetooth_class.intin->wMaxPacketSize);
interval = g_bluetooth_class.intin->bInterval;
USB_LOG_INFO("Create hc event rx thread\r\n");
while (1) {
usbh_int_urb_fill(&g_bluetooth_class.intin_urb, g_bluetooth_class.hport, g_bluetooth_class.intin, &g_bluetooth_evt_buf[1 + actual_len], ep_mps, USB_OSAL_WAITING_FOREVER, NULL, NULL);
ret = usbh_submit_urb(&g_bluetooth_class.intin_urb);
if (ret < 0) {
if (ret == -USB_ERR_SHUTDOWN) {
goto delete;
} else if (ret == -USB_ERR_NAK) {
usb_osal_msleep(interval);
continue;
} else {
retry++;
if (retry == 3) {
retry = 0;
goto delete;
}
usb_osal_msleep(interval);
continue;
}
}
actual_len += g_bluetooth_class.intin_urb.actual_length;
if (g_bluetooth_class.intin_urb.actual_length != ep_mps) {
g_bluetooth_evt_buf[0] = USB_BLUETOOTH_HCI_EVT;
usbh_bluetooth_hci_dump(g_bluetooth_evt_buf, actual_len + 1);
usbh_bluetooth_hci_read_callback(g_bluetooth_evt_buf, actual_len + 1);
actual_len = 0;
} else {
/* read continue util read short packet */
}
usb_osal_msleep(interval);
}
// clang-format off
delete :
USB_LOG_INFO("Delete hc event rx thread\r\n");
usb_osal_thread_delete(NULL);
// clang-format on
}
void usbh_bluetooth_hci_acl_rx_thread(void *argument)
{
int ret;
uint32_t ep_mps;
uint8_t retry = 0;
uint16_t actual_len = 0;
ep_mps = USB_GET_MAXPACKETSIZE(g_bluetooth_class.bulkin->wMaxPacketSize);
USB_LOG_INFO("Create hc acl rx thread\r\n");
while (1) {
usbh_bulk_urb_fill(&g_bluetooth_class.bulkin_urb, g_bluetooth_class.hport, g_bluetooth_class.bulkin, &g_bluetooth_rx_buf[1 + actual_len], ep_mps, USB_OSAL_WAITING_FOREVER, NULL, NULL);
ret = usbh_submit_urb(&g_bluetooth_class.bulkin_urb);
if (ret < 0) {
if (ret == -USB_ERR_SHUTDOWN) {
goto delete;
} else {
retry++;
if (retry == 3) {
retry = 0;
goto delete;
}
continue;
}
}
actual_len += g_bluetooth_class.bulkin_urb.actual_length;
if (g_bluetooth_class.bulkin_urb.actual_length != ep_mps) {
g_bluetooth_rx_buf[0] = USB_BLUETOOTH_HCI_ACL;
usbh_bluetooth_hci_dump(g_bluetooth_rx_buf, actual_len + 1);
usbh_bluetooth_hci_read_callback(g_bluetooth_rx_buf, actual_len + 1);
actual_len = 0;
} else {
/* read continue util read short packet */
}
}
// clang-format off
delete :
USB_LOG_INFO("Delete hc acl rx thread\r\n");
usb_osal_thread_delete(NULL);
// clang-format on
}
#endif
__WEAK void usbh_bluetooth_hci_read_callback(uint8_t *data, uint32_t len)
{
}
__WEAK void usbh_bluetooth_run(struct usbh_bluetooth *bluetooth_class)
{
}
__WEAK void usbh_bluetooth_stop(struct usbh_bluetooth *bluetooth_class)
{
}
static const struct usbh_class_driver bluetooth_class_driver = {
.driver_name = "bluetooth",
.connect = usbh_bluetooth_connect,
.disconnect = usbh_bluetooth_disconnect
};
#ifdef CONFIG_USBHOST_BLUETOOTH_HCI_H4
static const uint16_t bluetooth_id_table[][2] = {
{ 0x2fe3, 0x000c },
{ 0, 0 },
};
CLASS_INFO_DEFINE const struct usbh_class_info bluetooth_h4_nrf_class_info = {
.match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
.class = 0xff,
.subclass = 0x00,
.protocol = 0x00,
.id_table = bluetooth_id_table,
.class_driver = &bluetooth_class_driver
};
#else
CLASS_INFO_DEFINE const struct usbh_class_info bluetooth_class_info = {
.match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
.class = USB_DEVICE_CLASS_WIRELESS,
.subclass = 0x01,
.protocol = 0x01,
.id_table = NULL,
.class_driver = &bluetooth_class_driver
};
#endif

View File

@@ -0,0 +1,56 @@
/*
* Copyright (c) 2024, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_BLUETOOTH_H
#define USBH_BLUETOOTH_H
#define USB_BLUETOOTH_HCI_NONE 0x00
#define USB_BLUETOOTH_HCI_CMD 0x01
#define USB_BLUETOOTH_HCI_ACL 0x02
#define USB_BLUETOOTH_HCI_SCO 0x03
#define USB_BLUETOOTH_HCI_EVT 0x04
#define USB_BLUETOOTH_HCI_ISO 0x05
struct usbh_bluetooth {
struct usbh_hubport *hport;
uint8_t intf;
struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
struct usbh_urb bulkin_urb; /* Bulk IN urb */
struct usbh_urb bulkout_urb; /* Bulk OUT urb */
#ifndef CONFIG_USBHOST_BLUETOOTH_HCI_H4
struct usb_endpoint_descriptor *intin; /* INTR endpoint */
struct usb_endpoint_descriptor *isoin; /* Bulk IN endpoint */
struct usb_endpoint_descriptor *isoout; /* Bulk OUT endpoint */
struct usbh_urb intin_urb; /* INTR IN urb */
struct usbh_urb *isoin_urb; /* Bulk IN urb */
struct usbh_urb *isoout_urb; /* Bulk OUT urb */
uint8_t num_of_intf_altsettings;
#endif
void *user_data;
};
#ifdef __cplusplus
extern "C" {
#endif
int usbh_bluetooth_hci_write(uint8_t hci_type, uint8_t *buffer, uint32_t buflen);
void usbh_bluetooth_hci_read_callback(uint8_t *data, uint32_t len);
#ifdef CONFIG_USBHOST_BLUETOOTH_HCI_H4
void usbh_bluetooth_hci_rx_thread(void *argument);
#else
void usbh_bluetooth_hci_evt_rx_thread(void *argument);
void usbh_bluetooth_hci_acl_rx_thread(void *argument);
#endif
void usbh_bluetooth_run(struct usbh_bluetooth *bluetooth_class);
void usbh_bluetooth_stop(struct usbh_bluetooth *bluetooth_class);
#ifdef __cplusplus
}
#endif
#endif /* USBH_BLUETOOTH_H */

View File

@@ -0,0 +1,603 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbh_core.h"
#include "usbh_rndis.h"
#include "rndis_protocol.h"
#undef USB_DBG_TAG
#define USB_DBG_TAG "usbh_rndis"
#include "usb_log.h"
#define DEV_FORMAT "/dev/rndis"
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rndis_buf[4096];
#define CONFIG_USBHOST_RNDIS_ETH_MAX_FRAME_SIZE 1514
#define CONFIG_USBHOST_RNDIS_ETH_MSG_SIZE (CONFIG_USBHOST_RNDIS_ETH_MAX_FRAME_SIZE + 44)
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rndis_rx_buffer[CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE];
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rndis_tx_buffer[CONFIG_USBHOST_RNDIS_ETH_MAX_TX_SIZE];
// static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rndis_inttx_buffer[16];
static struct usbh_rndis g_rndis_class;
static int usbh_rndis_get_notification(struct usbh_rndis *rndis_class)
{
// int ret;
// struct usbh_urb *urb = &rndis_class->intin_urb;
// usbh_int_urb_fill(urb, rndis_class->hport, rndis_class->intin, g_rndis_inttx_buffer, rndis_class->intin->wMaxPacketSize, USB_OSAL_WAITING_FOREVER, NULL, NULL);
// ret = usbh_submit_urb(urb);
// if (ret == 0) {
// ret = urb->actual_length;
// }
// return ret;
return 0;
}
static int usbh_rndis_init_msg_transfer(struct usbh_rndis *rndis_class)
{
struct usb_setup_packet *setup;
int ret = 0;
rndis_initialize_msg_t *cmd;
rndis_initialize_cmplt_t *resp;
if (!rndis_class || !rndis_class->hport) {
return -USB_ERR_INVAL;
}
setup = rndis_class->hport->setup;
cmd = (rndis_initialize_msg_t *)g_rndis_buf;
cmd->MessageType = REMOTE_NDIS_INITIALIZE_MSG;
cmd->MessageLength = sizeof(rndis_initialize_msg_t);
cmd->RequestId = rndis_class->request_id++;
cmd->MajorVersion = 1;
cmd->MinorVersion = 0;
cmd->MaxTransferSize = 0x4000;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CDC_REQUEST_SEND_ENCAPSULATED_COMMAND;
setup->wValue = 0;
setup->wIndex = 0;
setup->wLength = sizeof(rndis_initialize_msg_t);
ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)cmd);
if (ret < 0) {
USB_LOG_ERR("rndis_initialize_msg_t send error, ret: %d\r\n", ret);
return ret;
}
usbh_rndis_get_notification(rndis_class);
resp = (rndis_initialize_cmplt_t *)g_rndis_buf;
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CDC_REQUEST_GET_ENCAPSULATED_RESPONSE;
setup->wValue = 0;
setup->wIndex = 0;
setup->wLength = 4096;
ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)resp);
if (ret < 0) {
USB_LOG_ERR("rndis_initialize_cmplt_t recv error, ret: %d\r\n", ret);
return ret;
}
rndis_class->max_transfer_pkts = resp->MaxPacketsPerTransfer;
rndis_class->max_transfer_size = resp->MaxTransferSize;
USB_LOG_INFO("MaxPacketsPerTransfer:%d\r\n", resp->MaxPacketsPerTransfer);
USB_LOG_INFO("MaxTransferSize:%d\r\n", resp->MaxTransferSize);
return ret;
}
int usbh_rndis_query_msg_transfer(struct usbh_rndis *rndis_class, uint32_t oid, uint32_t query_len, uint8_t *info, uint32_t *info_len)
{
struct usb_setup_packet *setup;
int ret = 0;
rndis_query_msg_t *cmd;
rndis_query_cmplt_t *resp;
if (!rndis_class || !rndis_class->hport) {
return -USB_ERR_INVAL;
}
setup = rndis_class->hport->setup;
cmd = (rndis_query_msg_t *)g_rndis_buf;
cmd->MessageType = REMOTE_NDIS_QUERY_MSG;
cmd->MessageLength = query_len + sizeof(rndis_query_msg_t);
cmd->RequestId = rndis_class->request_id++;
cmd->Oid = oid;
cmd->InformationBufferLength = query_len;
cmd->InformationBufferOffset = 20;
cmd->DeviceVcHandle = 0;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CDC_REQUEST_SEND_ENCAPSULATED_COMMAND;
setup->wValue = 0;
setup->wIndex = 0;
setup->wLength = query_len + sizeof(rndis_query_msg_t);
ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)cmd);
if (ret < 0) {
USB_LOG_ERR("oid:%08x send error, ret: %d\r\n", (unsigned int)oid, ret);
return ret;
}
usbh_rndis_get_notification(rndis_class);
resp = (rndis_query_cmplt_t *)g_rndis_buf;
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CDC_REQUEST_GET_ENCAPSULATED_RESPONSE;
setup->wValue = 0;
setup->wIndex = 0;
setup->wLength = 4096;
ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)resp);
if (ret < 0) {
USB_LOG_ERR("oid:%08x recv error, ret: %d\r\n", (unsigned int)oid, ret);
return ret;
}
memcpy(info, ((uint8_t *)resp + sizeof(rndis_query_cmplt_t)), resp->InformationBufferLength);
*info_len = resp->InformationBufferLength;
return ret;
}
static int usbh_rndis_set_msg_transfer(struct usbh_rndis *rndis_class, uint32_t oid, uint8_t *info, uint32_t info_len)
{
struct usb_setup_packet *setup;
int ret = 0;
rndis_set_msg_t *cmd;
rndis_set_cmplt_t *resp;
if (!rndis_class || !rndis_class->hport) {
return -USB_ERR_INVAL;
}
setup = rndis_class->hport->setup;
cmd = (rndis_set_msg_t *)g_rndis_buf;
cmd->MessageType = REMOTE_NDIS_SET_MSG;
cmd->MessageLength = info_len + sizeof(rndis_set_msg_t);
cmd->RequestId = rndis_class->request_id++;
cmd->Oid = oid;
cmd->InformationBufferLength = info_len;
cmd->InformationBufferOffset = 20;
cmd->DeviceVcHandle = 0;
memcpy(((uint8_t *)cmd + sizeof(rndis_set_msg_t)), info, info_len);
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CDC_REQUEST_SEND_ENCAPSULATED_COMMAND;
setup->wValue = 0;
setup->wIndex = 0;
setup->wLength = info_len + sizeof(rndis_set_msg_t);
ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)cmd);
if (ret < 0) {
USB_LOG_ERR("oid:%08x send error, ret: %d\r\n", (unsigned int)oid, ret);
return ret;
}
usbh_rndis_get_notification(rndis_class);
resp = (rndis_set_cmplt_t *)g_rndis_buf;
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CDC_REQUEST_GET_ENCAPSULATED_RESPONSE;
setup->wValue = 0;
setup->wIndex = 0;
setup->wLength = 4096;
ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)resp);
if (ret < 0) {
USB_LOG_ERR("oid:%08x recv error, ret: %d\r\n", (unsigned int)oid, ret);
return ret;
}
return ret;
}
int usbh_rndis_get_connect_status(struct usbh_rndis *rndis_class)
{
int ret;
uint8_t data[32];
uint32_t data_len;
ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_MEDIA_CONNECT_STATUS, 4, data, &data_len);
if (ret < 0) {
return ret;
}
if (NDIS_MEDIA_STATE_CONNECTED == data[0]) {
rndis_class->connect_status = true;
} else {
rndis_class->connect_status = false;
}
return 0;
}
int usbh_rndis_keepalive(struct usbh_rndis *rndis_class)
{
struct usb_setup_packet *setup;
int ret = 0;
rndis_keepalive_msg_t *cmd;
rndis_keepalive_cmplt_t *resp;
if (!rndis_class || !rndis_class->hport) {
return -USB_ERR_INVAL;
}
setup = rndis_class->hport->setup;
cmd = (rndis_keepalive_msg_t *)g_rndis_buf;
cmd->MessageType = REMOTE_NDIS_KEEPALIVE_MSG;
cmd->MessageLength = sizeof(rndis_keepalive_msg_t);
cmd->RequestId = rndis_class->request_id++;
setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CDC_REQUEST_SEND_ENCAPSULATED_COMMAND;
setup->wValue = 0;
setup->wIndex = 0;
setup->wLength = sizeof(rndis_keepalive_msg_t);
ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)cmd);
if (ret < 0) {
USB_LOG_ERR("keepalive send error, ret: %d\r\n", ret);
return ret;
}
usbh_rndis_get_notification(rndis_class);
resp = (rndis_keepalive_cmplt_t *)g_rndis_buf;
setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
setup->bRequest = CDC_REQUEST_GET_ENCAPSULATED_RESPONSE;
setup->wValue = 0;
setup->wIndex = 0;
setup->wLength = 4096;
ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)resp);
if (ret < 0) {
USB_LOG_ERR("keepalive recv error, ret: %d\r\n", ret);
return ret;
}
return ret;
}
static int usbh_rndis_connect(struct usbh_hubport *hport, uint8_t intf)
{
struct usb_endpoint_descriptor *ep_desc;
int ret;
uint32_t *oid_support_list;
unsigned int oid = 0;
unsigned int oid_num = 0;
uint32_t data_len;
uint8_t tmp_buffer[512];
uint8_t data[32];
struct usbh_rndis *rndis_class = &g_rndis_class;
memset(rndis_class, 0, sizeof(struct usbh_rndis));
rndis_class->hport = hport;
rndis_class->ctrl_intf = intf;
rndis_class->data_intf = intf + 1;
hport->config.intf[intf].priv = rndis_class;
hport->config.intf[intf + 1].priv = NULL;
// ep_desc = &hport->config.intf[intf].altsetting[0].ep[0].ep_desc;
// USBH_EP_INIT(rndis_class->intin, ep_desc);
for (uint8_t i = 0; i < hport->config.intf[intf + 1].altsetting[0].intf_desc.bNumEndpoints; i++) {
ep_desc = &hport->config.intf[intf + 1].altsetting[0].ep[i].ep_desc;
if (ep_desc->bEndpointAddress & 0x80) {
USBH_EP_INIT(rndis_class->bulkin, ep_desc);
} else {
USBH_EP_INIT(rndis_class->bulkout, ep_desc);
}
}
ret = usbh_rndis_init_msg_transfer(rndis_class);
if (ret < 0) {
return ret;
}
USB_LOG_INFO("rndis init success\r\n");
ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_SUPPORTED_LIST, 0, tmp_buffer, &data_len);
if (ret < 0) {
return ret;
}
oid_num = (data_len / 4);
USB_LOG_INFO("rndis query OID_GEN_SUPPORTED_LIST success,oid num :%d\r\n", oid_num);
oid_support_list = (uint32_t *)tmp_buffer;
for (uint8_t i = 0; i < oid_num; i++) {
oid = oid_support_list[i];
switch (oid) {
case OID_GEN_PHYSICAL_MEDIUM:
ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_PHYSICAL_MEDIUM, 4, data, &data_len);
if (ret < 0) {
goto query_errorout;
}
break;
case OID_GEN_MAXIMUM_FRAME_SIZE:
ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_MAXIMUM_FRAME_SIZE, 4, data, &data_len);
if (ret < 0) {
goto query_errorout;
}
break;
case OID_GEN_LINK_SPEED:
ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_LINK_SPEED, 4, data, &data_len);
if (ret < 0) {
goto query_errorout;
}
memcpy(&rndis_class->link_speed, data, 4);
break;
case OID_GEN_MEDIA_CONNECT_STATUS:
ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_MEDIA_CONNECT_STATUS, 4, data, &data_len);
if (ret < 0) {
goto query_errorout;
}
if (NDIS_MEDIA_STATE_CONNECTED == data[0]) {
rndis_class->connect_status = true;
} else {
rndis_class->connect_status = false;
}
break;
case OID_802_3_MAXIMUM_LIST_SIZE:
ret = usbh_rndis_query_msg_transfer(rndis_class, OID_802_3_MAXIMUM_LIST_SIZE, 4, data, &data_len);
if (ret < 0) {
goto query_errorout;
}
break;
case OID_802_3_CURRENT_ADDRESS:
ret = usbh_rndis_query_msg_transfer(rndis_class, OID_802_3_CURRENT_ADDRESS, 6, data, &data_len);
if (ret < 0) {
goto query_errorout;
}
for (uint8_t j = 0; j < 6; j++) {
rndis_class->mac[j] = data[j];
}
break;
case OID_802_3_PERMANENT_ADDRESS:
ret = usbh_rndis_query_msg_transfer(rndis_class, OID_802_3_PERMANENT_ADDRESS, 6, data, &data_len);
if (ret < 0) {
goto query_errorout;
}
break;
default:
USB_LOG_WRN("Ignore rndis query iod:%08x\r\n", oid);
continue;
}
USB_LOG_INFO("rndis query iod:%08x success\r\n", oid);
}
uint32_t packet_filter = 0x0f;
usbh_rndis_set_msg_transfer(rndis_class, OID_GEN_CURRENT_PACKET_FILTER, (uint8_t *)&packet_filter, 4);
if (ret < 0) {
return ret;
}
USB_LOG_INFO("rndis set OID_GEN_CURRENT_PACKET_FILTER success\r\n");
uint8_t multicast_list[6] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0x01 };
usbh_rndis_set_msg_transfer(rndis_class, OID_802_3_MULTICAST_LIST, multicast_list, 6);
if (ret < 0) {
return ret;
}
USB_LOG_INFO("rndis set OID_802_3_MULTICAST_LIST success\r\n");
USB_LOG_INFO("rndis MAC address %02x:%02x:%02x:%02x:%02x:%02x\r\n",
rndis_class->mac[0],
rndis_class->mac[1],
rndis_class->mac[2],
rndis_class->mac[3],
rndis_class->mac[4],
rndis_class->mac[5]);
strncpy(hport->config.intf[intf].devname, DEV_FORMAT, CONFIG_USBHOST_DEV_NAMELEN);
USB_LOG_INFO("Register RNDIS Class:%s\r\n", hport->config.intf[intf].devname);
usbh_rndis_run(rndis_class);
return ret;
query_errorout:
USB_LOG_ERR("rndis query iod:%08x error\r\n", oid);
return ret;
}
static int usbh_rndis_disconnect(struct usbh_hubport *hport, uint8_t intf)
{
int ret = 0;
struct usbh_rndis *rndis_class = (struct usbh_rndis *)hport->config.intf[intf].priv;
if (rndis_class) {
if (rndis_class->bulkin) {
usbh_kill_urb(&rndis_class->bulkin_urb);
}
if (rndis_class->bulkout) {
usbh_kill_urb(&rndis_class->bulkout_urb);
}
// if (rndis_class->intin) {
// usbh_kill_urb(&rndis_class->intin_urb);
// }
if (hport->config.intf[intf].devname[0] != '\0') {
USB_LOG_INFO("Unregister RNDIS Class:%s\r\n", hport->config.intf[intf].devname);
usbh_rndis_stop(rndis_class);
}
memset(rndis_class, 0, sizeof(struct usbh_rndis));
}
return ret;
}
void usbh_rndis_rx_thread(void *argument)
{
uint32_t g_rndis_rx_length;
int ret;
uint32_t pmg_offset;
rndis_data_packet_t *pmsg;
rndis_data_packet_t temp;
#if CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE <= (16 * 1024)
uint32_t transfer_size = CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE;
#else
uint32_t transfer_size = (16 * 1024);
#endif
USB_LOG_INFO("Create rndis rx thread\r\n");
// clang-format off
find_class:
// clang-format on
g_rndis_class.connect_status = false;
if (usbh_find_class_instance("/dev/rndis") == NULL) {
goto delete;
}
while (g_rndis_class.connect_status == false) {
ret = usbh_rndis_get_connect_status(&g_rndis_class);
if (ret < 0) {
usb_osal_msleep(100);
goto find_class;
}
usb_osal_msleep(128);
}
g_rndis_rx_length = 0;
while (1) {
usbh_bulk_urb_fill(&g_rndis_class.bulkin_urb, g_rndis_class.hport, g_rndis_class.bulkin, &g_rndis_rx_buffer[g_rndis_rx_length], transfer_size, USB_OSAL_WAITING_FOREVER, NULL, NULL);
ret = usbh_submit_urb(&g_rndis_class.bulkin_urb);
if (ret < 0) {
goto find_class;
}
g_rndis_rx_length += g_rndis_class.bulkin_urb.actual_length;
/* A transfer is complete because last packet is a short packet.
* Short packet is not zero, match g_rndis_rx_length % USB_GET_MAXPACKETSIZE(g_rndis_class.bulkin->wMaxPacketSize).
* Short packet cannot be zero.
*/
if (g_rndis_rx_length % USB_GET_MAXPACKETSIZE(g_rndis_class.bulkin->wMaxPacketSize)) {
pmg_offset = 0;
uint32_t total_len = g_rndis_rx_length;
while (g_rndis_rx_length > 0) {
USB_LOG_DBG("rxlen:%d\r\n", g_rndis_rx_length);
pmsg = (rndis_data_packet_t *)(g_rndis_rx_buffer + pmg_offset);
/* Not word-aligned case */
if (pmg_offset & 0x3) {
usb_memcpy(&temp, pmsg, sizeof(rndis_data_packet_t));
pmsg = &temp;
}
if (pmsg->MessageType == REMOTE_NDIS_PACKET_MSG) {
uint8_t *buf = (uint8_t *)(g_rndis_rx_buffer + pmg_offset + sizeof(rndis_generic_msg_t) + pmsg->DataOffset);
usbh_rndis_eth_input(buf, pmsg->DataLength);
pmg_offset += pmsg->MessageLength;
g_rndis_rx_length -= pmsg->MessageLength;
/* drop the last dummy byte, it is a short packet to tell us we have received a multiple of wMaxPacketSize */
if (g_rndis_rx_length < 4) {
g_rndis_rx_length = 0;
}
} else {
USB_LOG_ERR("offset:%d,remain:%d,total:%d\r\n", pmg_offset, g_rndis_rx_length, total_len);
g_rndis_rx_length = 0;
USB_LOG_ERR("Error rndis packet message\r\n");
}
}
} else {
#if CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE <= (16 * 1024)
if (g_rndis_rx_length == CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE) {
#else
if ((g_rndis_rx_length + (16 * 1024)) > CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE) {
#endif
USB_LOG_ERR("Rx packet is overflow, please ruduce tcp window size or increase CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE\r\n");
while (1) {
}
}
}
}
// clang-format off
delete:
USB_LOG_INFO("Delete rndis rx thread\r\n");
usb_osal_thread_delete(NULL);
// clang-format on
}
uint8_t *usbh_rndis_get_eth_txbuf(void)
{
return (g_rndis_tx_buffer + sizeof(rndis_data_packet_t));
}
int usbh_rndis_eth_output(uint32_t buflen)
{
rndis_data_packet_t *hdr;
uint32_t len;
if (g_rndis_class.connect_status == false) {
return -USB_ERR_NOTCONN;
}
hdr = (rndis_data_packet_t *)g_rndis_tx_buffer;
memset(hdr, 0, sizeof(rndis_data_packet_t));
hdr->MessageType = REMOTE_NDIS_PACKET_MSG;
hdr->MessageLength = sizeof(rndis_data_packet_t) + buflen;
hdr->DataOffset = sizeof(rndis_data_packet_t) - sizeof(rndis_generic_msg_t);
hdr->DataLength = buflen;
len = hdr->MessageLength;
/* if message length is the multiple of wMaxPacketSize, we should add a short packet to tell device transfer is over. */
if (!(len % g_rndis_class.bulkout->wMaxPacketSize)) {
len += 1;
}
USB_LOG_DBG("txlen:%d\r\n", len);
usbh_bulk_urb_fill(&g_rndis_class.bulkout_urb, g_rndis_class.hport, g_rndis_class.bulkout, g_rndis_tx_buffer, len, USB_OSAL_WAITING_FOREVER, NULL, NULL);
return usbh_submit_urb(&g_rndis_class.bulkout_urb);
}
__WEAK void usbh_rndis_run(struct usbh_rndis *rndis_class)
{
}
__WEAK void usbh_rndis_stop(struct usbh_rndis *rndis_class)
{
}
static const struct usbh_class_driver rndis_class_driver = {
.driver_name = "rndis",
.connect = usbh_rndis_connect,
.disconnect = usbh_rndis_disconnect
};
CLASS_INFO_DEFINE const struct usbh_class_info rndis_class_info = {
.match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
.class = USB_DEVICE_CLASS_WIRELESS,
.subclass = 0x01,
.protocol = 0x03,
.id_table = NULL,
.class_driver = &rndis_class_driver
};

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2022, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef USBH_RNDIS_H
#define USBH_RNDIS_H
#include "usb_cdc.h"
struct usbh_rndis {
struct usbh_hubport *hport;
struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
struct usb_endpoint_descriptor *intin; /* INTR endpoint */
struct usbh_urb bulkin_urb; /* Bulk IN urb */
struct usbh_urb bulkout_urb; /* Bulk OUT urb */
struct usbh_urb intin_urb; /* INTR IN urb */
uint8_t ctrl_intf; /* Control interface number */
uint8_t data_intf; /* Data interface number */
uint8_t minor;
uint32_t request_id;
uint32_t tx_offset;
uint32_t max_transfer_pkts; /* max packets in one transfer */
uint32_t max_transfer_size; /* max size in one transfer */
uint32_t link_speed;
bool connect_status;
uint8_t mac[6];
void *user_data;
};
#ifdef __cplusplus
extern "C" {
#endif
int usbh_rndis_get_connect_status(struct usbh_rndis *rndis_class);
int usbh_rndis_keepalive(struct usbh_rndis *rndis_class);
void usbh_rndis_run(struct usbh_rndis *rndis_class);
void usbh_rndis_stop(struct usbh_rndis *rndis_class);
uint8_t *usbh_rndis_get_eth_txbuf(void);
int usbh_rndis_eth_output(uint32_t buflen);
void usbh_rndis_eth_input(uint8_t *buf, uint32_t buflen);
void usbh_rndis_rx_thread(void *argument);
#ifdef __cplusplus
}
#endif
#endif /* USBH_RNDIS_H */