diff options
Diffstat (limited to 'components/legacy')
34 files changed, 14684 insertions, 0 deletions
diff --git a/components/legacy/Kconfig b/components/legacy/Kconfig new file mode 100644 index 0000000000..d20c0f92f9 --- /dev/null +++ b/components/legacy/Kconfig @@ -0,0 +1,200 @@ +menu "Using USB legacy version" + config RT_USING_USB + bool + default n + + config RT_USING_USB_HOST + bool "Using USB host" + default n + select RT_USING_USB + + if RT_USING_USB_HOST + config RT_USBH_MSTORAGE + bool "Enable Udisk Drivers" + default n + if RT_USBH_MSTORAGE + config UDISK_MOUNTPOINT + string "Udisk mount dir" + default "/" + endif + config RT_USBH_HID + bool "Enable HID Drivers" + default n + if RT_USBH_HID + config RT_USBH_HID_MOUSE + bool "Enable HID mouse protocol" + default n + config RT_USBH_HID_KEYBOARD + bool "Enable HID keyboard protocol" + default n + endif + endif + config RT_USING_USB_DEVICE + bool "Using USB device" + default n + select RT_USING_USB + + if RT_USING_USB_DEVICE || RT_USING_USB_HOST + config RT_USBD_THREAD_STACK_SZ + int "usb thread stack size" + default 4096 + endif + if RT_USING_USB_DEVICE + config USB_VENDOR_ID + hex "USB Vendor ID" + default 0x0FFE + config USB_PRODUCT_ID + hex "USB Product ID" + default 0x0001 + + config RT_USB_DEVICE_COMPOSITE + bool "Enable composite device" + default n + choice + prompt "Device type" + default _RT_USB_DEVICE_NONE + depends on !RT_USB_DEVICE_COMPOSITE + config _RT_USB_DEVICE_NONE + bool "Using custom class by register interface" + select RT_USB_DEVICE_NONE + config _RT_USB_DEVICE_CDC + bool "Enable to use device as CDC device" + select RT_USB_DEVICE_CDC + config _RT_USB_DEVICE_MSTORAGE + bool "Enable to use device as Mass Storage device" + select RT_USB_DEVICE_MSTORAGE + config _RT_USB_DEVICE_HID + bool "Enable to use device as HID device" + select RT_USB_DEVICE_HID + config _RT_USB_DEVICE_RNDIS + bool "Enable to use device as rndis device" + select RT_USB_DEVICE_RNDIS + depends on RT_USING_LWIP + config _RT_USB_DEVICE_ECM + bool "Enable to use device as ecm device" + select RT_USB_DEVICE_ECM + depends on RT_USING_LWIP + config _RT_USB_DEVICE_WINUSB + bool "Enable to use device as winusb device" + select RT_USB_DEVICE_WINUSB + config _RT_USB_DEVICE_AUDIO + bool "Enable to use device as audio device" + select RT_USB_DEVICE_AUDIO + endchoice + if RT_USB_DEVICE_COMPOSITE + config RT_USB_DEVICE_CDC + bool "Enable to use device as CDC device" + default n + config RT_USB_DEVICE_NONE + bool + default y + config RT_USB_DEVICE_MSTORAGE + bool "Enable to use device as Mass Storage device" + default n + config RT_USB_DEVICE_HID + bool "Enable to use device as HID device" + default n + config RT_USB_DEVICE_RNDIS + bool "Enable to use device as rndis device" + default n + depends on RT_USING_LWIP + config RT_USB_DEVICE_ECM + bool "Enable to use device as ecm device" + default n + depends on RT_USING_LWIP + config RT_USB_DEVICE_WINUSB + bool "Enable to use device as winusb device" + default n + config RT_USB_DEVICE_AUDIO + bool "Enable to use device as audio device" + default n + endif + if RT_USB_DEVICE_CDC + config RT_VCOM_TASK_STK_SIZE + int "virtual com thread stack size" + default 512 + config RT_CDC_RX_BUFSIZE + int "virtual com rx buffer size" + default 128 + config RT_VCOM_TX_USE_DMA + bool "Enable to use dma for vcom tx" + default n + config RT_VCOM_SERNO + string "serial number of virtual com" + default "32021919830108" + config RT_VCOM_SER_LEN + int "serial number length of virtual com" + default 14 + config RT_VCOM_TX_TIMEOUT + int "tx timeout(ticks) of virtual com" + default 1000 + endif + if RT_USB_DEVICE_WINUSB + config RT_WINUSB_GUID + string "Guid for winusb" + default "{6860DC3C-C05F-4807-8807-1CA861CC1D66}" + endif + if RT_USB_DEVICE_MSTORAGE + config RT_USB_MSTORAGE_DISK_NAME + string "msc class disk name" + default "flash0" + endif + + if RT_USB_DEVICE_RNDIS + config RNDIS_DELAY_LINK_UP + bool "Delay linkup media connection" + select RT_USING_TIMER_SOFT + default n + endif + + if RT_USB_DEVICE_HID + config RT_USB_DEVICE_HID_KEYBOARD + bool "Use to HID device as Keyboard" + default n + if RT_USB_DEVICE_HID_KEYBOARD + config RT_USB_DEVICE_HID_KEYBOARD_NUMBER + int "Number of Keyboard(max 3)" + default 1 + range 1 3 + endif + config RT_USB_DEVICE_HID_MOUSE + bool "Use to HID device as Mouse" + default n + config RT_USB_DEVICE_HID_GENERAL + bool "Use to HID device as General HID device" + default y + if RT_USB_DEVICE_HID_GENERAL + config RT_USB_DEVICE_HID_GENERAL_OUT_REPORT_LENGTH + int "General HID device out report length" + default 63 + range 0 63 + + config RT_USB_DEVICE_HID_GENERAL_IN_REPORT_LENGTH + int "General HID device in report length" + default 63 + range 0 63 + endif + config RT_USB_DEVICE_HID_MEDIA + bool "Use to HID device as media keyboard" + default y + endif + if RT_USB_DEVICE_AUDIO + config RT_USB_DEVICE_AUDIO_MIC + bool "Use usb mic device as audio device" + default n + if RT_USB_DEVICE_AUDIO_MIC + config RT_USBD_MIC_DEVICE_NAME + string "audio mic device name" + default "mic0" + endif + config RT_USB_DEVICE_AUDIO_SPEAKER + bool "Use usb speaker device as audio device" + default n + if RT_USB_DEVICE_AUDIO_SPEAKER + config RT_USBD_SPEAKER_DEVICE_NAME + string "audio speaker device name" + default "sound0" + endif + endif + endif +endmenu diff --git a/components/legacy/usb/SConscript b/components/legacy/usb/SConscript new file mode 100644 index 0000000000..3e148cefe2 --- /dev/null +++ b/components/legacy/usb/SConscript @@ -0,0 +1,13 @@ +# for module compiling +import os +from building import * + +cwd = GetCurrentDir() +objs = [] +list = os.listdir(cwd) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) +Return('objs') diff --git a/components/legacy/usb/usbdevice/SConscript b/components/legacy/usb/usbdevice/SConscript new file mode 100644 index 0000000000..3cc5bf6d43 --- /dev/null +++ b/components/legacy/usb/usbdevice/SConscript @@ -0,0 +1,38 @@ +Import('RTT_ROOT') +from building import * + +cwd = GetCurrentDir() +src = Split(""" +core/usbdevice_core.c +core/usbdevice.c +""") + +if GetDepend('RT_USB_DEVICE_CDC'): + src += Glob('class/cdc_vcom.c') + +if GetDepend('RT_USB_DEVICE_HID'): + src += Glob('class/hid.c') + +if GetDepend('RT_USB_DEVICE_MSTORAGE'): + src += Glob('class/mstorage.c') + +if GetDepend('RT_USB_DEVICE_ECM'): + src += Glob('class/ecm.c') + +if GetDepend('RT_USB_DEVICE_RNDIS'): + src += Glob('class/rndis.c') + +if GetDepend('RT_USB_DEVICE_WINUSB'): + src += Glob('class/winusb.c') + +if GetDepend('RT_USB_DEVICE_AUDIO_MIC'): + src += Glob('class/audio_mic.c') + +if GetDepend('RT_USB_DEVICE_AUDIO_SPEAKER'): + src += Glob('class/audio_speaker.c') + +CPPPATH = [cwd] + +group = DefineGroup('rt_usbd', src, depend = ['RT_USING_USB_DEVICE'], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/legacy/usb/usbdevice/class/audio_mic.c b/components/legacy/usb/usbdevice/class/audio_mic.c new file mode 100644 index 0000000000..9612e6b93f --- /dev/null +++ b/components/legacy/usb/usbdevice/class/audio_mic.c @@ -0,0 +1,581 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-09-07 flybreak the first version + */ + +#include <rthw.h> +#include <rtdevice.h> +#include "drivers/usb_device.h" + +#include "uaudioreg.h" + +#define DBG_TAG "usbd.audio.mic" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + +#define RECORD_SAMPLERATE 16000 +#define RECORD_CHANNEL 1 +#define RESOLUTION_BITS 16 + +#define RESOLUTION_BYTE (RESOLUTION_BITS / 8) +#define RECORD_PER_MS_SZ ((RECORD_SAMPLERATE * RECORD_CHANNEL * RESOLUTION_BYTE) / 1000) +#define RECORD_BUFFER_SZ (RECORD_PER_MS_SZ * 20) /* 20ms */ + +#if defined(RT_USBD_MIC_DEVICE_NAME) + #define MIC_DEVICE_NAME RT_USBD_MIC_DEVICE_NAME +#else + #define MIC_DEVICE_NAME "mic0" +#endif + +#define EVENT_RECORD_START (1 << 0) +#define EVENT_RECORD_STOP (1 << 1) +#define EVENT_RECORD_DATA (1 << 2) + +#define MIC_INTF_STR_INDEX 8 +/* + * uac mic descriptor define + */ + +#define UAC_CS_INTERFACE 0x24 +#define UAC_CS_ENDPOINT 0x25 + +#define UAC_MAX_PACKET_SIZE 64 +#define UAC_EP_MAX_PACKET_SIZE 32 +#define UAC_CHANNEL_NUM RECORD_CHANNEL + +struct uac_ac_descriptor +{ +#ifdef RT_USB_DEVICE_COMPOSITE + struct uiad_descriptor iad_desc; +#endif + struct uinterface_descriptor intf_desc; + struct usb_audio_control_descriptor hdr_desc; + struct usb_audio_input_terminal it_desc; + struct usb_audio_output_terminal ot_desc; +#if UAC_USE_FEATURE_UNIT + struct usb_audio_feature_unit feature_unit_desc; +#endif +}; + +struct uac_as_descriptor +{ + struct uinterface_descriptor intf_desc; + struct usb_audio_streaming_interface_descriptor hdr_desc; + struct usb_audio_streaming_type1_descriptor format_type_desc; + struct uendpoint_descriptor ep_desc; + struct usb_audio_streaming_endpoint_descriptor as_ep_desc; +}; + +/* + * uac mic device type + */ + +struct uac_audio_mic +{ + rt_device_t dev; + rt_event_t event; + rt_uint8_t open_count; + + rt_uint8_t *buffer; + rt_uint32_t buffer_index; + + uep_t ep; +}; +static struct uac_audio_mic mic; + +rt_align(4) +static struct udevice_descriptor dev_desc = +{ + USB_DESC_LENGTH_DEVICE, //bLength; + USB_DESC_TYPE_DEVICE, //type; + USB_BCD_VERSION, //bcdUSB; + USB_CLASS_DEVICE, //bDeviceClass; + 0x00, //bDeviceSubClass; + 0x00, //bDeviceProtocol; + UAC_MAX_PACKET_SIZE, //bMaxPacketSize0; + _VENDOR_ID, //idVendor; + _PRODUCT_ID, //idProduct; + USB_BCD_DEVICE, //bcdDevice; + USB_STRING_MANU_INDEX, //iManufacturer; + USB_STRING_PRODUCT_INDEX, //iProduct; + USB_STRING_SERIAL_INDEX, //iSerialNumber;Unused. + USB_DYNAMIC, //bNumConfigurations; +}; + +//FS and HS needed +rt_align(4) +static struct usb_qualifier_descriptor dev_qualifier = +{ + sizeof(dev_qualifier), //bLength + USB_DESC_TYPE_DEVICEQUALIFIER, //bDescriptorType + 0x0200, //bcdUSB + USB_CLASS_AUDIO, //bDeviceClass + 0x00, //bDeviceSubClass + 0x00, //bDeviceProtocol + 64, //bMaxPacketSize0 + 0x01, //bNumConfigurations + 0, +}; + +rt_align(4) +const static char *_ustring[] = +{ + "Language", + "RT-Thread Team.", + "RT-Thread Audio Microphone", + "32021919830108", + "Configuration", + "Interface", +}; + +rt_align(4) +static struct uac_ac_descriptor ac_desc = +{ +#ifdef RT_USB_DEVICE_COMPOSITE + /* Interface Association Descriptor */ + { + USB_DESC_LENGTH_IAD, + USB_DESC_TYPE_IAD, + USB_DYNAMIC, + 0x02, + USB_CLASS_AUDIO, + USB_SUBCLASS_AUDIOSTREAMING, + 0x00, + 0x00, + }, +#endif + /* Interface Descriptor */ + { + USB_DESC_LENGTH_INTERFACE, + USB_DESC_TYPE_INTERFACE, + USB_DYNAMIC, + 0x00, + 0x00, + USB_CLASS_AUDIO, + USB_SUBCLASS_AUDIOCONTROL, + 0x00, +#ifdef RT_USB_DEVICE_COMPOSITE + MIC_INTF_STR_INDEX, +#else + 0x00, +#endif + }, + /* Header Descriptor */ + { + sizeof(struct usb_audio_control_descriptor), + UAC_CS_INTERFACE, + UDESCSUB_AC_HEADER, + 0x0100, /* Version: 1.00 */ + 0x001E, /* Total length: 30 */ + 0x01, /* Total number of interfaces: 1 */ + {0x01}, /* Interface number: 1 */ + }, + /* Input Terminal Descriptor */ + { + sizeof(struct usb_audio_input_terminal), + UAC_CS_INTERFACE, + UDESCSUB_AC_INPUT, + 0x01, /* Terminal ID: 1 */ + 0x0201, /* Terminal Type: Microphone (0x0201) */ + 0x00, /* Assoc Terminal: 0 */ + 0x01, /* Number Channels: 1 */ + 0x0000, /* Channel Config: 0x0000 */ + 0x00, /* Channel Names: 0 */ + 0x00, /* Terminal: 0 */ + }, + /* Output Terminal Descriptor */ + { + sizeof(struct usb_audio_output_terminal), + UAC_CS_INTERFACE, + UDESCSUB_AC_OUTPUT, + 0x02, /* Terminal ID: 2 */ + 0x0101, /* Terminal Type: USB Streaming (0x0101) */ + 0x00, /* Assoc Terminal: 0 */ + 0x01, /* Source ID: 1 */ + 0x00, /* Terminal: 0 */ + }, +#if UAC_USE_FEATURE_UNIT + /* Feature unit Descriptor */ + { + sizeof(struct usb_audio_feature_unit), + UAC_CS_INTERFACE, + UDESCSUB_AC_FEATURE, + 0x02, + 0x01, + 0x01, + 0x00, + 0x01, + }, +#endif +}; + +rt_align(4) +static struct uinterface_descriptor as_desc0 = +{ + USB_DESC_LENGTH_INTERFACE, + USB_DESC_TYPE_INTERFACE, + USB_DYNAMIC, + 0x00, + 0x00, + USB_CLASS_AUDIO, + USB_SUBCLASS_AUDIOSTREAMING, + 0x00, + 0x00, +}; + +rt_align(4) +static struct uac_as_descriptor as_desc = +{ + /* Interface Descriptor */ + { + USB_DESC_LENGTH_INTERFACE, + USB_DESC_TYPE_INTERFACE, + USB_DYNAMIC, + 0x01, + 0x01, + USB_CLASS_AUDIO, + USB_SUBCLASS_AUDIOSTREAMING, + 0x00, + 0x00, + }, + /* General AS Descriptor */ + { + sizeof(struct usb_audio_streaming_interface_descriptor), + UAC_CS_INTERFACE, + AS_GENERAL, + 0x02, /* Terminal ID: 2 */ + 0x01, /* Interface delay in frames: 1 */ + UA_FMT_PCM, + }, + /* Format type i Descriptor */ + { + sizeof(struct usb_audio_streaming_type1_descriptor), + UAC_CS_INTERFACE, + FORMAT_TYPE, + FORMAT_TYPE_I, + UAC_CHANNEL_NUM, + 2, /* Subframe Size: 2 */ + RESOLUTION_BITS, + 0x01, /* Samples Frequence Type: 1 */ + {0}, /* Samples Frequence */ + }, + /* Endpoint Descriptor */ + { + USB_DESC_LENGTH_ENDPOINT, + USB_DESC_TYPE_ENDPOINT, + USB_DYNAMIC | USB_DIR_IN, + USB_EP_ATTR_ISOC, + UAC_EP_MAX_PACKET_SIZE, + 0x01, + }, + /* AS Endpoint Descriptor */ + { + sizeof(struct usb_audio_streaming_endpoint_descriptor), + UAC_CS_ENDPOINT, + AS_GENERAL, + }, +}; + +void mic_entry(void *parameter) +{ + struct rt_audio_caps caps = {0}; + rt_uint32_t e, index; + + mic.buffer = rt_malloc(RECORD_BUFFER_SZ); + if (mic.buffer == RT_NULL) + { + LOG_E("malloc failed"); + goto __exit; + } + + mic.dev = rt_device_find(MIC_DEVICE_NAME); + if (mic.dev == RT_NULL) + { + LOG_E("can't find device:%s", MIC_DEVICE_NAME); + goto __exit; + } + + while (1) + { + if (rt_event_recv(mic.event, EVENT_RECORD_START | EVENT_RECORD_STOP, + RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, + 1000, &e) != RT_EOK) + { + continue; + } + if (mic.open_count == 0) + { + continue; + } + LOG_D("record start"); + + rt_device_open(mic.dev, RT_DEVICE_OFLAG_RDONLY); + + caps.main_type = AUDIO_TYPE_INPUT; + caps.sub_type = AUDIO_DSP_PARAM; + caps.udata.config.samplerate = RECORD_SAMPLERATE; + caps.udata.config.channels = RECORD_CHANNEL; + caps.udata.config.samplebits = RESOLUTION_BITS; + rt_device_control(mic.dev, AUDIO_CTL_CONFIGURE, &caps); + + while (1) + { + if (rt_event_recv(mic.event, EVENT_RECORD_DATA | EVENT_RECORD_STOP, + RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, + 1000, &e) != RT_EOK) + { + if (mic.open_count > 0) + continue; + else + break; + } + if (e & EVENT_RECORD_DATA) + { + index = (mic.buffer_index >= RECORD_BUFFER_SZ / 2) ? 0 : (RECORD_BUFFER_SZ / 2); + rt_device_read(mic.dev, 0, mic.buffer + index, RECORD_BUFFER_SZ / 2); + } + else if (e & EVENT_RECORD_STOP) + { + break; + } + } + LOG_D("record stop"); + rt_device_close(mic.dev); + } + +__exit: + if (mic.buffer) + rt_free(mic.buffer); +} + +static rt_err_t _record_start(ufunction_t func) +{ + mic.ep->request.buffer = RT_NULL; + mic.ep->request.size = UAC_EP_MAX_PACKET_SIZE; + mic.ep->request.req_type = UIO_REQUEST_WRITE; + rt_usbd_io_request(func->device, mic.ep, &mic.ep->request); + + mic.open_count ++; + rt_event_send(mic.event, EVENT_RECORD_START); + return 0; +} + +static rt_err_t _record_stop(ufunction_t func) +{ + mic.open_count --; + rt_event_send(mic.event, EVENT_RECORD_STOP); + return 0; +} + +static rt_err_t _ep_data_in_handler(ufunction_t func, rt_size_t size) +{ + RT_ASSERT(func != RT_NULL); + LOG_D("_ep_data_in_handler"); + + mic.ep->request.buffer = mic.buffer + mic.buffer_index; + mic.ep->request.size = UAC_EP_MAX_PACKET_SIZE; + mic.ep->request.req_type = UIO_REQUEST_WRITE; + rt_usbd_io_request(func->device, mic.ep, &mic.ep->request); + + mic.buffer_index += UAC_EP_MAX_PACKET_SIZE; + if (mic.buffer_index >= RECORD_BUFFER_SZ) + { + mic.buffer_index = 0; + rt_event_send(mic.event, EVENT_RECORD_DATA); + } + else if (mic.buffer_index == RECORD_BUFFER_SZ / 2) + { + rt_event_send(mic.event, EVENT_RECORD_DATA); + } + + return RT_EOK; +} + +static rt_err_t _interface_as_handler(ufunction_t func, ureq_t setup) +{ + RT_ASSERT(func != RT_NULL); + RT_ASSERT(func->device != RT_NULL); + RT_ASSERT(setup != RT_NULL); + + LOG_D("_interface_as_handler"); + + if ((setup->request_type & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_STANDARD) + { + switch (setup->bRequest) + { + case USB_REQ_GET_INTERFACE: + break; + case USB_REQ_SET_INTERFACE: + LOG_D("set interface handler"); + if (setup->wValue == 1) + { + _record_start(func); + } + else if (setup->wValue == 0) + { + _record_stop(func); + } + break; + default: + LOG_D("unknown uac request 0x%x", setup->bRequest); + return -RT_ERROR; + } + } + + return RT_EOK; +} + +static rt_err_t _function_enable(ufunction_t func) +{ + RT_ASSERT(func != RT_NULL); + + LOG_D("uac function enable"); + + return RT_EOK; +} + +static rt_err_t _function_disable(ufunction_t func) +{ + RT_ASSERT(func != RT_NULL); + + LOG_D("uac function disable"); + _record_stop(func); + return RT_EOK; +} + +static struct ufunction_ops ops = +{ + _function_enable, + _function_disable, + RT_NULL, +}; +/** + * This function will configure uac descriptor. + * + * @param comm the communication interface number. + * @param data the data interface number. + * + * @return RT_EOK on successful. + */ +static rt_err_t _uac_descriptor_config(struct uac_ac_descriptor *ac, + rt_uint8_t cintf_nr, struct uac_as_descriptor *as, rt_uint8_t sintf_nr) +{ + ac->hdr_desc.baInterfaceNr[0] = sintf_nr; +#ifdef RT_USB_DEVICE_COMPOSITE + ac->iad_desc.bFirstInterface = cintf_nr; +#endif + + return RT_EOK; +} + +static rt_err_t _uac_samplerate_config(struct uac_as_descriptor *as, rt_uint32_t samplerate) +{ + as->format_type_desc.tSamFreq[0 * 3 + 2] = samplerate >> 16 & 0xff; + as->format_type_desc.tSamFreq[0 * 3 + 1] = samplerate >> 8 & 0xff; + as->format_type_desc.tSamFreq[0 * 3 + 0] = samplerate & 0xff; + return RT_EOK; +} + +/** + * This function will create a uac function instance. + * + * @param device the usb device object. + * + * @return RT_EOK on successful. + */ +ufunction_t rt_usbd_function_uac_mic_create(udevice_t device) +{ + ufunction_t func; + uintf_t intf_ac, intf_as; + ualtsetting_t setting_as0; + ualtsetting_t setting_ac, setting_as; + struct uac_as_descriptor *as_desc_t; + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + +#ifdef RT_USB_DEVICE_COMPOSITE + rt_usbd_device_set_interface_string(device, MIC_INTF_STR_INDEX, _ustring[2]); +#else + /* set usb device string description */ + rt_usbd_device_set_string(device, _ustring); +#endif + /* create a uac function */ + func = rt_usbd_function_new(device, &dev_desc, &ops); + //not support HS + //rt_usbd_device_set_qualifier(device, &dev_qualifier); + + /* create interface */ + intf_ac = rt_usbd_interface_new(device, RT_NULL); + intf_as = rt_usbd_interface_new(device, _interface_as_handler); + + /* create alternate setting */ + setting_ac = rt_usbd_altsetting_new(sizeof(struct uac_ac_descriptor)); + setting_as0 = rt_usbd_altsetting_new(sizeof(struct uinterface_descriptor)); + setting_as = rt_usbd_altsetting_new(sizeof(struct uac_as_descriptor)); + /* config desc in alternate setting */ + rt_usbd_altsetting_config_descriptor(setting_ac, &ac_desc, + (rt_off_t) & ((struct uac_ac_descriptor *)0)->intf_desc); + rt_usbd_altsetting_config_descriptor(setting_as0, &as_desc0, 0); + rt_usbd_altsetting_config_descriptor(setting_as, &as_desc, + (rt_off_t) & ((struct uac_as_descriptor *)0)->intf_desc); + /* configure the uac interface descriptor */ + _uac_descriptor_config(setting_ac->desc, intf_ac->intf_num, setting_as->desc, intf_as->intf_num); + _uac_samplerate_config(setting_as->desc, RECORD_SAMPLERATE); + + /* create endpoint */ + as_desc_t = (struct uac_as_descriptor *)setting_as->desc; + mic.ep = rt_usbd_endpoint_new(&as_desc_t->ep_desc, _ep_data_in_handler); + + /* add the endpoint to the alternate setting */ + rt_usbd_altsetting_add_endpoint(setting_as, mic.ep); + + /* add the alternate setting to the interface, then set default setting of the interface */ + rt_usbd_interface_add_altsetting(intf_ac, setting_ac); + rt_usbd_set_altsetting(intf_ac, 0); + rt_usbd_interface_add_altsetting(intf_as, setting_as0); + rt_usbd_interface_add_altsetting(intf_as, setting_as); + rt_usbd_set_altsetting(intf_as, 0); + + /* add the interface to the uac function */ + rt_usbd_function_add_interface(func, intf_ac); + rt_usbd_function_add_interface(func, intf_as); + + return func; +} + +int audio_mic_init(void) +{ + rt_thread_t mic_tid; + mic.event = rt_event_create("mic_event", RT_IPC_FLAG_FIFO); + + mic_tid = rt_thread_create("mic_thread", + mic_entry, RT_NULL, + 1024, + 5, 10); + + if (mic_tid != RT_NULL) + rt_thread_startup(mic_tid); + return RT_EOK; +} +INIT_COMPONENT_EXPORT(audio_mic_init); + +/* + * register uac class + */ +struct udclass uac_class = +{ + .rt_usbd_function_create = rt_usbd_function_uac_mic_create +}; + +int rt_usbd_uac_mic_class_register(void) +{ + rt_usbd_class_register(&uac_class); + return 0; +} +INIT_PREV_EXPORT(rt_usbd_uac_mic_class_register); diff --git a/components/legacy/usb/usbdevice/class/audio_speaker.c b/components/legacy/usb/usbdevice/class/audio_speaker.c new file mode 100644 index 0000000000..6560990c8e --- /dev/null +++ b/components/legacy/usb/usbdevice/class/audio_speaker.c @@ -0,0 +1,582 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-09-19 flybreak the first version + */ + +#include <rthw.h> +#include <rtdevice.h> +#include "drivers/usb_device.h" + +#define AUFMT_MAX_FREQUENCIES 1 +#include "uaudioreg.h" + +#define DBG_TAG "usbd.audio.speaker" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + +#define AUDIO_SAMPLERATE 16000 +#define AUDIO_CHANNEL 1 +#define RESOLUTION_BITS 16 + +#define RESOLUTION_BYTE (RESOLUTION_BITS / 8) +#define AUDIO_PER_MS_SZ ((AUDIO_SAMPLERATE * AUDIO_CHANNEL * RESOLUTION_BYTE) / 1000) +#define AUDIO_BUFFER_SZ (AUDIO_PER_MS_SZ * 20) /* 20ms */ + +#if defined(RT_USBD_SPEAKER_DEVICE_NAME) + #define SPEAKER_DEVICE_NAME RT_USBD_SPEAKER_DEVICE_NAME +#else + #define SPEAKER_DEVICE_NAME "sound0" +#endif + +#define EVENT_AUDIO_START (1 << 0) +#define EVENT_AUDIO_STOP (1 << 1) +#define EVENT_AUDIO_DATA (1 << 2) + +#define SPK_INTF_STR_INDEX 9 +/* + * uac speaker descriptor define + */ + +#define UAC_CS_INTERFACE 0x24 +#define UAC_CS_ENDPOINT 0x25 + +#define UAC_MAX_PACKET_SIZE 64 +#define UAC_EP_MAX_PACKET_SIZE 32 +#define UAC_CHANNEL_NUM AUDIO_CHANNEL + +struct uac_ac_descriptor +{ +#ifdef RT_USB_DEVICE_COMPOSITE + struct uiad_descriptor iad_desc; +#endif + struct uinterface_descriptor intf_desc; + struct usb_audio_control_descriptor hdr_desc; + struct usb_audio_input_terminal it_desc; + struct usb_audio_output_terminal ot_desc; +#if UAC_USE_FEATURE_UNIT + struct usb_audio_feature_unit feature_unit_desc; +#endif +}; + +struct uac_as_descriptor +{ + struct uinterface_descriptor intf_desc; + struct usb_audio_streaming_interface_descriptor hdr_desc; + struct usb_audio_streaming_type1_descriptor format_type_desc; + struct uendpoint_descriptor ep_desc; + struct usb_audio_streaming_endpoint_descriptor as_ep_desc; +}; + +/* + * uac speaker device type + */ + +struct uac_audio_speaker +{ + rt_device_t dev; + rt_event_t event; + rt_uint8_t open_count; + + rt_uint8_t *buffer; + rt_uint32_t buffer_index; + + uep_t ep; +}; +static struct uac_audio_speaker speaker; + +rt_align(4) +static struct udevice_descriptor dev_desc = +{ + USB_DESC_LENGTH_DEVICE, //bLength; + USB_DESC_TYPE_DEVICE, //type; + USB_BCD_VERSION, //bcdUSB; + USB_CLASS_DEVICE, //bDeviceClass; + 0x00, //bDeviceSubClass; + 0x00, //bDeviceProtocol; + UAC_MAX_PACKET_SIZE, //bMaxPacketSize0; + _VENDOR_ID, //idVendor; + _PRODUCT_ID, //idProduct; + USB_BCD_DEVICE, //bcdDevice; + USB_STRING_MANU_INDEX, //iManufacturer; + USB_STRING_PRODUCT_INDEX, //iProduct; + USB_STRING_SERIAL_INDEX, //iSerialNumber;Unused. + USB_DYNAMIC, //bNumConfigurations; +}; + +//FS and HS needed +rt_align(4) +static struct usb_qualifier_descriptor dev_qualifier = +{ + sizeof(dev_qualifier), //bLength + USB_DESC_TYPE_DEVICEQUALIFIER, //bDescriptorType + 0x0200, //bcdUSB + USB_CLASS_AUDIO, //bDeviceClass + 0x00, //bDeviceSubClass + 0x00, //bDeviceProtocol + 64, //bMaxPacketSize0 + 0x01, //bNumConfigurations + 0, +}; + +rt_align(4) +const static char *_ustring[] = +{ + "Language", + "RT-Thread Team.", + "RT-Thread Audio Speaker", + "32021919830108", + "Configuration", + "Interface", +}; + +rt_align(4) +static struct uac_ac_descriptor ac_desc = +{ +#ifdef RT_USB_DEVICE_COMPOSITE + /* Interface Association Descriptor */ + { + USB_DESC_LENGTH_IAD, + USB_DESC_TYPE_IAD, + USB_DYNAMIC, + 0x02, + USB_CLASS_AUDIO, + USB_SUBCLASS_AUDIOSTREAMING, + 0x00, + 0x00, + }, +#endif + /* Interface Descriptor */ + { + USB_DESC_LENGTH_INTERFACE, + USB_DESC_TYPE_INTERFACE, + USB_DYNAMIC, + 0x00, + 0x00, + USB_CLASS_AUDIO, + USB_SUBCLASS_AUDIOCONTROL, + 0x00, +#ifdef RT_USB_DEVICE_COMPOSITE + SPK_INTF_STR_INDEX, +#else + 0x00, +#endif + }, + /* Header Descriptor */ + { + sizeof(struct usb_audio_control_descriptor), + UAC_CS_INTERFACE, + UDESCSUB_AC_HEADER, + 0x0100, /* Version: 1.00 */ + 0x0027, /* Total length: 39 */ + 0x01, /* Total number of interfaces: 1 */ + {0x01}, /* Interface number: 1 */ + }, + /* Input Terminal Descriptor */ + { + sizeof(struct usb_audio_input_terminal), + UAC_CS_INTERFACE, + UDESCSUB_AC_INPUT, + 0x01, /* Terminal ID: 1 */ + 0x0101, /* Terminal Type: USB Streaming (0x0101) */ + 0x00, /* Assoc Terminal: 0 */ + 0x01, /* Number Channels: 1 */ + 0x0000, /* Channel Config: 0x0000 */ + 0x00, /* Channel Names: 0 */ + 0x00, /* Terminal: 0 */ + }, + /* Output Terminal Descriptor */ + { + sizeof(struct usb_audio_output_terminal), + UAC_CS_INTERFACE, + UDESCSUB_AC_OUTPUT, + 0x02, /* Terminal ID: 2 */ + 0x0302, /* Terminal Type: Headphones (0x0302) */ + 0x00, /* Assoc Terminal: 0 */ + 0x01, /* Source ID: 1 */ + 0x00, /* Terminal: 0 */ + }, +#if UAC_USE_FEATURE_UNIT + /* Feature unit Descriptor */ + { + UAC_DT_FEATURE_UNIT_SIZE(UAC_CH_NUM), + UAC_CS_INTERFACE, + UAC_FEATURE_UNIT, + 0x02, + 0x0101, + 0x00, + 0x01, + }, +#endif +}; + +rt_align(4) +static struct uinterface_descriptor as_desc0 = +{ + USB_DESC_LENGTH_INTERFACE, + USB_DESC_TYPE_INTERFACE, + USB_DYNAMIC, + 0x00, + 0x00, + USB_CLASS_AUDIO, + USB_SUBCLASS_AUDIOSTREAMING, + 0x00, + 0x00, +}; + +rt_align(4) +static struct uac_as_descriptor as_desc = +{ + /* Interface Descriptor */ + { + USB_DESC_LENGTH_INTERFACE, + USB_DESC_TYPE_INTERFACE, + USB_DYNAMIC, + 0x01, + 0x01, + USB_CLASS_AUDIO, + USB_SUBCLASS_AUDIOSTREAMING, + 0x00, + 0x00, + }, + /* General AS Descriptor */ + { + sizeof(struct usb_audio_streaming_interface_descriptor), + UAC_CS_INTERFACE, + AS_GENERAL, + 0x01, /* Terminal ID: 1 */ + 0x01, /* Interface delay in frames: 1 */ + UA_FMT_PCM, + }, + /* Format type i Descriptor */ + { + sizeof(struct usb_audio_streaming_type1_descriptor), + UAC_CS_INTERFACE, + FORMAT_TYPE, + FORMAT_TYPE_I, + UAC_CHANNEL_NUM, + 2, /* Subframe Size: 2 */ + RESOLUTION_BITS, + 0x01, /* Samples Frequence Type: 1 */ + {0}, /* Samples Frequence */ + }, + /* Endpoint Descriptor */ + { + USB_DESC_LENGTH_ENDPOINT, + USB_DESC_TYPE_ENDPOINT, + USB_DYNAMIC | USB_DIR_OUT, + USB_EP_ATTR_ISOC, + UAC_EP_MAX_PACKET_SIZE, + 0x01, + }, + /* AS Endpoint Descriptor */ + { + sizeof(struct usb_audio_streaming_endpoint_descriptor), + UAC_CS_ENDPOINT, + AS_GENERAL, + }, +}; + +void speaker_entry(void *parameter) +{ + struct rt_audio_caps caps = {0}; + rt_uint32_t e, index; + + speaker.buffer = rt_malloc(AUDIO_BUFFER_SZ); + if (speaker.buffer == RT_NULL) + { + LOG_E("malloc failed"); + goto __exit; + } + + speaker.dev = rt_device_find(SPEAKER_DEVICE_NAME); + if (speaker.dev == RT_NULL) + { + LOG_E("can't find device:%s", SPEAKER_DEVICE_NAME); + goto __exit; + } + + while (1) + { + if (rt_event_recv(speaker.event, EVENT_AUDIO_START | EVENT_AUDIO_STOP, + RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, + 1000, &e) != RT_EOK) + { + continue; + } + if (speaker.open_count == 0) + { + continue; + } + LOG_D("play start"); + + rt_device_open(speaker.dev, RT_DEVICE_OFLAG_WRONLY); + + caps.main_type = AUDIO_TYPE_OUTPUT; + caps.sub_type = AUDIO_DSP_PARAM; + caps.udata.config.samplerate = AUDIO_SAMPLERATE; + caps.udata.config.channels = AUDIO_CHANNEL; + caps.udata.config.samplebits = RESOLUTION_BITS; + rt_device_control(speaker.dev, AUDIO_CTL_CONFIGURE, &caps); + + while (1) + { + if (rt_event_recv(speaker.event, EVENT_AUDIO_DATA | EVENT_AUDIO_STOP, + RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, + 1000, &e) != RT_EOK) + { + if (speaker.open_count > 0) + continue; + else + break; + } + if (e & EVENT_AUDIO_DATA) + { + index = (speaker.buffer_index >= AUDIO_BUFFER_SZ / 2) ? 0 : (AUDIO_BUFFER_SZ / 2); + rt_device_write(speaker.dev, 0, speaker.buffer + index, AUDIO_BUFFER_SZ / 2); + } + else if (e & EVENT_AUDIO_STOP) + { + break; + } + } + LOG_D("play stop"); + rt_device_close(speaker.dev); + } + +__exit: + if (speaker.buffer) + rt_free(speaker.buffer); +} + +static rt_err_t _audio_start(ufunction_t func) +{ + speaker.ep->request.buffer = speaker.buffer; + speaker.ep->request.size = UAC_EP_MAX_PACKET_SIZE; + speaker.ep->request.req_type = UIO_REQUEST_READ_FULL; + rt_usbd_io_request(func->device, speaker.ep, &speaker.ep->request); + + speaker.open_count ++; + rt_event_send(speaker.event, EVENT_AUDIO_START); + + return 0; +} + +static rt_err_t _audio_stop(ufunction_t func) +{ + speaker.open_count --; + rt_event_send(speaker.event, EVENT_AUDIO_STOP); + return 0; +} + +static rt_err_t _ep_data_handler(ufunction_t func, rt_size_t size) +{ + RT_ASSERT(func != RT_NULL); + LOG_D("_ep_data_handler"); + + speaker.ep->request.buffer = speaker.buffer + speaker.buffer_index; + speaker.ep->request.size = UAC_EP_MAX_PACKET_SIZE; + speaker.ep->request.req_type = UIO_REQUEST_READ_FULL; + rt_usbd_io_request(func->device, speaker.ep, &speaker.ep->request); + + speaker.buffer_index += UAC_EP_MAX_PACKET_SIZE; + if (speaker.buffer_index >= AUDIO_BUFFER_SZ) + { + speaker.buffer_index = 0; + rt_event_send(speaker.event, EVENT_AUDIO_DATA); + } + else if (speaker.buffer_index == AUDIO_BUFFER_SZ / 2) + { + rt_event_send(speaker.event, EVENT_AUDIO_DATA); + } + + return RT_EOK; +} + +static rt_err_t _interface_as_handler(ufunction_t func, ureq_t setup) +{ + RT_ASSERT(func != RT_NULL); + RT_ASSERT(func->device != RT_NULL); + RT_ASSERT(setup != RT_NULL); + + LOG_D("_interface_as_handler"); + + if ((setup->request_type & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_STANDARD) + { + switch (setup->bRequest) + { + case USB_REQ_GET_INTERFACE: + break; + case USB_REQ_SET_INTERFACE: + LOG_D("set interface handler"); + if (setup->wValue == 1) + { + _audio_start(func); + } + else if (setup->wValue == 0) + { + _audio_stop(func); + } + break; + default: + LOG_D("unknown uac request 0x%x", setup->bRequest); + return -RT_ERROR; + } + } + + return RT_EOK; +} + +static rt_err_t _function_enable(ufunction_t func) +{ + RT_ASSERT(func != RT_NULL); + + LOG_D("uac function enable"); + + return RT_EOK; +} + +static rt_err_t _function_disable(ufunction_t func) +{ + RT_ASSERT(func != RT_NULL); + + LOG_D("uac function disable"); + _audio_stop(func); + return RT_EOK; +} + +static struct ufunction_ops ops = +{ + _function_enable, + _function_disable, + RT_NULL, +}; +/** + * This function will configure uac descriptor. + * + * @param comm the communication interface number. + * @param data the data interface number. + * + * @return RT_EOK on successful. + */ +static rt_err_t _uac_descriptor_config(struct uac_ac_descriptor *ac, + rt_uint8_t cintf_nr, struct uac_as_descriptor *as, rt_uint8_t sintf_nr) +{ + ac->hdr_desc.baInterfaceNr[0] = sintf_nr; +#ifdef RT_USB_DEVICE_COMPOSITE + ac->iad_desc.bFirstInterface = cintf_nr; +#endif + + return RT_EOK; +} + +static rt_err_t _uac_samplerate_config(struct uac_as_descriptor *as, rt_uint32_t samplerate) +{ + as->format_type_desc.tSamFreq[0 * 3 + 2] = samplerate >> 16 & 0xff; + as->format_type_desc.tSamFreq[0 * 3 + 1] = samplerate >> 8 & 0xff; + as->format_type_desc.tSamFreq[0 * 3 + 0] = samplerate & 0xff; + return RT_EOK; +} + +/** + * This function will create a uac function instance. + * + * @param device the usb device object. + * + * @return RT_EOK on successful. + */ +ufunction_t rt_usbd_function_uac_speaker_create(udevice_t device) +{ + ufunction_t func; + uintf_t intf_ac, intf_as; + ualtsetting_t setting_as0; + ualtsetting_t setting_ac, setting_as; + struct uac_as_descriptor *as_desc_t; + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + +#ifdef RT_USB_DEVICE_COMPOSITE + rt_usbd_device_set_interface_string(device, SPK_INTF_STR_INDEX, _ustring[2]); +#else + /* set usb device string description */ + rt_usbd_device_set_string(device, _ustring); +#endif + /* create a uac function */ + func = rt_usbd_function_new(device, &dev_desc, &ops); + //not support HS + //rt_usbd_device_set_qualifier(device, &dev_qualifier); + + /* create interface */ + intf_ac = rt_usbd_interface_new(device, RT_NULL); + intf_as = rt_usbd_interface_new(device, _interface_as_handler); + + /* create alternate setting */ + setting_ac = rt_usbd_altsetting_new(sizeof(struct uac_ac_descriptor)); + setting_as0 = rt_usbd_altsetting_new(sizeof(struct uinterface_descriptor)); + setting_as = rt_usbd_altsetting_new(sizeof(struct uac_as_descriptor)); + /* config desc in alternate setting */ + rt_usbd_altsetting_config_descriptor(setting_ac, &ac_desc, + (rt_off_t) & ((struct uac_ac_descriptor *)0)->intf_desc); + rt_usbd_altsetting_config_descriptor(setting_as0, &as_desc0, 0); + rt_usbd_altsetting_config_descriptor(setting_as, &as_desc, + (rt_off_t) & ((struct uac_as_descriptor *)0)->intf_desc); + /* configure the uac interface descriptor */ + _uac_descriptor_config(setting_ac->desc, intf_ac->intf_num, setting_as->desc, intf_as->intf_num); + _uac_samplerate_config(setting_as->desc, AUDIO_SAMPLERATE); + + /* create endpoint */ + as_desc_t = (struct uac_as_descriptor *)setting_as->desc; + speaker.ep = rt_usbd_endpoint_new(&as_desc_t->ep_desc, _ep_data_handler); + + /* add the endpoint to the alternate setting */ + rt_usbd_altsetting_add_endpoint(setting_as, speaker.ep); + + /* add the alternate setting to the interface, then set default setting of the interface */ + rt_usbd_interface_add_altsetting(intf_ac, setting_ac); + rt_usbd_set_altsetting(intf_ac, 0); + rt_usbd_interface_add_altsetting(intf_as, setting_as0); + rt_usbd_interface_add_altsetting(intf_as, setting_as); + rt_usbd_set_altsetting(intf_as, 0); + + /* add the interface to the uac function */ + rt_usbd_function_add_interface(func, intf_ac); + rt_usbd_function_add_interface(func, intf_as); + + return func; +} + +int audio_speaker_init(void) +{ + rt_thread_t speaker_tid; + speaker.event = rt_event_create("speaker_event", RT_IPC_FLAG_FIFO); + + speaker_tid = rt_thread_create("speaker_thread", + speaker_entry, RT_NULL, + 1024, + 5, 10); + + if (speaker_tid != RT_NULL) + rt_thread_startup(speaker_tid); + return RT_EOK; +} +INIT_COMPONENT_EXPORT(audio_speaker_init); + +/* + * register uac class + */ +static struct udclass uac_speaker_class = +{ + .rt_usbd_function_create = rt_usbd_function_uac_speaker_create +}; + +int rt_usbd_uac_speaker_class_register(void) +{ + rt_usbd_class_register(&uac_speaker_class); + return 0; +} +INIT_PREV_EXPORT(rt_usbd_uac_speaker_class_register); diff --git a/components/legacy/usb/usbdevice/class/cdc.h b/components/legacy/usb/usbdevice/class/cdc.h new file mode 100644 index 0000000000..6b79876734 --- /dev/null +++ b/components/legacy/usb/usbdevice/class/cdc.h @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2012-10-03 Yi Qiu first version + * 2012-12-12 heyuanjie87 add CDC endpoints collection + */ + +#ifndef __CDC_H__ +#define __CDC_H__ + +#define USB_CDC_BUFSIZE 0x40 + +#define USB_CDC_CLASS_COMM 0x02 +#define USB_CDC_CLASS_DATA 0x0A + +#define USB_CDC_SUBCLASS_NONE 0x00 +#define USB_CDC_SUBCLASS_DLCM 0x01 +#define USB_CDC_SUBCLASS_ACM 0x02 +#define USB_CDC_SUBCLASS_TCM 0x03 +#define USB_CDC_SUBCLASS_MCCM 0x04 +#define USB_CDC_SUBCLASS_CCM 0x05 +#define USB_CDC_SUBCLASS_ETH 0x06 +#define USB_CDC_SUBCLASS_ATM 0x07 +#define USB_CDC_SUBCLASS_EEM 0x0C + +#define USB_CDC_PROTOCOL_NONE 0x00 +#define USB_CDC_PROTOCOL_V25TER 0x01 +#define USB_CDC_PROTOCOL_I430 0x30 +#define USB_CDC_PROTOCOL_HDLC 0x31 +#define USB_CDC_PROTOCOL_TRANS 0x32 +#define USB_CDC_PROTOCOL_Q921M 0x50 +#define USB_CDC_PROTOCOL_Q921 0x51 +#define USB_CDC_PROTOCOL_Q921TM 0x52 +#define USB_CDC_PROTOCOL_V42BIS 0x90 +#define USB_CDC_PROTOCOL_Q931 0x91 +#define USB_CDC_PROTOCOL_V120 0x92 +#define USB_CDC_PROTOCOL_CAPI20 0x93 +#define USB_CDC_PROTOCOL_HOST 0xFD +#define USB_CDC_PROTOCOL_PUFD 0xFE +#define USB_CDC_PROTOCOL_VENDOR 0xFF +#define USB_CDC_PROTOCOL_EEM 0x07 + +#define USB_CDC_CS_INTERFACE 0x24 +#define USB_CDC_CS_ENDPOINT 0x25 + +#define USB_CDC_SCS_HEADER 0x00 +#define USB_CDC_SCS_CALL_MGMT 0x01 +#define USB_CDC_SCS_ACM 0x02 +#define USB_CDC_SCS_UNION 0x06 +#define USB_CDC_SCS_ETH 0x0F + +#define CDC_SEND_ENCAPSULATED_COMMAND 0x00 +#define CDC_GET_ENCAPSULATED_RESPONSE 0x01 +#define CDC_SET_COMM_FEATURE 0x02 +#define CDC_GET_COMM_FEATURE 0x03 +#define CDC_CLEAR_COMM_FEATURE 0x04 +#define CDC_SET_AUX_LINE_STATE 0x10 +#define CDC_SET_HOOK_STATE 0x11 +#define CDC_PULSE_SETUP 0x12 +#define CDC_SEND_PULSE 0x13 +#define CDC_SET_PULSE_TIME 0x14 +#define CDC_RING_AUX_JACK 0x15 +#define CDC_SET_LINE_CODING 0x20 +#define CDC_GET_LINE_CODING 0x21 +#define CDC_SET_CONTROL_LINE_STATE 0x22 +#define CDC_SEND_BREAK 0x23 +#define CDC_SET_RINGER_PARMS 0x30 +#define CDC_GET_RINGER_PARMS 0x31 +#define CDC_SET_OPERATION_PARMS 0x32 +#define CDC_GET_OPERATION_PARMS 0x33 +#define CDC_SET_LINE_PARMS 0x34 +#define CDC_GET_LINE_PARMS 0x35 +#define CDC_DIAL_DIGITS 0x36 +#define CDC_SET_UNIT_PARAMETER 0x37 +#define CDC_GET_UNIT_PARAMETER 0x38 +#define CDC_CLEAR_UNIT_PARAMETER 0x39 +#define CDC_GET_PROFILE 0x3A +#define CDC_SET_ETH_MULTICAST_FILTERS 0x40 +#define CDC_SET_ETH_POWER_MGMT_FILT 0x41 +#define CDC_GET_ETH_POWER_MGMT_FILT 0x42 +#define CDC_SET_ETH_PACKET_FILTER 0x43 +#define CDC_GET_ETH_STATISTIC 0x44 +#define CDC_SET_ATM_DATA_FORMAT 0x50 +#define CDC_GET_ATM_DEVICE_STATISTICS 0x51 +#define CDC_SET_ATM_DEFAULT_VC 0x52 +#define CDC_GET_ATM_VC_STATISTICS 0x53 + +#pragma pack(1) + +struct ucdc_header_descriptor +{ + rt_uint8_t length; + rt_uint8_t type; + rt_uint8_t subtype; + rt_uint16_t bcd; +}; +typedef struct ucdc_header_descriptor* ucdc_hdr_desc_t; + +struct ucdc_acm_descriptor +{ + rt_uint8_t length; + rt_uint8_t type; + rt_uint8_t subtype; + rt_uint8_t capabilties; +}; +typedef struct ucdc_acm_descriptor* ucdc_acm_desc_t; + +struct ucdc_call_mgmt_descriptor +{ + rt_uint8_t length; + rt_uint8_t type; + rt_uint8_t subtype; + rt_uint8_t capabilties; + rt_uint8_t data_interface; +}; +typedef struct ucdc_call_mgmt_descriptor* ucdc_call_mgmt_desc_t; + +struct ucdc_union_descriptor +{ + rt_uint8_t length; + rt_uint8_t type; + rt_uint8_t subtype; + rt_uint8_t master_interface; + rt_uint8_t slave_interface0; +}; +typedef struct ucdc_union_descriptor* ucdc_union_desc_t; + +struct ucdc_comm_descriptor +{ +#ifdef RT_USB_DEVICE_COMPOSITE + struct uiad_descriptor iad_desc; +#endif + struct uinterface_descriptor intf_desc; + struct ucdc_header_descriptor hdr_desc; + struct ucdc_call_mgmt_descriptor call_mgmt_desc; + struct ucdc_acm_descriptor acm_desc; + struct ucdc_union_descriptor union_desc; + struct uendpoint_descriptor ep_desc; +}; +typedef struct ucdc_comm_descriptor* ucdc_comm_desc_t; + +struct ucdc_enet_descriptor +{ + rt_uint8_t bFunctionLength; + rt_uint8_t bDescriptorType; + rt_uint8_t bDescriptorSubtype; + rt_uint8_t iMACAddress; + rt_uint8_t bmEthernetStatistics[4]; + rt_uint16_t wMaxSegmentSize; + rt_uint16_t wMCFilters; + rt_uint8_t bNumberPowerFilters; +}; +struct ucdc_eth_descriptor +{ +#ifdef RT_USB_DEVICE_COMPOSITE + struct uiad_descriptor iad_desc; +#endif + struct uinterface_descriptor intf_desc; + struct ucdc_header_descriptor hdr_desc; + struct ucdc_union_descriptor union_desc; + struct ucdc_enet_descriptor enet_desc; + struct uendpoint_descriptor ep_desc; +}; +typedef struct ucdc_eth_descriptor* ucdc_eth_desc_t; + +struct ucdc_data_descriptor +{ + struct uinterface_descriptor intf_desc; + struct uendpoint_descriptor ep_out_desc; + struct uendpoint_descriptor ep_in_desc; +}; +typedef struct ucdc_data_descriptor* ucdc_data_desc_t; + +struct ucdc_line_coding +{ + rt_uint32_t dwDTERate; + rt_uint8_t bCharFormat; + rt_uint8_t bParityType; + rt_uint8_t bDataBits; +}; +typedef struct ucdc_line_coding* ucdc_line_coding_t; + +struct cdc_eps +{ + uep_t ep_out; + uep_t ep_in; + uep_t ep_cmd; +}; +typedef struct cdc_eps* cdc_eps_t; + + + +struct ucdc_management_element_notifications +{ + rt_uint8_t bmRequestType; + rt_uint8_t bNotificatinCode; + rt_uint16_t wValue; + rt_uint16_t wIndex; + rt_uint16_t wLength; +}; +typedef struct ucdc_management_element_notifications * ucdc_mg_notifications_t; + +struct ucdc_connection_speed_change_data +{ + rt_uint32_t down_bit_rate; + rt_uint32_t up_bit_rate; +}; +typedef struct connection_speed_change_data * connect_speed_data_t; + +enum ucdc_notification_code +{ + UCDC_NOTIFI_NETWORK_CONNECTION = 0x00, + UCDC_NOTIFI_RESPONSE_AVAILABLE = 0x01, + UCDC_NOTIFI_AUX_JACK_HOOK_STATE = 0x08, + UCDC_NOTIFI_RING_DETECT = 0x09, + UCDC_NOTIFI_SERIAL_STATE = 0x20, + UCDC_NOTIFI_CALL_STATE_CHANGE = 0x28, + UCDC_NOTIFI_LINE_STATE_CHANGE = 0x29, + UCDC_NOTIFI_CONNECTION_SPEED_CHANGE = 0x2A, +}; +typedef enum ucdc_notification_code ucdc_notification_code_t; + +#pragma pack() + +#endif diff --git a/components/legacy/usb/usbdevice/class/cdc_vcom.c b/components/legacy/usb/usbdevice/class/cdc_vcom.c new file mode 100644 index 0000000000..dec102b135 --- /dev/null +++ b/components/legacy/usb/usbdevice/class/cdc_vcom.c @@ -0,0 +1,1008 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2012-10-02 Yi Qiu first version + * 2012-12-12 heyuanjie87 change endpoints and function handler + * 2013-06-25 heyuanjie87 remove SOF mechinism + * 2013-07-20 Yi Qiu do more test + * 2016-02-01 Urey Fix some error + * 2021-10-14 mazhiyuan Fix some error + */ + +#include <rthw.h> +#include <rtdevice.h> +#include "drivers/usb_device.h" +#include "cdc.h" + +#ifdef RT_USB_DEVICE_CDC + +#define DBG_TAG "usbdevice_cdc" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + +#define VCOM_INTF_STR_INDEX 5 +#ifdef RT_VCOM_TX_TIMEOUT +#define VCOM_TX_TIMEOUT RT_VCOM_TX_TIMEOUT +#else /*!RT_VCOM_TX_TIMEOUT*/ +#define VCOM_TX_TIMEOUT 1000 +#endif /*RT_VCOM_TX_TIMEOUT*/ + +#ifdef RT_CDC_RX_BUFSIZE +#define CDC_RX_BUFSIZE RT_CDC_RX_BUFSIZE +#else +#define CDC_RX_BUFSIZE 128 +#endif +#define CDC_MAX_PACKET_SIZE 64 +#define VCOM_DEVICE "vcom" + +#ifdef RT_VCOM_TASK_STK_SIZE +#define VCOM_TASK_STK_SIZE RT_VCOM_TASK_STK_SIZE +#else /*!RT_VCOM_TASK_STK_SIZE*/ +#define VCOM_TASK_STK_SIZE 512 +#endif /*RT_VCOM_TASK_STK_SIZE*/ + +#ifdef RT_VCOM_TX_USE_DMA +#define VCOM_TX_USE_DMA +#endif /*RT_VCOM_TX_USE_DMA*/ + +#ifdef RT_VCOM_SERNO +#define _SER_NO RT_VCOM_SERNO +#else /*!RT_VCOM_SERNO*/ +#define _SER_NO "32021919830108" +#endif /*RT_VCOM_SERNO*/ + +#ifdef RT_VCOM_SER_LEN +#define _SER_NO_LEN RT_VCOM_SER_LEN +#else /*!RT_VCOM_SER_LEN*/ +#define _SER_NO_LEN 14 /*rt_strlen("32021919830108")*/ +#endif /*RT_VCOM_SER_LEN*/ + +rt_align(RT_ALIGN_SIZE) +static rt_uint8_t vcom_thread_stack[VCOM_TASK_STK_SIZE]; +static struct rt_thread vcom_thread; +static struct ucdc_line_coding line_coding; + +#define CDC_TX_BUFSIZE 1024 +#define CDC_BULKIN_MAXSIZE (CDC_TX_BUFSIZE / 8) + +#define CDC_TX_HAS_DATE 0x01 +#define CDC_TX_HAS_SPACE 0x02 + +struct vcom +{ + struct rt_serial_device serial; + uep_t ep_out; + uep_t ep_in; + uep_t ep_cmd; + rt_bool_t connected; + rt_bool_t in_sending; + struct rt_completion wait; + rt_uint8_t rx_rbp[CDC_RX_BUFSIZE]; + struct rt_ringbuffer rx_ringbuffer; + rt_uint8_t tx_rbp[CDC_TX_BUFSIZE]; + struct rt_ringbuffer tx_ringbuffer; + struct rt_event tx_event; +}; + +struct vcom_tx_msg +{ + struct rt_serial_device * serial; + const char *buf; + rt_size_t size; +}; + +rt_align(4) +static struct udevice_descriptor dev_desc = +{ + USB_DESC_LENGTH_DEVICE, //bLength; + USB_DESC_TYPE_DEVICE, //type; + USB_BCD_VERSION, //bcdUSB; + USB_CLASS_CDC, //bDeviceClass; + 0x02, //bDeviceSubClass; + 0x00, //bDeviceProtocol; + CDC_MAX_PACKET_SIZE, //bMaxPacketSize0; + _VENDOR_ID, //idVendor; + _PRODUCT_ID, //idProduct; + USB_BCD_DEVICE, //bcdDevice; + USB_STRING_MANU_INDEX, //iManufacturer; + USB_STRING_PRODUCT_INDEX, //iProduct; + USB_STRING_SERIAL_INDEX, //iSerialNumber; + USB_DYNAMIC, //bNumConfigurations; +}; + +//FS and HS needed +rt_align(4) +static struct usb_qualifier_descriptor dev_qualifier = +{ + sizeof(dev_qualifier), //bLength + USB_DESC_TYPE_DEVICEQUALIFIER, //bDescriptorType + 0x0200, //bcdUSB + USB_CLASS_CDC, //bDeviceClass + 0x02, //bDeviceSubClass + 0x00, //bDeviceProtocol + 64, //bMaxPacketSize0 + 0x01, //bNumConfigurations + 0, +}; + +/* communcation interface descriptor */ +rt_align(4) +const static struct ucdc_comm_descriptor _comm_desc = +{ +#ifdef RT_USB_DEVICE_COMPOSITE + /* Interface Association Descriptor */ + { + USB_DESC_LENGTH_IAD, + USB_DESC_TYPE_IAD, + USB_DYNAMIC, + 0x02, + USB_CDC_CLASS_COMM, + USB_CDC_SUBCLASS_ACM, + USB_CDC_PROTOCOL_V25TER, + 0x00, + }, +#endif + /* Interface Descriptor */ + { + USB_DESC_LENGTH_INTERFACE, + USB_DESC_TYPE_INTERFACE, + USB_DYNAMIC, + 0x00, + 0x01, + USB_CDC_CLASS_COMM, + USB_CDC_SUBCLASS_ACM, + USB_CDC_PROTOCOL_V25TER, +#ifdef RT_USB_DEVICE_COMPOSITE + VCOM_INTF_STR_INDEX, +#else + 0, +#endif + }, + /* Header Functional Descriptor */ + { + 0x05, + USB_CDC_CS_INTERFACE, + USB_CDC_SCS_HEADER, + 0x0110, + }, + /* Call Management Functional Descriptor */ + { + 0x05, + USB_CDC_CS_INTERFACE, + USB_CDC_SCS_CALL_MGMT, + 0x00, + USB_DYNAMIC, + }, + /* Abstract Control Management Functional Descriptor */ + { + 0x04, + USB_CDC_CS_INTERFACE, + USB_CDC_SCS_ACM, + 0x02, + }, + /* Union Functional Descriptor */ + { + 0x05, + USB_CDC_CS_INTERFACE, + USB_CDC_SCS_UNION, + USB_DYNAMIC, + USB_DYNAMIC, + }, + /* Endpoint Descriptor */ + { + USB_DESC_LENGTH_ENDPOINT, + USB_DESC_TYPE_ENDPOINT, + USB_DYNAMIC | USB_DIR_IN, + USB_EP_ATTR_INT, + 0x08, + 0xFF, + }, +}; + +/* data interface descriptor */ +rt_align(4) +const static struct ucdc_data_descriptor _data_desc = +{ + /* interface descriptor */ + { + USB_DESC_LENGTH_INTERFACE, + USB_DESC_TYPE_INTERFACE, + USB_DYNAMIC, + 0x00, + 0x02, + USB_CDC_CLASS_DATA, + 0x00, + 0x00, + 0x00, + }, + /* endpoint, bulk out */ + { + USB_DESC_LENGTH_ENDPOINT, + USB_DESC_TYPE_ENDPOINT, + USB_DYNAMIC | USB_DIR_OUT, + USB_EP_ATTR_BULK, + USB_CDC_BUFSIZE, + 0x00, + }, + /* endpoint, bulk in */ + { + USB_DESC_LENGTH_ENDPOINT, + USB_DESC_TYPE_ENDPOINT, + USB_DYNAMIC | USB_DIR_IN, + USB_EP_ATTR_BULK, + USB_CDC_BUFSIZE, + 0x00, + }, +}; +rt_align(4) +static char serno[_SER_NO_LEN + 1] = {'\0'}; +rt_weak rt_err_t vcom_get_stored_serno(char *serno, int size); + +rt_err_t vcom_get_stored_serno(char *serno, int size) +{ + return -RT_ERROR; +} +rt_align(4) +const static char* _ustring[] = +{ + "Language", + "RT-Thread Team.", + "RTT Virtual Serial", + serno, + "Configuration", + "Interface", +}; +static void rt_usb_vcom_init(struct ufunction *func); + +static void _vcom_reset_state(ufunction_t func) +{ + struct vcom* data; + rt_base_t level; + + RT_ASSERT(func != RT_NULL) + + data = (struct vcom*)func->user_data; + + level = rt_hw_interrupt_disable(); + data->connected = RT_FALSE; + data->in_sending = RT_FALSE; + /*rt_kprintf("reset USB serial\n", cnt);*/ + rt_hw_interrupt_enable(level); +} + +/** + * This function will handle cdc bulk in endpoint request. + * + * @param func the usb function object. + * @param size request size. + * + * @return RT_EOK. + */ +static rt_err_t _ep_in_handler(ufunction_t func, rt_size_t size) +{ + struct vcom *data; + rt_size_t request_size; + + RT_ASSERT(func != RT_NULL); + + data = (struct vcom*)func->user_data; + request_size = data->ep_in->request.size; + LOG_D("_ep_in_handler %d", request_size); + if ((request_size != 0) && ((request_size % EP_MAXPACKET(data->ep_in)) == 0)) + { + /* don't have data right now. Send a zero-length-packet to + * terminate the transaction. + * + * FIXME: actually, this might not be the right place to send zlp. + * Only the rt_device_write could know how much data is sending. */ + data->in_sending = RT_TRUE; + + data->ep_in->request.buffer = RT_NULL; + data->ep_in->request.size = 0; + data->ep_in->request.req_type = UIO_REQUEST_WRITE; + rt_usbd_io_request(func->device, data->ep_in, &data->ep_in->request); + + return RT_EOK; + } + + rt_completion_done(&data->wait); + + return RT_EOK; +} + +/** + * This function will handle cdc bulk out endpoint request. + * + * @param func the usb function object. + * @param size request size. + * + * @return RT_EOK. + */ +static rt_err_t _ep_out_handler(ufunction_t func, rt_size_t size) +{ + rt_base_t level; + struct vcom *data; + + RT_ASSERT(func != RT_NULL); + + LOG_D("_ep_out_handler %d", size); + + data = (struct vcom*)func->user_data; + /* ensure serial is active */ + if((data->serial.parent.flag & RT_DEVICE_FLAG_ACTIVATED) + && (data->serial.parent.open_flag & RT_DEVICE_OFLAG_OPEN)) + { + /* receive data from USB VCOM */ + level = rt_hw_interrupt_disable(); + + rt_ringbuffer_put(&data->rx_ringbuffer, data->ep_out->buffer, size); + rt_hw_interrupt_enable(level); + + /* notify receive data */ + rt_hw_serial_isr(&data->serial,RT_SERIAL_EVENT_RX_IND); + } + + data->ep_out->request.buffer = data->ep_out->buffer; + data->ep_out->request.size = EP_MAXPACKET(data->ep_out); + data->ep_out->request.req_type = UIO_REQUEST_READ_BEST; + rt_usbd_io_request(func->device, data->ep_out, &data->ep_out->request); + + return RT_EOK; +} + +/** + * This function will handle cdc interrupt in endpoint request. + * + * @param device the usb device object. + * @param size request size. + * + * @return RT_EOK. + */ +static rt_err_t _ep_cmd_handler(ufunction_t func, rt_size_t size) +{ + RT_ASSERT(func != RT_NULL); + + LOG_D("_ep_cmd_handler"); + + return RT_EOK; +} + +/** + * This function will handle cdc_get_line_coding request. + * + * @param device the usb device object. + * @param setup the setup request. + * + * @return RT_EOK on successful. + */ +static rt_err_t _cdc_get_line_coding(udevice_t device, ureq_t setup) +{ + struct ucdc_line_coding data; + rt_uint16_t size; + + RT_ASSERT(device != RT_NULL); + RT_ASSERT(setup != RT_NULL); + + LOG_D("_cdc_get_line_coding"); + + data.dwDTERate = 115200; + data.bCharFormat = 0; + data.bDataBits = 8; + data.bParityType = 0; + size = setup->wLength > 7 ? 7 : setup->wLength; + + rt_usbd_ep0_write(device, (void*)&data, size); + + return RT_EOK; +} + +static rt_err_t _cdc_set_line_coding_callback(udevice_t device, rt_size_t size) +{ + LOG_D("_cdc_set_line_coding_callback"); + + dcd_ep0_send_status(device->dcd); + + return RT_EOK; +} + +/** + * This function will handle cdc_set_line_coding request. + * + * @param device the usb device object. + * @param setup the setup request. + * + * @return RT_EOK on successful. + */ +static rt_err_t _cdc_set_line_coding(udevice_t device, ureq_t setup) +{ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(setup != RT_NULL); + + LOG_D("_cdc_set_line_coding"); + + rt_usbd_ep0_read(device, (void*)&line_coding, sizeof(struct ucdc_line_coding), + _cdc_set_line_coding_callback); + + return RT_EOK; +} + +/** + * This function will handle cdc interface request. + * + * @param device the usb device object. + * @param setup the setup request. + * + * @return RT_EOK on successful. + */ +static rt_err_t _interface_handler(ufunction_t func, ureq_t setup) +{ + struct vcom *data; + + RT_ASSERT(func != RT_NULL); + RT_ASSERT(func->device != RT_NULL); + RT_ASSERT(setup != RT_NULL); + + data = (struct vcom*)func->user_data; + + switch(setup->bRequest) + { + case CDC_SEND_ENCAPSULATED_COMMAND: + break; + case CDC_GET_ENCAPSULATED_RESPONSE: + break; + case CDC_SET_COMM_FEATURE: + break; + case CDC_GET_COMM_FEATURE: + break; + case CDC_CLEAR_COMM_FEATURE: + break; + case CDC_SET_LINE_CODING: + _cdc_set_line_coding(func->device, setup); + break; + case CDC_GET_LINE_CODING: + _cdc_get_line_coding(func->device, setup); + break; + case CDC_SET_CONTROL_LINE_STATE: + data->connected = (setup->wValue & 0x01) > 0?RT_TRUE:RT_FALSE; + LOG_D("vcom state:%d ", data->connected); + dcd_ep0_send_status(func->device->dcd); + break; + case CDC_SEND_BREAK: + break; + default: + rt_kprintf("unknown cdc request\n",setup->request_type); + return -RT_ERROR; + } + + return RT_EOK; +} + +/** + * This function will run cdc function, it will be called on handle set configuration request. + * + * @param func the usb function object. + * + * @return RT_EOK on successful. + */ +static rt_err_t _function_enable(ufunction_t func) +{ + struct vcom *data; + + RT_ASSERT(func != RT_NULL); + + LOG_D("cdc function enable"); + + _vcom_reset_state(func); + + data = (struct vcom*)func->user_data; + data->ep_out->buffer = rt_malloc(CDC_RX_BUFSIZE); + RT_ASSERT(data->ep_out->buffer != RT_NULL); + +#ifdef RT_USING_SERIAL_V2 + data->serial.serial_rx = &data->rx_ringbuffer; +#endif + + data->ep_out->request.buffer = data->ep_out->buffer; + data->ep_out->request.size = EP_MAXPACKET(data->ep_out); + + data->ep_out->request.req_type = UIO_REQUEST_READ_BEST; + rt_usbd_io_request(func->device, data->ep_out, &data->ep_out->request); + + return RT_EOK; +} + +/** + * This function will stop cdc function, it will be called on handle set configuration request. + * + * @param func the usb function object. + * + * @return RT_EOK on successful. + */ +static rt_err_t _function_disable(ufunction_t func) +{ + struct vcom *data; + + RT_ASSERT(func != RT_NULL); + + LOG_D("cdc function disable"); + + _vcom_reset_state(func); + + data = (struct vcom*)func->user_data; + if(data->ep_out->buffer != RT_NULL) + { + rt_free(data->ep_out->buffer); + data->ep_out->buffer = RT_NULL; + } + + return RT_EOK; +} + +static struct ufunction_ops ops = +{ + _function_enable, + _function_disable, + RT_NULL, +}; + +/** + * This function will configure cdc descriptor. + * + * @param comm the communication interface number. + * @param data the data interface number. + * + * @return RT_EOK on successful. + */ +static rt_err_t _cdc_descriptor_config(ucdc_comm_desc_t comm, + rt_uint8_t cintf_nr, ucdc_data_desc_t data, rt_uint8_t dintf_nr) +{ + comm->call_mgmt_desc.data_interface = dintf_nr; + comm->union_desc.master_interface = cintf_nr; + comm->union_desc.slave_interface0 = dintf_nr; +#ifdef RT_USB_DEVICE_COMPOSITE + comm->iad_desc.bFirstInterface = cintf_nr; +#endif + + return RT_EOK; +} + +/** + * This function will create a cdc function instance. + * + * @param device the usb device object. + * + * @return RT_EOK on successful. + */ +ufunction_t rt_usbd_function_cdc_create(udevice_t device) +{ + ufunction_t func; + struct vcom* data; + uintf_t intf_comm, intf_data; + ualtsetting_t comm_setting, data_setting; + ucdc_data_desc_t data_desc; + ucdc_comm_desc_t comm_desc; + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + + rt_memset(serno, 0, _SER_NO_LEN + 1); + if(vcom_get_stored_serno(serno, _SER_NO_LEN) != RT_EOK) + { + rt_memset(serno, 0, _SER_NO_LEN + 1); + rt_memcpy(serno, _SER_NO, rt_strlen(_SER_NO)); + } +#ifdef RT_USB_DEVICE_COMPOSITE + rt_usbd_device_set_interface_string(device, VCOM_INTF_STR_INDEX, _ustring[2]); +#else + /* set usb device string description */ + rt_usbd_device_set_string(device, _ustring); +#endif + /* create a cdc function */ + func = rt_usbd_function_new(device, &dev_desc, &ops); + + /* support HS */ + rt_usbd_device_set_qualifier(device, &dev_qualifier); + + /* allocate memory for cdc vcom data */ + data = (struct vcom*)rt_malloc(sizeof(struct vcom)); + RT_ASSERT(data != RT_NULL); + rt_memset(data, 0, sizeof(struct vcom)); + func->user_data = (void*)data; + + /* initilize vcom */ + rt_usb_vcom_init(func); + + /* create a cdc communication interface and a cdc data interface */ + intf_comm = rt_usbd_interface_new(device, _interface_handler); + intf_data = rt_usbd_interface_new(device, _interface_handler); + + /* create a communication alternate setting and a data alternate setting */ + comm_setting = rt_usbd_altsetting_new(sizeof(struct ucdc_comm_descriptor)); + data_setting = rt_usbd_altsetting_new(sizeof(struct ucdc_data_descriptor)); + + /* config desc in alternate setting */ + rt_usbd_altsetting_config_descriptor(comm_setting, &_comm_desc, + (rt_off_t)&((ucdc_comm_desc_t)0)->intf_desc); + rt_usbd_altsetting_config_descriptor(data_setting, &_data_desc, 0); + /* configure the cdc interface descriptor */ + _cdc_descriptor_config(comm_setting->desc, intf_comm->intf_num, data_setting->desc, intf_data->intf_num); + + /* create a command endpoint */ + comm_desc = (ucdc_comm_desc_t)comm_setting->desc; + data->ep_cmd = rt_usbd_endpoint_new(&comm_desc->ep_desc, _ep_cmd_handler); + + /* add the command endpoint to the cdc communication interface */ + rt_usbd_altsetting_add_endpoint(comm_setting, data->ep_cmd); + + /* add the communication alternate setting to the communication interface, + then set default setting of the interface */ + rt_usbd_interface_add_altsetting(intf_comm, comm_setting); + rt_usbd_set_altsetting(intf_comm, 0); + + /* add the communication interface to the cdc function */ + rt_usbd_function_add_interface(func, intf_comm); + + /* create a bulk in and a bulk endpoint */ + data_desc = (ucdc_data_desc_t)data_setting->desc; + data->ep_out = rt_usbd_endpoint_new(&data_desc->ep_out_desc, _ep_out_handler); + data->ep_in = rt_usbd_endpoint_new(&data_desc->ep_in_desc, _ep_in_handler); + + /* add the bulk out and bulk in endpoints to the data alternate setting */ + rt_usbd_altsetting_add_endpoint(data_setting, data->ep_in); + rt_usbd_altsetting_add_endpoint(data_setting, data->ep_out); + + /* add the data alternate setting to the data interface + then set default setting of the interface */ + rt_usbd_interface_add_altsetting(intf_data, data_setting); + rt_usbd_set_altsetting(intf_data, 0); + + /* add the cdc data interface to cdc function */ + rt_usbd_function_add_interface(func, intf_data); + + return func; +} + +/** +* UART device in RT-Thread +*/ +static rt_err_t _vcom_configure(struct rt_serial_device *serial, + struct serial_configure *cfg) +{ + return RT_EOK; +} + +static rt_err_t _vcom_control(struct rt_serial_device *serial, + int cmd, void *arg) +{ + struct ufunction *func; + struct vcom *data; + + func = (struct ufunction*)serial->parent.user_data; + data = (struct vcom*)func->user_data; + + switch (cmd) + { + case RT_DEVICE_CTRL_CLR_INT: + /* disable rx irq */ + break; + case RT_DEVICE_CTRL_SET_INT: + /* enable rx irq */ + break; + case RT_USBD_CLASS_CTRL_CONNECTED: + (*(rt_bool_t*)arg) = data->connected; + break; + } + + return RT_EOK; +} + +static int _vcom_getc(struct rt_serial_device *serial) +{ + int result; + rt_uint8_t ch; + rt_base_t level; + struct ufunction *func; + struct vcom *data; + + func = (struct ufunction*)serial->parent.user_data; + data = (struct vcom*)func->user_data; + + result = -1; + + level = rt_hw_interrupt_disable(); + + if(rt_ringbuffer_getchar(&data->rx_ringbuffer, &ch) != 0) + { + result = ch; + } + + rt_hw_interrupt_enable(level); + + return result; +} + +static rt_ssize_t _vcom_rb_block_put(struct vcom *data, const rt_uint8_t *buf, rt_size_t size) +{ + rt_base_t level; + rt_size_t put_len = 0; + rt_size_t w_ptr = 0; + rt_uint32_t res; + rt_size_t remain_size = size; + + while (remain_size) + { + level = rt_hw_interrupt_disable(); + put_len = rt_ringbuffer_put(&data->tx_ringbuffer, (const rt_uint8_t *)&buf[w_ptr], remain_size); + rt_hw_interrupt_enable(level); + w_ptr += put_len; + remain_size -= put_len; + if (put_len == 0) + { + rt_event_recv(&data->tx_event, CDC_TX_HAS_SPACE, + RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, + VCOM_TX_TIMEOUT, &res); + } + else + { + rt_event_send(&data->tx_event, CDC_TX_HAS_DATE); + } + } + + return size; +} + +#ifdef RT_USING_SERIAL_V1 +static rt_ssize_t _vcom_tx(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size, int direction) +#else +static rt_ssize_t _vcom_tx(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size, rt_uint32_t tx_flag) +#endif +{ + struct ufunction *func; + struct vcom *data; + rt_uint32_t send_size = 0; + rt_size_t ptr = 0; + rt_uint8_t crlf[2] = {'\r', '\n',}; + + func = (struct ufunction*)serial->parent.user_data; + data = (struct vcom*)func->user_data; + + RT_ASSERT(serial != RT_NULL); + RT_ASSERT(buf != RT_NULL); + + LOG_D("%s",__func__); + + if (data->connected) + { + if((serial->parent.open_flag & RT_DEVICE_FLAG_STREAM)) + { + while(send_size < size) + { + while(ptr < size && buf[ptr] != '\n') + { + ptr++; + } + if(ptr < size) + { + send_size += _vcom_rb_block_put(data, (const rt_uint8_t *)&buf[send_size], ptr - send_size); + _vcom_rb_block_put(data, crlf, 2); + send_size++; + ptr++; + } + else if (ptr == size) + { + send_size += _vcom_rb_block_put(data, (const rt_uint8_t *)&buf[send_size], ptr - send_size); + } + else + { + break; + } + } + } + else + { + while (send_size < size) + { + send_size += _vcom_rb_block_put(data, (rt_uint8_t *)&buf[send_size], size - send_size); + } + } + } + else + { + /* recover dataqueue resources */ + rt_hw_serial_isr(&data->serial,RT_SERIAL_EVENT_TX_DMADONE); + } + + return size; +} +static int _vcom_putc(struct rt_serial_device *serial, char c) +{ + rt_base_t level; + struct ufunction *func; + struct vcom *data; + + func = (struct ufunction*)serial->parent.user_data; + data = (struct vcom*)func->user_data; + + RT_ASSERT(serial != RT_NULL); + + if (data->connected) + { + if(c == '\n' && (serial->parent.open_flag & RT_DEVICE_FLAG_STREAM)) + { + level = rt_hw_interrupt_disable(); + rt_ringbuffer_putchar_force(&data->tx_ringbuffer, '\r'); + rt_hw_interrupt_enable(level); + rt_event_send(&data->tx_event, CDC_TX_HAS_DATE); + } + level = rt_hw_interrupt_disable(); + rt_ringbuffer_putchar_force(&data->tx_ringbuffer, c); + rt_hw_interrupt_enable(level); + rt_event_send(&data->tx_event, CDC_TX_HAS_DATE); + } + + return 1; +} + +static const struct rt_uart_ops usb_vcom_ops = +{ + _vcom_configure, + _vcom_control, + _vcom_putc, + _vcom_getc, + _vcom_tx +}; + +/* Vcom Tx Thread */ +static void vcom_tx_thread_entry(void* parameter) +{ + rt_base_t level; + rt_uint32_t res; + struct ufunction *func = (struct ufunction *)parameter; + struct vcom *data = (struct vcom*)func->user_data; + rt_uint8_t ch[CDC_BULKIN_MAXSIZE]; + + while (1) + { + if + ( + (rt_event_recv(&data->tx_event, CDC_TX_HAS_DATE, + RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, + RT_WAITING_FOREVER, &res) != RT_EOK) || + (!(res & CDC_TX_HAS_DATE)) + ) + { + continue; + } + if(!(res & CDC_TX_HAS_DATE)) + { + continue; + } + while(rt_ringbuffer_data_len(&data->tx_ringbuffer)) + { + level = rt_hw_interrupt_disable(); + res = rt_ringbuffer_get(&data->tx_ringbuffer, ch, CDC_BULKIN_MAXSIZE); + rt_hw_interrupt_enable(level); + + if(!res) + { + continue; + } + if (!data->connected) + { +#ifdef RT_USING_SERIAL_V1 +#ifndef VCOM_TX_USE_DMA + if(data->serial.parent.open_flag & RT_DEVICE_FLAG_INT_TX) +#else + if(data->serial.parent.open_flag & RT_DEVICE_FLAG_DMA_TX) +#endif +#endif +#ifdef RT_USING_SERIAL_V2 + if(data->serial.parent.open_flag & RT_DEVICE_FLAG_TX_BLOCKING) +#endif + { + /* drop msg */ +#ifndef VCOM_TX_USE_DMA + rt_hw_serial_isr(&data->serial,RT_SERIAL_EVENT_TX_DONE); +#else + rt_hw_serial_isr(&data->serial,RT_SERIAL_EVENT_TX_DMADONE); +#endif + } + continue; + } + rt_completion_init(&data->wait); + data->ep_in->request.buffer = ch; + data->ep_in->request.size = res; + + data->ep_in->request.req_type = UIO_REQUEST_WRITE; + + rt_usbd_io_request(func->device, data->ep_in, &data->ep_in->request); + + if (rt_completion_wait(&data->wait, VCOM_TX_TIMEOUT) != RT_EOK) + { + LOG_D("vcom tx timeout"); + } +#ifdef RT_USING_SERIAL_V1 +#ifndef VCOM_TX_USE_DMA + if(data->serial.parent.open_flag & RT_DEVICE_FLAG_INT_TX) +#else + if(data->serial.parent.open_flag & RT_DEVICE_FLAG_DMA_TX) +#endif +#endif +#ifdef RT_USING_SERIAL_V2 + if(data->serial.parent.open_flag & RT_DEVICE_FLAG_TX_BLOCKING) +#endif + { +#ifndef VCOM_TX_USE_DMA + rt_hw_serial_isr(&data->serial,RT_SERIAL_EVENT_TX_DONE); +#else + rt_hw_serial_isr(&data->serial,RT_SERIAL_EVENT_TX_DMADONE); +#endif + rt_event_send(&data->tx_event, CDC_TX_HAS_SPACE); + } + } + + } +} + +static void rt_usb_vcom_init(struct ufunction *func) +{ + rt_err_t result = RT_EOK; + struct serial_configure config; + struct vcom *data = (struct vcom*)func->user_data; + + /* initialize ring buffer */ + rt_ringbuffer_init(&data->rx_ringbuffer, data->rx_rbp, CDC_RX_BUFSIZE); + rt_ringbuffer_init(&data->tx_ringbuffer, data->tx_rbp, CDC_TX_BUFSIZE); + + rt_event_init(&data->tx_event, "vcom", RT_IPC_FLAG_FIFO); + + config.baud_rate = BAUD_RATE_115200; + config.data_bits = DATA_BITS_8; + config.stop_bits = STOP_BITS_1; + config.parity = PARITY_NONE; + config.bit_order = BIT_ORDER_LSB; + config.invert = NRZ_NORMAL; +#if defined(RT_USING_SERIAL_V1) + config.bufsz = CDC_RX_BUFSIZE; +#elif defined(RT_USING_SERIAL_V2) + config.rx_bufsz = CDC_RX_BUFSIZE; + config.tx_bufsz = CDC_TX_BUFSIZE; +#endif + data->serial.ops = &usb_vcom_ops; + data->serial.serial_rx = RT_NULL; + data->serial.config = config; + + /* register vcom device */ + rt_hw_serial_register(&data->serial, VCOM_DEVICE, +#ifndef VCOM_TX_USE_DMA + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX, +#else + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_DMA_TX, +#endif + func); + + /* init usb device thread */ + rt_thread_init(&vcom_thread, "vcom", + vcom_tx_thread_entry, func, + vcom_thread_stack, VCOM_TASK_STK_SIZE, + 16, 20); + result = rt_thread_startup(&vcom_thread); + RT_ASSERT(result == RT_EOK); +} +struct udclass vcom_class = +{ + .rt_usbd_function_create = rt_usbd_function_cdc_create +}; + +int rt_usbd_vcom_class_register(void) +{ + rt_usbd_class_register(&vcom_class); + return 0; +} +INIT_PREV_EXPORT(rt_usbd_vcom_class_register); + +#endif diff --git a/components/legacy/usb/usbdevice/class/ecm.c b/components/legacy/usb/usbdevice/class/ecm.c new file mode 100644 index 0000000000..d856a1e332 --- /dev/null +++ b/components/legacy/usb/usbdevice/class/ecm.c @@ -0,0 +1,682 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2017-11-19 ZYH first version + * 2019-06-10 ZYH fix hotplug + */ + +#include <rtdevice.h> +#ifdef RT_USB_DEVICE_ECM +#include "cdc.h" + +#define DBG_LEVEL DBG_WARNING +#define DBG_SECTION_NAME "ECM" +#include <rtdbg.h> + +/* RT-Thread LWIP ethernet interface */ +#include <netif/ethernetif.h> + +#ifndef USB_ETH_MTU +#define USB_ETH_MTU 1514 +#endif +#define MAX_ADDR_LEN 6 +#define ECM_INTF_STR_INDEX 10 + +struct rt_ecm_eth +{ + /* inherit from ethernet device */ + struct eth_device parent; + struct ufunction * func; + struct cdc_eps eps; + /* interface address info */ + rt_uint8_t host_addr[MAX_ADDR_LEN]; + rt_uint8_t dev_addr[MAX_ADDR_LEN]; + + rt_align(4) + rt_uint8_t rx_pool[512]; + rt_align(4) + rt_size_t rx_size; + rt_align(4) + rt_size_t rx_offset; + rt_align(4) + char rx_buffer[USB_ETH_MTU]; + char tx_buffer[USB_ETH_MTU]; + + struct rt_semaphore tx_buffer_free; + +}; +typedef struct rt_ecm_eth * rt_ecm_eth_t; + +rt_align(4) +static struct udevice_descriptor _dev_desc = +{ + USB_DESC_LENGTH_DEVICE, /* bLength */ + USB_DESC_TYPE_DEVICE, /* type */ + USB_BCD_VERSION, /* bcdUSB */ + USB_CLASS_CDC, /* bDeviceClass */ + USB_CDC_SUBCLASS_ETH, /* bDeviceSubClass */ + USB_CDC_PROTOCOL_NONE, /* bDeviceProtocol */ + 0x40, /* bMaxPacketSize0 */ + _VENDOR_ID, /* idVendor */ + _PRODUCT_ID, /* idProduct */ + USB_BCD_DEVICE, /* bcdDevice */ + USB_STRING_MANU_INDEX, /* iManufacturer */ + USB_STRING_PRODUCT_INDEX, /* iProduct */ + USB_STRING_SERIAL_INDEX, /* iSerialNumber */ + USB_DYNAMIC /* bNumConfigurations */ +}; + +/* communcation interface descriptor */ +rt_align(4) +const static struct ucdc_eth_descriptor _comm_desc = +{ +#ifdef RT_USB_DEVICE_COMPOSITE + /* Interface Association Descriptor */ + { + USB_DESC_LENGTH_IAD, + USB_DESC_TYPE_IAD, + USB_DYNAMIC, + 0x02, + USB_CDC_CLASS_COMM, + USB_CDC_SUBCLASS_ETH, + USB_CDC_PROTOCOL_NONE, + 0x00, + }, +#endif + /* Interface Descriptor */ + { + USB_DESC_LENGTH_INTERFACE, + USB_DESC_TYPE_INTERFACE, + USB_DYNAMIC, + 0x00, + 0x01, + USB_CDC_CLASS_COMM, + USB_CDC_SUBCLASS_ETH, + USB_CDC_PROTOCOL_NONE, +#ifdef RT_USB_DEVICE_COMPOSITE + ECM_INTF_STR_INDEX, +#else + 0x00, +#endif + }, + /* Header Functional Descriptor */ + { + sizeof(struct ucdc_header_descriptor), + USB_CDC_CS_INTERFACE, + USB_CDC_SCS_HEADER, + 0x0110, + }, + /* Union Functional Descriptor */ + { + sizeof(struct ucdc_union_descriptor), + USB_CDC_CS_INTERFACE, + USB_CDC_SCS_UNION, + USB_DYNAMIC, + USB_DYNAMIC, + }, + /* Abstract Control Management Functional Descriptor */ + { + sizeof(struct ucdc_enet_descriptor), + USB_CDC_CS_INTERFACE, + USB_CDC_SCS_ETH, + USB_STRING_SERIAL_INDEX, + {0,0,0,0}, + USB_ETH_MTU, + 0x00, + 0x00, + }, + /* Endpoint Descriptor */ + { + USB_DESC_LENGTH_ENDPOINT, + USB_DESC_TYPE_ENDPOINT, + USB_DIR_IN | USB_DYNAMIC, + USB_EP_ATTR_INT, + 0x08, + 0xFF, + }, +}; + +/* data interface descriptor */ +rt_align(4) +const static struct ucdc_data_descriptor _data_desc = +{ + /* interface descriptor */ + { + USB_DESC_LENGTH_INTERFACE, + USB_DESC_TYPE_INTERFACE, + USB_DYNAMIC, + 0x00, + 0x02, + USB_CDC_CLASS_DATA, + USB_CDC_SUBCLASS_ETH, + 0x00, + 0x00, + }, + /* endpoint, bulk out */ + { + USB_DESC_LENGTH_ENDPOINT, + USB_DESC_TYPE_ENDPOINT, + USB_DIR_OUT | USB_DYNAMIC, + USB_EP_ATTR_BULK, + USB_DYNAMIC, + 0x00, + }, + /* endpoint, bulk in */ + { + USB_DESC_LENGTH_ENDPOINT, + USB_DESC_TYPE_ENDPOINT, + USB_DYNAMIC | USB_DIR_IN, + USB_EP_ATTR_BULK, + USB_DYNAMIC, + 0x00, + }, +}; + +rt_align(4) +const static char* _ustring[] = +{ + "Language", /* LANGID */ + "RT-Thread Team.", /* MANU */ + "RT-Thread ECM device", /* PRODUCT */ + "3497F694ECAB", /* SERIAL (MAC)*/ + "Configuration", /* CONFIG */ + "Interface", /* INTERFACE */ +}; + +rt_align(4) +//FS and HS needed +static struct usb_qualifier_descriptor dev_qualifier = +{ + sizeof(dev_qualifier), //bLength + USB_DESC_TYPE_DEVICEQUALIFIER, //bDescriptorType + 0x0200, //bcdUSB + USB_CLASS_CDC, //bDeviceClass + USB_CDC_SUBCLASS_ETH, //bDeviceSubClass + USB_CDC_PROTOCOL_NONE, //bDeviceProtocol + 64, //bMaxPacketSize0 + 0x01, //bNumConfigurations + 0, +}; + +static rt_err_t _cdc_send_notifi(ufunction_t func,ucdc_notification_code_t notifi,rt_uint16_t wValue,rt_uint16_t wLength) +{ + static struct ucdc_management_element_notifications _notifi; + cdc_eps_t eps; + RT_ASSERT(func!=RT_NULL) + eps = &((rt_ecm_eth_t)func->user_data)->eps; + _notifi.bmRequestType = 0xA1; + _notifi.bNotificatinCode = notifi; + _notifi.wValue = wValue; + _notifi.wLength = wLength; + + eps->ep_cmd->request.buffer = (void *)&_notifi; + eps->ep_cmd->request.size = 8; + eps->ep_cmd->request.req_type = UIO_REQUEST_WRITE; + rt_usbd_io_request(func->device, eps->ep_cmd, &eps->ep_cmd->request); + return RT_EOK; +} + + +static rt_err_t _ecm_set_eth_packet_filter(ufunction_t func, ureq_t setup) +{ + rt_ecm_eth_t _ecm_eth = (rt_ecm_eth_t)func->user_data; + dcd_ep0_send_status(func->device->dcd); + + /* send link up. */ + eth_device_linkchange(&_ecm_eth->parent, RT_TRUE); + _cdc_send_notifi(func, UCDC_NOTIFI_NETWORK_CONNECTION, 1, 0); + +#ifdef LWIP_USING_DHCPD + extern void dhcpd_start(const char *netif_name); + dhcpd_start("u0"); +#endif + + return RT_EOK; +} +/** + * This function will handle rndis interface request. + * + * @param device the usb device object. + * @param setup the setup request. + * + * @return RT_EOK on successful. + */ +static rt_err_t _interface_handler(ufunction_t func, ureq_t setup) +{ + RT_ASSERT(func != RT_NULL); + RT_ASSERT(setup != RT_NULL); + + switch(setup->bRequest) + { + case CDC_SET_ETH_PACKET_FILTER: + LOG_D("CDC_SET_ETH_PACKET_FILTER"); + _ecm_set_eth_packet_filter(func, setup); + break; + default: + LOG_E("Unknow setup->bRequest: 0x%02X", setup->bRequest); + break; + } + return RT_EOK; +} + +/** + * This function will handle rndis bulk in endpoint request. + * + * @param device the usb device object. + * @param size request size. + * + * @return RT_EOK. + */ + +static rt_err_t _ep_in_handler(ufunction_t func, rt_size_t size) +{ + rt_ecm_eth_t ecm_device = (rt_ecm_eth_t)func->user_data; + rt_sem_release(&ecm_device->tx_buffer_free); + return RT_EOK; +} + +/** + * This function will handle RNDIS bulk out endpoint request. + * + * @param device the usb device object. + * @param size request size. + * + * @return RT_EOK. + */ +static rt_err_t _ep_out_handler(ufunction_t func, rt_size_t size) +{ + rt_ecm_eth_t ecm_device = (rt_ecm_eth_t)func->user_data; + rt_memcpy((void *)(ecm_device->rx_buffer + ecm_device->rx_offset),ecm_device->rx_pool,size); + ecm_device->rx_offset += size; + if(size < EP_MAXPACKET(ecm_device->eps.ep_out)) + { + ecm_device->rx_size = ecm_device->rx_offset; + ecm_device->rx_offset = 0; + eth_device_ready(&ecm_device->parent); + + }else + { + ecm_device->eps.ep_out->request.buffer = ecm_device->eps.ep_out->buffer; + ecm_device->eps.ep_out->request.size = EP_MAXPACKET(ecm_device->eps.ep_out); + ecm_device->eps.ep_out->request.req_type = UIO_REQUEST_READ_BEST; + rt_usbd_io_request(ecm_device->func->device, ecm_device->eps.ep_out, &ecm_device->eps.ep_out->request); + } + + + return RT_EOK; +} +static rt_err_t rt_ecm_eth_init(rt_device_t dev) +{ + return RT_EOK; +} + +static rt_err_t rt_ecm_eth_open(rt_device_t dev, rt_uint16_t oflag) +{ + return RT_EOK; +} + +static rt_err_t rt_ecm_eth_close(rt_device_t dev) +{ + return RT_EOK; +} + +static rt_ssize_t rt_ecm_eth_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size) +{ + rt_set_errno(-RT_ENOSYS); + return 0; +} + +static rt_ssize_t rt_ecm_eth_write (rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size) +{ + rt_set_errno(-RT_ENOSYS); + return 0; +} +static rt_err_t rt_ecm_eth_control(rt_device_t dev, int cmd, void *args) +{ + rt_ecm_eth_t ecm_eth_dev = (rt_ecm_eth_t)dev; + switch(cmd) + { + case NIOCTL_GADDR: + /* get mac address */ + if(args) rt_memcpy(args, ecm_eth_dev->dev_addr, MAX_ADDR_LEN); + else return -RT_ERROR; + break; + + default : + break; + } + + return RT_EOK; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops ecm_device_ops = +{ + rt_ecm_eth_init, + rt_ecm_eth_open, + rt_ecm_eth_close, + rt_ecm_eth_read, + rt_ecm_eth_write, + rt_ecm_eth_control +}; +#endif + +struct pbuf *rt_ecm_eth_rx(rt_device_t dev) +{ + struct pbuf* p = RT_NULL; + rt_uint32_t offset = 0; + rt_ecm_eth_t ecm_eth_dev = (rt_ecm_eth_t)dev; + if(ecm_eth_dev->rx_size != 0) + { + /* allocate buffer */ + p = pbuf_alloc(PBUF_RAW, ecm_eth_dev->rx_size, PBUF_RAM); + if (p != RT_NULL) + { + struct pbuf* q; + + for (q = p; q != RT_NULL; q= q->next) + { + /* Copy the received frame into buffer from memory pointed by the current ETHERNET DMA Rx descriptor */ + rt_memcpy(q->payload, + (rt_uint8_t *)((ecm_eth_dev->rx_buffer) + offset), + q->len); + offset += q->len; + } + } + } + + { + if(ecm_eth_dev->func->device->state == USB_STATE_CONFIGURED) + { + ecm_eth_dev->rx_size = 0; + ecm_eth_dev->rx_offset = 0; + ecm_eth_dev->eps.ep_out->request.buffer = ecm_eth_dev->eps.ep_out->buffer; + ecm_eth_dev->eps.ep_out->request.size = EP_MAXPACKET(ecm_eth_dev->eps.ep_out); + ecm_eth_dev->eps.ep_out->request.req_type = UIO_REQUEST_READ_BEST; + rt_usbd_io_request(ecm_eth_dev->func->device, ecm_eth_dev->eps.ep_out, &ecm_eth_dev->eps.ep_out->request); + } + } + + return p; +} + +rt_err_t rt_ecm_eth_tx(rt_device_t dev, struct pbuf* p) +{ + struct pbuf* q; + char * pbuffer; + rt_err_t result = RT_EOK; + rt_ecm_eth_t ecm_eth_dev = (rt_ecm_eth_t)dev; + + if(!ecm_eth_dev->parent.link_status) + { + LOG_D("linkdown, drop pkg"); + return RT_EOK; + } + + if(p->tot_len > USB_ETH_MTU) + { + LOG_W("ECM MTU is:%d, but the send packet size is %d", + USB_ETH_MTU, p->tot_len); + p->tot_len = USB_ETH_MTU; + } + + result = rt_sem_take(&ecm_eth_dev->tx_buffer_free, rt_tick_from_millisecond(1000)); + if(result != RT_EOK) + { + LOG_W("wait for buffer free timeout"); + /* if cost 1s to wait send done it said that connection is close . drop it */ + rt_sem_release(&ecm_eth_dev->tx_buffer_free); + return result; + } + + pbuffer = (char *)&ecm_eth_dev->tx_buffer; + for (q = p; q != NULL; q = q->next) + { + rt_memcpy(pbuffer, q->payload, q->len); + pbuffer += q->len; + } + + { + if(ecm_eth_dev->func->device->state == USB_STATE_CONFIGURED) + { + ecm_eth_dev->eps.ep_in->request.buffer = (void *)&ecm_eth_dev->tx_buffer; + ecm_eth_dev->eps.ep_in->request.size = p->tot_len; + ecm_eth_dev->eps.ep_in->request.req_type = UIO_REQUEST_WRITE; + rt_usbd_io_request(ecm_eth_dev->func->device, ecm_eth_dev->eps.ep_in, &ecm_eth_dev->eps.ep_in->request); + } + } + + return result; +} +/** + * This function will handle RNDIS interrupt in endpoint request. + * + * @param device the usb device object. + * @param size request size. + * + * @return RT_EOK. + */ +static rt_err_t _ep_cmd_handler(ufunction_t func, rt_size_t size) +{ + return RT_EOK; +} + +/** + * This function will run cdc class, it will be called on handle set configuration request. + * + * @param device the usb device object. + * + * @return RT_EOK on successful. + */ +static rt_err_t _function_enable(ufunction_t func) +{ + cdc_eps_t eps; + rt_ecm_eth_t ecm_device = (rt_ecm_eth_t)func->user_data; + + LOG_D("plugged in"); + + eps = (cdc_eps_t)&ecm_device->eps; + eps->ep_out->buffer = ecm_device->rx_pool; + + /* reset eth rx tx */ + ecm_device->rx_size = 0; + ecm_device->rx_offset = 0; + + eps->ep_out->request.buffer = (void *)eps->ep_out->buffer; + eps->ep_out->request.size = EP_MAXPACKET(eps->ep_out); + eps->ep_out->request.req_type = UIO_REQUEST_READ_BEST; + rt_usbd_io_request(func->device, eps->ep_out, &eps->ep_out->request); + return RT_EOK; +} + +/** + * This function will stop cdc class, it will be called on handle set configuration request. + * + * @param device the usb device object. + * + * @return RT_EOK on successful. + */ +static rt_err_t _function_disable(ufunction_t func) +{ + LOG_D("plugged out"); + + eth_device_linkchange(&((rt_ecm_eth_t)func->user_data)->parent, RT_FALSE); + + /* reset eth rx tx */ + ((rt_ecm_eth_t)func->user_data)->rx_size = 0; + ((rt_ecm_eth_t)func->user_data)->rx_offset = 0; + + return RT_EOK; +} + + +static struct ufunction_ops ops = +{ + _function_enable, + _function_disable, + RT_NULL, +}; + +/** + * This function will configure cdc descriptor. + * + * @param comm the communication interface number. + * @param data the data interface number. + * + * @return RT_EOK on successful. + */ +static rt_err_t _cdc_descriptor_config(ucdc_comm_desc_t comm, rt_uint8_t cintf_nr, ucdc_data_desc_t data, rt_uint8_t dintf_nr, rt_uint8_t device_is_hs) +{ + comm->call_mgmt_desc.data_interface = dintf_nr; + comm->union_desc.master_interface = cintf_nr; + comm->union_desc.slave_interface0 = dintf_nr; +#ifdef RT_USB_DEVICE_COMPOSITE + comm->iad_desc.bFirstInterface = cintf_nr; +#endif + data->ep_out_desc.wMaxPacketSize = device_is_hs ? 512 : 64; + data->ep_in_desc.wMaxPacketSize = device_is_hs ? 512 : 64; + return RT_EOK; +} + + + +/** + * This function will create a cdc ecm class instance. + * + * @param device the usb device object. + * + * @return RT_EOK on successful. + */ +ufunction_t rt_usbd_function_ecm_create(udevice_t device) +{ + ufunction_t cdc; + rt_ecm_eth_t _ecm_eth; + cdc_eps_t eps; + uintf_t intf_comm, intf_data; + ualtsetting_t comm_setting, data_setting; + ucdc_data_desc_t data_desc; + ucdc_eth_desc_t comm_desc; + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + + /* set usb device string description */ +#ifdef RT_USB_DEVICE_COMPOSITE + rt_usbd_device_set_interface_string(device, ECM_INTF_STR_INDEX, _ustring[2]); +#else + rt_usbd_device_set_string(device, _ustring); +#endif + /* create a cdc class */ + cdc = rt_usbd_function_new(device, &_dev_desc, &ops); + rt_usbd_device_set_qualifier(device, &dev_qualifier); + _ecm_eth= rt_malloc(sizeof(struct rt_ecm_eth)); + RT_ASSERT(_ecm_eth != RT_NULL); + rt_memset(_ecm_eth, 0, sizeof(struct rt_ecm_eth)); + cdc->user_data = _ecm_eth; + + _ecm_eth->func = cdc; + /* create a cdc class endpoints collection */ + eps = &_ecm_eth->eps; + /* create a cdc communication interface and a cdc data interface */ + intf_comm = rt_usbd_interface_new(device, _interface_handler); + intf_data = rt_usbd_interface_new(device, _interface_handler); + + /* create a communication alternate setting and a data alternate setting */ + comm_setting = rt_usbd_altsetting_new(sizeof(struct ucdc_eth_descriptor)); + data_setting = rt_usbd_altsetting_new(sizeof(struct ucdc_data_descriptor)); + + /* config desc in alternate setting */ + rt_usbd_altsetting_config_descriptor(comm_setting, &_comm_desc, + (rt_off_t)&((ucdc_eth_desc_t)0)->intf_desc); + rt_usbd_altsetting_config_descriptor(data_setting, &_data_desc, 0); + /* configure the cdc interface descriptor */ + _cdc_descriptor_config(comm_setting->desc, intf_comm->intf_num, data_setting->desc, intf_data->intf_num, device->dcd->device_is_hs); + + /* create a command endpoint */ + comm_desc = (ucdc_eth_desc_t)comm_setting->desc; + eps->ep_cmd = rt_usbd_endpoint_new(&comm_desc->ep_desc, _ep_cmd_handler); + /* add the command endpoint to the cdc communication interface */ + rt_usbd_altsetting_add_endpoint(comm_setting, eps->ep_cmd); + + /* add the communication alternate setting to the communication interface, + then set default setting of the interface */ + rt_usbd_interface_add_altsetting(intf_comm, comm_setting); + rt_usbd_set_altsetting(intf_comm, 0); + /* add the communication interface to the cdc class */ + rt_usbd_function_add_interface(cdc, intf_comm); + + /* create a bulk in and a bulk out endpoint */ + data_desc = (ucdc_data_desc_t)data_setting->desc; + eps->ep_out = rt_usbd_endpoint_new(&data_desc->ep_out_desc, _ep_out_handler); + eps->ep_in = rt_usbd_endpoint_new(&data_desc->ep_in_desc, _ep_in_handler); + + /* add the bulk out and bulk in endpoints to the data alternate setting */ + rt_usbd_altsetting_add_endpoint(data_setting, eps->ep_in); + rt_usbd_altsetting_add_endpoint(data_setting, eps->ep_out); + + /* add the data alternate setting to the data interface + then set default setting of the interface */ + rt_usbd_interface_add_altsetting(intf_data, data_setting); + rt_usbd_set_altsetting(intf_data, 0); + + /* add the cdc data interface to cdc class */ + rt_usbd_function_add_interface(cdc, intf_data); + + rt_sem_init(&_ecm_eth->tx_buffer_free, "ue_tx", 1, RT_IPC_FLAG_FIFO); + /* OUI 00-00-00, only for test. */ + _ecm_eth->dev_addr[0] = 0x34; + _ecm_eth->dev_addr[1] = 0x97; + _ecm_eth->dev_addr[2] = 0xF6; + /* generate random MAC. */ + _ecm_eth->dev_addr[3] = 0x94;//*(const rt_uint8_t *)(0x1fff7a10); + _ecm_eth->dev_addr[4] = 0xEC;//*(const rt_uint8_t *)(0x1fff7a14); + _ecm_eth->dev_addr[5] = 0xAC;//(const rt_uint8_t *)(0x1fff7a18); + /* OUI 00-00-00, only for test. */ + _ecm_eth->host_addr[0] = 0x34; + _ecm_eth->host_addr[1] = 0x97; + _ecm_eth->host_addr[2] = 0xF6; + /* generate random MAC. */ + _ecm_eth->host_addr[3] = 0x94;//*(const rt_uint8_t *)(0x1fff7a10); + _ecm_eth->host_addr[4] = 0xEC;//*(const rt_uint8_t *)(0x1fff7a14); + _ecm_eth->host_addr[5] = 0xAB;//*(const rt_uint8_t *)(0x1fff7a18); + +#ifdef RT_USING_DEVICE_OPS + _ecm_eth->parent.parent.ops = &ecm_device_ops; +#else + _ecm_eth->parent.parent.init = rt_ecm_eth_init; + _ecm_eth->parent.parent.open = rt_ecm_eth_open; + _ecm_eth->parent.parent.close = rt_ecm_eth_close; + _ecm_eth->parent.parent.read = rt_ecm_eth_read; + _ecm_eth->parent.parent.write = rt_ecm_eth_write; + _ecm_eth->parent.parent.control = rt_ecm_eth_control; +#endif + _ecm_eth->parent.parent.user_data = device; + + _ecm_eth->parent.eth_rx = rt_ecm_eth_rx; + _ecm_eth->parent.eth_tx = rt_ecm_eth_tx; + /* register eth device */ + eth_device_init(&_ecm_eth->parent, "u0"); + + /* send link up. */ + eth_device_linkchange(&_ecm_eth->parent, RT_FALSE); + + return cdc; +} + +struct udclass ecm_class = +{ + .rt_usbd_function_create = rt_usbd_function_ecm_create +}; + +int rt_usbd_ecm_class_register(void) +{ + rt_usbd_class_register(&ecm_class); + return 0; +} +INIT_PREV_EXPORT(rt_usbd_ecm_class_register); + +#endif /* RT_USB_DEVICE_ECM */ diff --git a/components/legacy/usb/usbdevice/class/hid.c b/components/legacy/usb/usbdevice/class/hid.c new file mode 100644 index 0000000000..b0c7e63e00 --- /dev/null +++ b/components/legacy/usb/usbdevice/class/hid.c @@ -0,0 +1,756 @@ +/* + * File : hid.c + * COPYRIGHT (C) 2006 - 2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2017-03-13 Urey the first version + * 2017-11-16 ZYH Update to common hid + */ + +#include <rthw.h> +#include <rtdevice.h> +#include "drivers/usb_common.h" +#include "drivers/usb_device.h" +#include "hid.h" + +#ifdef RT_USB_DEVICE_HID + +#define DBG_TAG "usbdevice.hid" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + +#define HID_INTF_STR_INDEX 7 +struct hid_s +{ + struct rt_device parent; + struct ufunction *func; + uep_t ep_in; + uep_t ep_out; + int status; + rt_uint8_t protocol; + rt_uint8_t report_buf[MAX_REPORT_SIZE]; + struct rt_messagequeue hid_mq; +}; + +/* CustomHID_ConfigDescriptor */ +rt_align(4) +const rt_uint8_t _report_desc[]= +{ +#ifdef RT_USB_DEVICE_HID_KEYBOARD + USAGE_PAGE(1), 0x01, + USAGE(1), 0x06, + COLLECTION(1), 0x01, + REPORT_ID(1), HID_REPORT_ID_KEYBOARD1, + + USAGE_PAGE(1), 0x07, + USAGE_MINIMUM(1), 0xE0, + USAGE_MAXIMUM(1), 0xE7, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x01, + REPORT_SIZE(1), 0x01, + REPORT_COUNT(1), 0x08, + INPUT(1), 0x02, + REPORT_COUNT(1), 0x01, + REPORT_SIZE(1), 0x08, + INPUT(1), 0x01, + + + REPORT_COUNT(1), 0x05, + REPORT_SIZE(1), 0x01, + USAGE_PAGE(1), 0x08, + USAGE_MINIMUM(1), 0x01, + USAGE_MAXIMUM(1), 0x05, + OUTPUT(1), 0x02, + REPORT_COUNT(1), 0x01, + REPORT_SIZE(1), 0x03, + OUTPUT(1), 0x01, + + + REPORT_COUNT(1), 0x06, + REPORT_SIZE(1), 0x08, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x65, + USAGE_PAGE(1), 0x07, + USAGE_MINIMUM(1), 0x00, + USAGE_MAXIMUM(1), 0x65, + INPUT(1), 0x00, + END_COLLECTION(0), +#if RT_USB_DEVICE_HID_KEYBOARD_NUMBER>1 + /****keyboard2*****/ + USAGE_PAGE(1), 0x01, + USAGE(1), 0x06, + COLLECTION(1), 0x01, + REPORT_ID(1), HID_REPORT_ID_KEYBOARD2, + + USAGE_PAGE(1), 0x07, + USAGE_MINIMUM(1), 0xE0, + USAGE_MAXIMUM(1), 0xE7, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x01, + REPORT_SIZE(1), 0x01, + REPORT_COUNT(1), 0x08, + INPUT(1), 0x02, + REPORT_COUNT(1), 0x01, + REPORT_SIZE(1), 0x08, + INPUT(1), 0x01, + + REPORT_COUNT(1), 0x06, + REPORT_SIZE(1), 0x08, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x65, + USAGE_PAGE(1), 0x07, + USAGE_MINIMUM(1), 0x00, + USAGE_MAXIMUM(1), 0x65, + INPUT(1), 0x00, + END_COLLECTION(0), +#if RT_USB_DEVICE_HID_KEYBOARD_NUMBER>2 + USAGE_PAGE(1), 0x01, + USAGE(1), 0x06, + COLLECTION(1), 0x01, + REPORT_ID(1), HID_REPORT_ID_KEYBOARD3, + + USAGE_PAGE(1), 0x07, + USAGE_MINIMUM(1), 0xE0, + USAGE_MAXIMUM(1), 0xE7, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x01, + REPORT_SIZE(1), 0x01, + REPORT_COUNT(1), 0x08, + INPUT(1), 0x02, + REPORT_COUNT(1), 0x01, + REPORT_SIZE(1), 0x08, + INPUT(1), 0x01, + + REPORT_COUNT(1), 0x06, + REPORT_SIZE(1), 0x08, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x65, + USAGE_PAGE(1), 0x07, + USAGE_MINIMUM(1), 0x00, + USAGE_MAXIMUM(1), 0x65, + INPUT(1), 0x00, + END_COLLECTION(0), +#if RT_USB_DEVICE_HID_KEYBOARD_NUMBER>3 + USAGE_PAGE(1), 0x01, + USAGE(1), 0x06, + COLLECTION(1), 0x01, + REPORT_ID(1), HID_REPORT_ID_KEYBOARD4, + + USAGE_PAGE(1), 0x07, + USAGE_MINIMUM(1), 0xE0, + USAGE_MAXIMUM(1), 0xE7, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x01, + REPORT_SIZE(1), 0x01, + REPORT_COUNT(1), 0x08, + INPUT(1), 0x02, + REPORT_COUNT(1), 0x01, + REPORT_SIZE(1), 0x08, + INPUT(1), 0x01, + + REPORT_COUNT(1), 0x06, + REPORT_SIZE(1), 0x08, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x65, + USAGE_PAGE(1), 0x07, + USAGE_MINIMUM(1), 0x00, + USAGE_MAXIMUM(1), 0x65, + INPUT(1), 0x00, + END_COLLECTION(0), +#endif +#endif +#endif +#endif + // Media Control +#ifdef RT_USB_DEVICE_HID_MEDIA + USAGE_PAGE(1), 0x0C, + USAGE(1), 0x01, + COLLECTION(1), 0x01, + REPORT_ID(1), HID_REPORT_ID_MEDIA, + USAGE_PAGE(1), 0x0C, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x01, + REPORT_SIZE(1), 0x01, + REPORT_COUNT(1), 0x07, + USAGE(1), 0xB5, // Next Track + USAGE(1), 0xB6, // Previous Track + USAGE(1), 0xB7, // Stop + USAGE(1), 0xCD, // Play / Pause + USAGE(1), 0xE2, // Mute + USAGE(1), 0xE9, // Volume Up + USAGE(1), 0xEA, // Volume Down + INPUT(1), 0x02, // Input (Data, Variable, Absolute) + REPORT_COUNT(1), 0x01, + INPUT(1), 0x01, + END_COLLECTION(0), +#endif + +#ifdef RT_USB_DEVICE_HID_GENERAL + USAGE_PAGE(1), 0x8c, + USAGE(1), 0x01, + COLLECTION(1), 0x01, + REPORT_ID(1), HID_REPORT_ID_GENERAL, + + REPORT_COUNT(1), RT_USB_DEVICE_HID_GENERAL_IN_REPORT_LENGTH, + USAGE(1), 0x03, + REPORT_SIZE(1), 0x08, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0xFF, + INPUT(1), 0x02, + + REPORT_COUNT(1), RT_USB_DEVICE_HID_GENERAL_OUT_REPORT_LENGTH, + USAGE(1), 0x04, + REPORT_SIZE(1), 0x08, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0xFF, + OUTPUT(1), 0x02, + END_COLLECTION(0), +#endif +#ifdef RT_USB_DEVICE_HID_MOUSE + USAGE_PAGE(1), 0x01, // Generic Desktop + USAGE(1), 0x02, // Mouse + COLLECTION(1), 0x01, // Application + USAGE(1), 0x01, // Pointer + COLLECTION(1), 0x00, // Physical + REPORT_ID(1), HID_REPORT_ID_MOUSE, + REPORT_COUNT(1), 0x03, + REPORT_SIZE(1), 0x01, + USAGE_PAGE(1), 0x09, // Buttons + USAGE_MINIMUM(1), 0x1, + USAGE_MAXIMUM(1), 0x3, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x01, + INPUT(1), 0x02, + REPORT_COUNT(1), 0x01, + REPORT_SIZE(1), 0x05, + INPUT(1), 0x01, + REPORT_COUNT(1), 0x03, + REPORT_SIZE(1), 0x08, + USAGE_PAGE(1), 0x01, + USAGE(1), 0x30, // X + USAGE(1), 0x31, // Y + USAGE(1), 0x38, // scroll + LOGICAL_MINIMUM(1), 0x81, + LOGICAL_MAXIMUM(1), 0x7f, + INPUT(1), 0x06, + END_COLLECTION(0), + END_COLLECTION(0), +#endif +}; /* CustomHID_ReportDescriptor */ + +rt_align(4) +static struct udevice_descriptor _dev_desc = +{ + USB_DESC_LENGTH_DEVICE, //bLength; + USB_DESC_TYPE_DEVICE, //type; + USB_BCD_VERSION, //bcdUSB; + 0x0, //bDeviceClass; + 0x00, //bDeviceSubClass; + 0x00, //bDeviceProtocol; + 64, //bMaxPacketSize0; + _VENDOR_ID, //idVendor; + _PRODUCT_ID, //idProduct; + USB_BCD_DEVICE, //bcdDevice; + USB_STRING_MANU_INDEX, //iManufacturer; + USB_STRING_PRODUCT_INDEX, //iProduct; + USB_STRING_SERIAL_INDEX, //iSerialNumber; + USB_DYNAMIC, //bNumConfigurations; +}; + +//FS and HS needed +rt_align(4) +static struct usb_qualifier_descriptor dev_qualifier = +{ + sizeof(dev_qualifier), //bLength + USB_DESC_TYPE_DEVICEQUALIFIER, //bDescriptorType + 0x0200, //bcdUSB + 0x0, //bDeviceClass + 0x0, //bDeviceSubClass + 0x50, //bDeviceProtocol + 64, //bMaxPacketSize0 + 0x01, //bNumConfigurations + 0, +}; + + +/* hid interface descriptor */ +rt_align(4) +const static struct uhid_comm_descriptor _hid_comm_desc = +{ +#ifdef RT_USB_DEVICE_COMPOSITE + /* Interface Association Descriptor */ + { + USB_DESC_LENGTH_IAD, + USB_DESC_TYPE_IAD, + USB_DYNAMIC, + 0x01, + 0x03, /* bInterfaceClass: HID */ +#if defined(RT_USB_DEVICE_HID_KEYBOARD)||defined(RT_USB_DEVICE_HID_MOUSE) + USB_HID_SUBCLASS_BOOT, /* bInterfaceSubClass : 1=BOOT, 0=no boot */ +#else + USB_HID_SUBCLASS_NOBOOT, /* bInterfaceSubClass : 1=BOOT, 0=no boot */ +#endif +#if !defined(RT_USB_DEVICE_HID_KEYBOARD)||!defined(RT_USB_DEVICE_HID_MOUSE)||!defined(RT_USB_DEVICE_HID_MEDIA) + USB_HID_PROTOCOL_NONE, /* nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse */ +#elif !defined(RT_USB_DEVICE_HID_MOUSE) + USB_HID_PROTOCOL_KEYBOARD, /* nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse */ +#else + USB_HID_PROTOCOL_MOUSE, /* nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse */ +#endif + 0x00, + }, +#endif + /* Interface Descriptor */ + { + USB_DESC_LENGTH_INTERFACE, + USB_DESC_TYPE_INTERFACE, + USB_DYNAMIC, /* bInterfaceNumber: Number of Interface */ + 0x00, /* bAlternateSetting: Alternate setting */ + 0x02, /* bNumEndpoints */ + 0x03, /* bInterfaceClass: HID */ +#if defined(RT_USB_DEVICE_HID_KEYBOARD)||defined(RT_USB_DEVICE_HID_MOUSE) + USB_HID_SUBCLASS_BOOT, /* bInterfaceSubClass : 1=BOOT, 0=no boot */ +#else + USB_HID_SUBCLASS_NOBOOT, /* bInterfaceSubClass : 1=BOOT, 0=no boot */ +#endif +#if !defined(RT_USB_DEVICE_HID_KEYBOARD)||!defined(RT_USB_DEVICE_HID_MOUSE)||!defined(RT_USB_DEVICE_HID_MEDIA) + USB_HID_PROTOCOL_NONE, /* nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse */ +#elif !defined(RT_USB_DEVICE_HID_MOUSE) + USB_HID_PROTOCOL_KEYBOARD, /* nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse */ +#else + USB_HID_PROTOCOL_MOUSE, /* nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse */ +#endif +#ifdef RT_USB_DEVICE_COMPOSITE + HID_INTF_STR_INDEX, /* iInterface: Index of string descriptor */ +#else + 0, +#endif + }, + + /* HID Descriptor */ + { + HID_DESCRIPTOR_SIZE, /* bLength: HID Descriptor size */ + HID_DESCRIPTOR_TYPE, /* bDescriptorType: HID */ + 0x0110, /* bcdHID: HID Class Spec release number */ + 0x00, /* bCountryCode: Hardware target country */ + 0x01, /* bNumDescriptors: Number of HID class descriptors to follow */ + { + { + 0x22, /* bDescriptorType */ + sizeof(_report_desc), /* wItemLength: Total length of Report descriptor */ + }, + }, + }, + + /* Endpoint Descriptor IN */ + { + USB_DESC_LENGTH_ENDPOINT, + USB_DESC_TYPE_ENDPOINT, + USB_DYNAMIC | USB_DIR_IN, + USB_EP_ATTR_INT, + 0x40, + 0x0A, + }, + + /* Endpoint Descriptor OUT */ + { + USB_DESC_LENGTH_ENDPOINT, + USB_DESC_TYPE_ENDPOINT, + USB_DYNAMIC | USB_DIR_OUT, + USB_EP_ATTR_INT, + 0x40, + 0x01, + }, +}; + +rt_align(4) +const static char* _ustring[] = +{ + "Language", + "RT-Thread Team.", + "RTT HID-Device", + "32021919830108", + "Configuration", + "Interface", +}; + +static void dump_data(rt_uint8_t *data, rt_size_t size) +{ + rt_size_t i; + for (i = 0; i < size; i++) + { + rt_kprintf("%02x ", *data++); + if ((i + 1) % 8 == 0) + { + rt_kprintf("\n"); + }else if ((i + 1) % 4 == 0){ + rt_kprintf(" "); + } + } +} +static void dump_report(struct hid_report * report) +{ + rt_kprintf("\nHID Recived:"); + rt_kprintf("\nReport ID %02x \n", report->report_id); + dump_data(report->report,report->size); +} + +static rt_err_t _ep_out_handler(ufunction_t func, rt_size_t size) +{ + struct hid_s *data; + struct hid_report report; + RT_ASSERT(func != RT_NULL); + RT_ASSERT(func->device != RT_NULL); + data = (struct hid_s *) func->user_data; + + if(size != 0) + { + rt_memcpy((void *)&report,(void*)data->ep_out->buffer,size); + report.size = size-1; + rt_mq_send(&data->hid_mq,(void *)&report,sizeof(report)); + } + + data->ep_out->request.buffer = data->ep_out->buffer; + data->ep_out->request.size = EP_MAXPACKET(data->ep_out); + data->ep_out->request.req_type = UIO_REQUEST_READ_BEST; + rt_usbd_io_request(func->device, data->ep_out, &data->ep_out->request); + return RT_EOK; +} + +static rt_err_t _ep_in_handler(ufunction_t func, rt_size_t size) +{ + struct hid_s *data; + RT_ASSERT(func != RT_NULL); + RT_ASSERT(func->device != RT_NULL); + + data = (struct hid_s *) func->user_data; + if(data->parent.tx_complete != RT_NULL) + { + data->parent.tx_complete(&data->parent,RT_NULL); + } + return RT_EOK; +} + +static rt_err_t _hid_set_report_callback(udevice_t device, rt_size_t size) +{ + LOG_D("_hid_set_report_callback"); + + if(size != 0) + { + } + + dcd_ep0_send_status(device->dcd); + + return RT_EOK; +} + +/** + * This function will handle hid interface bRequest. + * + * @param device the usb device object. + * @param setup the setup bRequest. + * + * @return RT_EOK on successful. + */ +static rt_err_t _interface_handler(ufunction_t func, ureq_t setup) +{ + RT_ASSERT(func != RT_NULL); + RT_ASSERT(func->device != RT_NULL); + RT_ASSERT(setup != RT_NULL); + + struct hid_s *data = (struct hid_s *) func->user_data; + + + switch (setup->bRequest) + { + case USB_REQ_GET_DESCRIPTOR: + if((setup->wValue >> 8) == USB_DESC_TYPE_REPORT) + { + rt_usbd_ep0_write(func->device, (void *)(&_report_desc[0]), sizeof(_report_desc)); + } + else if((setup->wValue >> 8) == USB_DESC_TYPE_HID) + { + + rt_usbd_ep0_write(func->device, (void *)(&_hid_comm_desc.hid_desc), sizeof(struct uhid_descriptor)); + } + break; + case USB_HID_REQ_GET_REPORT: + if(setup->wLength == 0) + { + rt_usbd_ep0_set_stall(func->device); + break; + } + if((setup->wLength == 0) || (setup->wLength > MAX_REPORT_SIZE)) + setup->wLength = MAX_REPORT_SIZE; + rt_usbd_ep0_write(func->device, data->report_buf,setup->wLength); + break; + case USB_HID_REQ_GET_IDLE: + + dcd_ep0_send_status(func->device->dcd); + break; + case USB_HID_REQ_GET_PROTOCOL: + rt_usbd_ep0_write(func->device, &data->protocol,1); + break; + case USB_HID_REQ_SET_REPORT: + + if((setup->wLength == 0) || (setup->wLength > MAX_REPORT_SIZE)) + rt_usbd_ep0_set_stall(func->device); + + rt_usbd_ep0_read(func->device, data->report_buf, setup->wLength, _hid_set_report_callback); + break; + case USB_HID_REQ_SET_IDLE: + dcd_ep0_send_status(func->device->dcd); + break; + case USB_HID_REQ_SET_PROTOCOL: + data->protocol = setup->wValue; + + dcd_ep0_send_status(func->device->dcd); + break; + } + + return RT_EOK; +} + + +/** + * This function will run cdc function, it will be called on handle set configuration bRequest. + * + * @param func the usb function object. + * + * @return RT_EOK on successful. + */ +static rt_err_t _function_enable(ufunction_t func) +{ + struct hid_s *data; + + RT_ASSERT(func != RT_NULL); + RT_ASSERT(func->device != RT_NULL); + data = (struct hid_s *) func->user_data; + + LOG_D("hid function enable"); +// +// _vcom_reset_state(func); +// + if(data->ep_out->buffer == RT_NULL) + { + data->ep_out->buffer = rt_malloc(HID_RX_BUFSIZE); + } + data->ep_out->request.buffer = data->ep_out->buffer; + data->ep_out->request.size = EP_MAXPACKET(data->ep_out); + data->ep_out->request.req_type = UIO_REQUEST_READ_BEST; + + rt_usbd_io_request(func->device, data->ep_out, &data->ep_out->request); + + return RT_EOK; +} + +/** + * This function will stop cdc function, it will be called on handle set configuration bRequest. + * + * @param func the usb function object. + * + * @return RT_EOK on successful. + */ +static rt_err_t _function_disable(ufunction_t func) +{ + struct hid_s *data; + + RT_ASSERT(func != RT_NULL); + RT_ASSERT(func->device != RT_NULL); + data = (struct hid_s *) func->user_data; + + LOG_D("hid function disable"); + + if(data->ep_out->buffer != RT_NULL) + { + rt_free(data->ep_out->buffer); + data->ep_out->buffer = RT_NULL; + } + + return RT_EOK; +} + +static struct ufunction_ops ops = +{ + _function_enable, + _function_disable, + RT_NULL, +}; + + + + +/** + * This function will configure hid descriptor. + * + * @param comm the communication interface number. + * @param data the data interface number. + * + * @return RT_EOK on successful. + */ +static rt_err_t _hid_descriptor_config(uhid_comm_desc_t hid, rt_uint8_t cintf_nr) +{ +#ifdef RT_USB_DEVICE_COMPOSITE + hid->iad_desc.bFirstInterface = cintf_nr; +#endif + + return RT_EOK; +} +static rt_ssize_t _hid_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) +{ + struct hid_s *hiddev = (struct hid_s *)dev; + struct hid_report report; + if (hiddev->func->device->state == USB_STATE_CONFIGURED) + { + report.report_id = pos; + rt_memcpy((void *)report.report,(void *)buffer,size); + report.size = size; + hiddev->ep_in->request.buffer = (void *)&report; + hiddev->ep_in->request.size = (size+1) > 64 ? 64 : size+1; + hiddev->ep_in->request.req_type = UIO_REQUEST_WRITE; + rt_usbd_io_request(hiddev->func->device, hiddev->ep_in, &hiddev->ep_in->request); + return size; + } + + return 0; +} +rt_weak void HID_Report_Received(hid_report_t report) +{ + dump_report(report); +} +rt_align(RT_ALIGN_SIZE) +static rt_uint8_t hid_thread_stack[512]; +static struct rt_thread hid_thread; + +static void hid_thread_entry(void* parameter) +{ + struct hid_report report; + struct hid_s *hiddev; + hiddev = (struct hid_s *)parameter; + while(1) + { + if(rt_mq_recv(&hiddev->hid_mq, &report, sizeof(report),RT_WAITING_FOREVER) < 0) + continue; + HID_Report_Received(&report); + } +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops hid_device_ops = +{ + RT_NULL, + RT_NULL, + RT_NULL, + RT_NULL, + _hid_write, + RT_NULL, +}; +#endif + +static rt_uint8_t hid_mq_pool[(sizeof(struct hid_report)+sizeof(void*))*8]; +static void rt_usb_hid_init(struct ufunction *func) +{ + struct hid_s *hiddev; + hiddev = (struct hid_s *)func->user_data; + rt_memset(&hiddev->parent, 0, sizeof(hiddev->parent)); + +#ifdef RT_USING_DEVICE_OPS + hiddev->parent.ops = &hid_device_ops; +#else + hiddev->parent.write = _hid_write; +#endif + hiddev->func = func; + + rt_device_register(&hiddev->parent, "hidd", RT_DEVICE_FLAG_RDWR); + rt_mq_init(&hiddev->hid_mq, "hiddmq", hid_mq_pool, sizeof(struct hid_report), + sizeof(hid_mq_pool), RT_IPC_FLAG_FIFO); + + rt_thread_init(&hid_thread, "hidd", hid_thread_entry, hiddev, + hid_thread_stack, sizeof(hid_thread_stack), RT_USBD_THREAD_PRIO, 20); + rt_thread_startup(&hid_thread); +} + + +/** + * This function will create a hid function instance. + * + * @param device the usb device object. + * + * @return RT_EOK on successful. + */ +ufunction_t rt_usbd_function_hid_create(udevice_t device) +{ + ufunction_t func; + struct hid_s *data; + + uintf_t hid_intf; + ualtsetting_t hid_setting; + uhid_comm_desc_t hid_desc; + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + + /* set usb device string description */ +#ifdef RT_USB_DEVICE_COMPOSITE + rt_usbd_device_set_interface_string(device, HID_INTF_STR_INDEX, _ustring[2]); +#else + rt_usbd_device_set_string(device, _ustring); +#endif + /* create a cdc function */ + func = rt_usbd_function_new(device, &_dev_desc, &ops); + + /* For high speed mode supporting */ + rt_usbd_device_set_qualifier(device, &dev_qualifier); + + /* allocate memory for cdc vcom data */ + data = (struct hid_s*)rt_malloc(sizeof(struct hid_s)); + rt_memset(data, 0, sizeof(struct hid_s)); + func->user_data = (void*)data; + + /* create an interface object */ + hid_intf = rt_usbd_interface_new(device, _interface_handler); + + /* create an alternate setting object */ + hid_setting = rt_usbd_altsetting_new(sizeof(struct uhid_comm_descriptor)); + + /* config desc in alternate setting */ + rt_usbd_altsetting_config_descriptor(hid_setting, &_hid_comm_desc, (rt_off_t)&((uhid_comm_desc_t)0)->intf_desc); + + /* configure the hid interface descriptor */ + _hid_descriptor_config(hid_setting->desc, hid_intf->intf_num); + + /* create endpoint */ + hid_desc = (uhid_comm_desc_t)hid_setting->desc; + data->ep_out = rt_usbd_endpoint_new(&hid_desc->ep_out_desc, _ep_out_handler); + data->ep_in = rt_usbd_endpoint_new(&hid_desc->ep_in_desc, _ep_in_handler); + + /* add the int out and int in endpoint to the alternate setting */ + rt_usbd_altsetting_add_endpoint(hid_setting, data->ep_out); + rt_usbd_altsetting_add_endpoint(hid_setting, data->ep_in); + + /* add the alternate setting to the interface, then set default setting */ + rt_usbd_interface_add_altsetting(hid_intf, hid_setting); + rt_usbd_set_altsetting(hid_intf, 0); + + /* add the interface to the mass storage function */ + rt_usbd_function_add_interface(func, hid_intf); + + /* initilize hid */ + rt_usb_hid_init(func); + return func; +} +struct udclass hid_class = +{ + .rt_usbd_function_create = rt_usbd_function_hid_create +}; + +int rt_usbd_hid_class_register(void) +{ + rt_usbd_class_register(&hid_class); + return 0; +} +INIT_PREV_EXPORT(rt_usbd_hid_class_register); +#endif /* RT_USB_DEVICE_HID */ diff --git a/components/legacy/usb/usbdevice/class/hid.h b/components/legacy/usb/usbdevice/class/hid.h new file mode 100644 index 0000000000..99da810ce8 --- /dev/null +++ b/components/legacy/usb/usbdevice/class/hid.h @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2017-03-13 Urey the first version + * 2017-11-16 ZYH Update to common hid + */ +#ifndef _USBDEVICE_CLASS_HID_H_ +#define _USBDEVICE_CLASS_HID_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define HID_DESCRIPTOR_TYPE 0x21 +#define HID_DESCRIPTOR_SIZE 0x09 +#define HID_OFF_HID_DESC 0x12 + +#define USB_HID_SUBCLASS_BOOT 0x01 +#define USB_HID_SUBCLASS_NOBOOT 0x00 + +#define USB_HID_PROTOCOL_NONE 0x00 +#define USB_HID_PROTOCOL_KEYBOARD 0x01 +#define USB_HID_PROTOCOL_MOUSE 0x02 + + +#define USB_HID_REQ_GET_REPORT 0x01 +#define USB_HID_REQ_GET_IDLE 0x02 +#define USB_HID_REQ_GET_PROTOCOL 0x03 +#define USB_HID_REQ_SET_REPORT 0x09 +#define USB_HID_REQ_SET_IDLE 0x0a +#define USB_HID_REQ_SET_PROTOCOL 0x0b + +#define MAX_REPORT_SIZE 64 +#define HID_RX_BUFSIZE 64 + +/* HID Report Types */ +#define HID_REPORT_INPUT 0x01 +#define HID_REPORT_OUTPUT 0x02 +#define HID_REPORT_FEATURE 0x03 + +/* Usage Pages */ +#define USAGEPAGE_UNDEFINED 0x00 +#define USAGEPAGE_GENERIC 0x01 +#define USAGEPAGE_SIMULATION 0x02 +#define USAGEPAGE_VR 0x03 +#define USAGEPAGE_SPORT 0x04 +#define USAGEPAGE_GAME 0x05 +#define USAGEPAGE_DEV_CONTROLS 0x06 +#define USAGEPAGE_KEYBOARD 0x07 +#define USAGEPAGE_LED 0x08 +#define USAGEPAGE_BUTTON 0x09 +#define USAGEPAGE_ORDINAL 0x0A +#define USAGEPAGE_TELEPHONY 0x0B +#define USAGEPAGE_CONSUMER 0x0C +#define USAGEPAGE_DIGITIZER 0x0D +#define USAGEPAGE_PIDPAGE 0x0F +#define USAGEPAGE_UNICODE 0x10 +#define USAGEPAGE_ALPHANUMERIC 0x14 +#define USAGEPAGE_BARCODESCANNER 0x8C + +/* Generic Desktop Page (0x01) */ +#define USAGE_GENERIC_POINTER 0x01 +#define USAGE_GENERIC_MOUSE 0x02 +#define USAGE_GENERIC_JOYSTICK 0x04 +#define USAGE_GENERIC_GAMEPAD 0x05 +#define USAGE_GENERIC_KEYBOARD 0x06 +#define USAGE_GENERIC_KEYPAD 0x07 +#define USAGE_GENERIC_X 0x30 +#define USAGE_GENERIC_Y 0x31 +#define USAGE_GENERIC_Z 0x32 +#define USAGE_GENERIC_RX 0x33 +#define USAGE_GENERIC_RY 0x34 +#define USAGE_GENERIC_RZ 0x35 +#define USAGE_GENERIC_SLIDER 0x36 +#define USAGE_GENERIC_DIAL 0x37 +#define USAGE_GENERIC_WHEEL 0x38 +#define USAGE_GENERIC_HATSWITCH 0x39 +#define USAGE_GENERIC_COUNTED_BUFFER 0x3A +#define USAGE_GENERIC_BYTE_COUNT 0x3B +#define USAGE_GENERIC_MOTION_WAKEUP 0x3C +#define USAGE_GENERIC_VX 0x40 +#define USAGE_GENERIC_VY 0x41 +#define USAGE_GENERIC_VZ 0x42 +#define USAGE_GENERIC_VBRX 0x43 +#define USAGE_GENERIC_VBRY 0x44 +#define USAGE_GENERIC_VBRZ 0x45 +#define USAGE_GENERIC_VNO 0x46 +#define USAGE_GENERIC_SYSTEM_CTL 0x80 +#define USAGE_GENERIC_SYSCTL_POWER 0x81 +#define USAGE_GENERIC_SYSCTL_SLEEP 0x82 +#define USAGE_GENERIC_SYSCTL_WAKE 0x83 +#define USAGE_GENERIC_SYSCTL_CONTEXT_MENU 0x84 +#define USAGE_GENERIC_SYSCTL_MAIN_MENU 0x85 +#define USAGE_GENERIC_SYSCTL_APP_MENU 0x86 +#define USAGE_GENERIC_SYSCTL_HELP_MENU 0x87 +#define USAGE_GENERIC_SYSCTL_MENU_EXIT 0x88 +#define USAGE_GENERIC_SYSCTL_MENU_SELECT 0x89 +#define USAGE_GENERIC_SYSCTL_MENU_RIGHT 0x8A +#define USAGE_GENERIC_SYSCTL_MENU_LEFT 0x8B +#define USAGE_GENERIC_SYSCTL_MENU_UP 0x8C +#define USAGE_GENERIC_SYSCTL_MENU_DOWN 0x8D + +/* Simulation Controls Page(0x02) */ +#define USAGE_SIMCTRL_THROTTLE 0xBB + +/* HID Report Items */ + +/* Main Items */ +#define HID_Input(x) 0x81,x +#define HID_Output(x) 0x91,x +#define HID_Feature(x) 0xB1,x +#define HID_Collection(x) 0xA1,x +#define HID_EndCollection() 0xC0 + +/* Local Items */ +#define HID_Usage(x) 0x09,x +#define HID_UsageMin(x) 0x19,x +#define HID_UsageMax(x) 0x29,x + +/* Global Items */ +#define HID_UsagePage(x) 0x05,x +#define HID_UsagePageVendor(x) 0x06,x,0xFF +#define HID_LogicalMin(x) 0x15,x +#define HID_LogicalMinS(x) 0x16,(x&0xFF),((x>>8)&0xFF) +#define HID_LogicalMinL(x) 0x17,(x&0xFF),((x>>8)&0xFF),((x>>16)&0xFF),((x>>24)&0xFF) +#define HID_LogicalMax(x) 0x25,x +#define HID_LogicalMaxS(x) 0x26,(x&0xFF),((x>>8)&0xFF) +#define HID_LogicalMaxL(x) 0x27,(x&0xFF),((x>>8)&0xFF),((x>>16)&0xFF),((x>>24)&0xFF) +#define HID_PhysicalMin(x) 0x35,x +#define HID_PhysicalMinS(x) 0x36,(x&0xFF),((x>>8)&0xFF) +#define HID_PhysicalMinL(x) 0x37,(x&0xFF),((x>>8)&0xFF),((x>>16)&0xFF),((x>>24)&0xFF) +#define HID_PhysicalMax(x) 0x45,x +#define HID_PhysicalMaxS(x) 0x46,(x&0xFF),((x>>8)&0xFF) +#define HID_PhysicalMaxL(x) 0x47,(x&0xFF),((x>>8)&0xFF),((x>>16)&0xFF),((x>>24)&0xFF) +#define HID_UnitExponent(x) 0x55,x +#define HID_Unit(x) 0x65,x +#define HID_UnitS(x) 0x66,(x&0xFF),((x>>8)&0xFF) +#define HID_UnitL(x) 0x67,(x&0xFF),((x>>8)&0xFF),((x>>16)&0xFF),((x>>24)&0xFF) +#define HID_ReportSize(x) 0x75,x +#define HID_ReportSizeS(x) 0x76,(x&0xFF),((x>>8)&0xFF)) +#define HID_ReportSizeL(x) 0x77,(x&0xFF),((x>>8)&0xFF),((x>>16)&0xFF),((x>>24)&0xFF) +#define HID_ReportID(x) 0x85,x +#define HID_ReportCount(x) 0x95,x +#define HID_ReportCountS(x) 0x96,(x&0xFF),((x>>8)&0xFF) +#define HID_ReportCountL(x) 0x97,(x&0xFF),((x>>8)&0xFF),((x>>16)&0xFF),((x>>24)&0xFF) +#define HID_Push() 0xA4 +#define HID_Pop() 0xB4 + +/* Input, Output, Feature Data */ +#define HID_DATA (0<<0) +#define HID_CONST (1<<0) +#define HID_ARRAY (0<<1) +#define HID_VAR (1<<1) +#define HID_ABS (0<<2) +#define HID_REL (1<<2) +#define HID_NOWRAP (0<<3) +#define HID_WRAP (1<<3) +#define HID_LINEAR (0<<4) +#define HID_NONLINEAR (1<<4) +#define HID_PREFERREDSTATE (0<<5) +#define HID_NOPREFERRED (1<<5) +#define HID_NONULLPOSITION (0<<6) +#define HID_NULLSTATE (1<<6) +#define HID_NONVOLATILE (0<<7) +#define HID_VOLATILE (1<<7) + +/* Collection Data */ +#define HID_PHYSICAL 0x00 +#define HID_APPLICATION 0x01 +#define HID_LOGICAL 0x02 +#define HID_REPORT 0x03 +#define HID_NAMEDARRAY 0x04 +#define HID_USAGESWITCH 0x05 +#define HID_USAGEMODIFIER 0x06 + +//HID_MBED_DEFINE +#define HID_VERSION_1_11 (0x0111) + +/* HID Class */ +#define HID_CLASS (3) +#define HID_SUBCLASS_NONE (0) +#define HID_SUBCLASS_BOOT (1) +#define HID_PROTOCOL_NONE (0) +#define HID_PROTOCOL_KEYBOARD (1) +#define HID_PROTOCOL_MOUSE (2) + +/* Descriptors */ +#define HID_DESCRIPTOR (33) +#define HID_DESCRIPTOR_LENGTH (0x09) +#define REPORT_DESCRIPTOR (34) + +/* Class requests */ +#define GET_REPORT (0x1) +#define GET_IDLE (0x2) +#define SET_REPORT (0x9) +#define SET_IDLE (0xa) + +/* HID Class Report Descriptor */ +/* Short items: size is 0, 1, 2 or 3 specifying 0, 1, 2 or 4 (four) bytes */ +/* of data as per HID Class standard */ + +/* Main items */ +#define INPUT(size) (0x80 | size) +#define OUTPUT(size) (0x90 | size) +#define FEATURE(size) (0xb0 | size) +#define COLLECTION(size) (0xa0 | size) +#define END_COLLECTION(size) (0xc0 | size) + +/* Global items */ +#define USAGE_PAGE(size) (0x04 | size) +#define LOGICAL_MINIMUM(size) (0x14 | size) +#define LOGICAL_MAXIMUM(size) (0x24 | size) +#define PHYSICAL_MINIMUM(size) (0x34 | size) +#define PHYSICAL_MAXIMUM(size) (0x44 | size) +#define UNIT_EXPONENT(size) (0x54 | size) +#define UNIT(size) (0x64 | size) +#define REPORT_SIZE(size) (0x74 | size) +#define REPORT_ID(size) (0x84 | size) +#define REPORT_COUNT(size) (0x94 | size) +#define PUSH(size) (0xa4 | size) +#define POP(size) (0xb4 | size) + +/* Local items */ +#define USAGE(size) (0x08 | size) +#define USAGE_MINIMUM(size) (0x18 | size) +#define USAGE_MAXIMUM(size) (0x28 | size) +#define DESIGNATOR_INDEX(size) (0x38 | size) +#define DESIGNATOR_MINIMUM(size) (0x48 | size) +#define DESIGNATOR_MAXIMUM(size) (0x58 | size) +#define STRING_INDEX(size) (0x78 | size) +#define STRING_MINIMUM(size) (0x88 | size) +#define STRING_MAXIMUM(size) (0x98 | size) +#define DELIMITER(size) (0xa8 | size) + +#define LSB(n) ((n)&0xff) +#define MSB(n) (((n)&0xff00)>>8) +struct uhid_comm_descriptor +{ +#ifdef RT_USB_DEVICE_COMPOSITE + struct uiad_descriptor iad_desc; +#endif + struct uinterface_descriptor intf_desc; + struct uhid_descriptor hid_desc; + struct uendpoint_descriptor ep_in_desc; + struct uendpoint_descriptor ep_out_desc; +}; +typedef struct uhid_comm_descriptor* uhid_comm_desc_t; + + +#ifdef __cplusplus +} +#endif + +#endif /* _USBDEVICE_CLASS_HID_H_ */ diff --git a/components/legacy/usb/usbdevice/class/mstorage.c b/components/legacy/usb/usbdevice/class/mstorage.c new file mode 100644 index 0000000000..a077988102 --- /dev/null +++ b/components/legacy/usb/usbdevice/class/mstorage.c @@ -0,0 +1,1167 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2012-10-01 Yi Qiu first version + * 2012-11-25 Heyuanjie87 reduce the memory consumption + * 2012-12-09 Heyuanjie87 change function and endpoint handler + * 2013-07-25 Yi Qiu update for USB CV test + */ + +#include <rtthread.h> +#include <rtdevice.h> + +#include "drivers/usb_device.h" +#include "mstorage.h" + +#ifdef RT_USING_DFS_MNTTABLE +#include "dfs_fs.h" +#endif +#ifdef RT_USB_DEVICE_MSTORAGE +#define MSTRORAGE_INTF_STR_INDEX 11 + +#define DBG_TAG "usbdevice.mstorage" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + +enum STAT +{ + STAT_CBW, + STAT_CMD, + STAT_CSW, + STAT_RECEIVE, + STAT_SEND, +}; + +typedef enum +{ + FIXED, + COUNT, + BLOCK_COUNT, +}CB_SIZE_TYPE; + +typedef enum +{ + DIR_IN, + DIR_OUT, + DIR_NONE, +}CB_DIR; + +typedef rt_ssize_t (*cbw_handler)(ufunction_t func, ustorage_cbw_t cbw); + +struct scsi_cmd +{ + rt_uint16_t cmd; + cbw_handler handler; + rt_size_t cmd_len; + CB_SIZE_TYPE type; + rt_size_t data_size; + CB_DIR dir; +}; + +struct mstorage +{ + struct ustorage_csw csw_response; + uep_t ep_in; + uep_t ep_out; + int status; + rt_uint32_t cb_data_size; + rt_device_t disk; + rt_uint32_t block; + rt_int32_t count; + rt_int32_t size; + struct scsi_cmd* processing; + struct rt_device_blk_geometry geometry; +}; + +rt_align(4) +static struct udevice_descriptor dev_desc = +{ + USB_DESC_LENGTH_DEVICE, //bLength; + USB_DESC_TYPE_DEVICE, //type; + USB_BCD_VERSION, //bcdUSB; + USB_CLASS_MASS_STORAGE, //bDeviceClass; + 0x06, //bDeviceSubClass; + 0x50, //bDeviceProtocol; + 0x40, //bMaxPacketSize0; + _VENDOR_ID, //idVendor; + _PRODUCT_ID, //idProduct; + USB_BCD_DEVICE, //bcdDevice; + USB_STRING_MANU_INDEX, //iManufacturer; + USB_STRING_PRODUCT_INDEX, //iProduct; + USB_STRING_SERIAL_INDEX, //iSerialNumber; + USB_DYNAMIC, //bNumConfigurations; +}; + +//FS and HS needed +rt_align(4) +static struct usb_qualifier_descriptor dev_qualifier = +{ + sizeof(dev_qualifier), //bLength + USB_DESC_TYPE_DEVICEQUALIFIER, //bDescriptorType + 0x0200, //bcdUSB + USB_CLASS_MASS_STORAGE, //bDeviceClass + 0x06, //bDeviceSubClass + 0x50, //bDeviceProtocol + 64, //bMaxPacketSize0 + 0x01, //bNumConfigurations + 0, +}; + + +rt_align(4) +const static struct umass_descriptor _mass_desc = +{ +#ifdef RT_USB_DEVICE_COMPOSITE + /* Interface Association Descriptor */ + { + USB_DESC_LENGTH_IAD, + USB_DESC_TYPE_IAD, + USB_DYNAMIC, + 0x01, + USB_CLASS_MASS_STORAGE, + 0x06, + 0x50, + 0x00, + }, +#endif + { + USB_DESC_LENGTH_INTERFACE, //bLength; + USB_DESC_TYPE_INTERFACE, //type; + USB_DYNAMIC, //bInterfaceNumber; + 0x00, //bAlternateSetting; + 0x02, //bNumEndpoints + USB_CLASS_MASS_STORAGE, //bInterfaceClass; + 0x06, //bInterfaceSubClass; + 0x50, //bInterfaceProtocol; +#ifdef RT_USB_DEVICE_COMPOSITE + MSTRORAGE_INTF_STR_INDEX, +#else + 0x00, //iInterface; +#endif + }, + + { + USB_DESC_LENGTH_ENDPOINT, //bLength; + USB_DESC_TYPE_ENDPOINT, //type; + USB_DYNAMIC | USB_DIR_OUT, //bEndpointAddress; + USB_EP_ATTR_BULK, //bmAttributes; + USB_DYNAMIC, //wMaxPacketSize; + 0x00, //bInterval; + }, + + { + USB_DESC_LENGTH_ENDPOINT, //bLength; + USB_DESC_TYPE_ENDPOINT, //type; + USB_DYNAMIC | USB_DIR_IN, //bEndpointAddress; + USB_EP_ATTR_BULK, //bmAttributes; + USB_DYNAMIC, //wMaxPacketSize; + 0x00, //bInterval; + }, +}; + +rt_align(4) +const static char* _ustring[] = +{ + "Language", + "RT-Thread Team.", + "RTT Mass Storage", + "320219198301", + "Configuration", + "Interface", +}; + +static rt_ssize_t _test_unit_ready(ufunction_t func, ustorage_cbw_t cbw); +static rt_ssize_t _request_sense(ufunction_t func, ustorage_cbw_t cbw); +static rt_ssize_t _inquiry_cmd(ufunction_t func, ustorage_cbw_t cbw); +static rt_ssize_t _allow_removal(ufunction_t func, ustorage_cbw_t cbw); +static rt_ssize_t _start_stop(ufunction_t func, ustorage_cbw_t cbw); +static rt_ssize_t _mode_sense_6(ufunction_t func, ustorage_cbw_t cbw); +static rt_ssize_t _read_capacities(ufunction_t func, ustorage_cbw_t cbw); +static rt_ssize_t _read_capacity(ufunction_t func, ustorage_cbw_t cbw); +static rt_ssize_t _read_10(ufunction_t func, ustorage_cbw_t cbw); +static rt_ssize_t _write_10(ufunction_t func, ustorage_cbw_t cbw); +static rt_ssize_t _verify_10(ufunction_t func, ustorage_cbw_t cbw); + +rt_align(4) +static struct scsi_cmd cmd_data[] = +{ + {SCSI_TEST_UNIT_READY, _test_unit_ready, 6, FIXED, 0, DIR_NONE}, + {SCSI_REQUEST_SENSE, _request_sense, 6, COUNT, 0, DIR_IN}, + {SCSI_INQUIRY_CMD, _inquiry_cmd, 6, COUNT, 0, DIR_IN}, + {SCSI_ALLOW_REMOVAL, _allow_removal, 6, FIXED, 0, DIR_NONE}, + {SCSI_MODE_SENSE_6, _mode_sense_6, 6, COUNT, 0, DIR_IN}, + {SCSI_START_STOP, _start_stop, 6, FIXED, 0, DIR_NONE}, + {SCSI_READ_CAPACITIES, _read_capacities, 10, COUNT, 0, DIR_NONE}, + {SCSI_READ_CAPACITY, _read_capacity, 10, FIXED, 8, DIR_IN}, + {SCSI_READ_10, _read_10, 10, BLOCK_COUNT, 0, DIR_IN}, + {SCSI_WRITE_10, _write_10, 10, BLOCK_COUNT, 0, DIR_OUT}, + {SCSI_VERIFY_10, _verify_10, 10, FIXED, 0, DIR_NONE}, +}; + +static void _send_status(ufunction_t func) +{ + struct mstorage *data; + + RT_ASSERT(func != RT_NULL); + + LOG_D("_send_status"); + + data = (struct mstorage*)func->user_data; + data->ep_in->request.buffer = (rt_uint8_t*)&data->csw_response; + data->ep_in->request.size = SIZEOF_CSW; + data->ep_in->request.req_type = UIO_REQUEST_WRITE; + rt_usbd_io_request(func->device, data->ep_in, &data->ep_in->request); + data->status = STAT_CSW; +} + +static rt_ssize_t _test_unit_ready(ufunction_t func, ustorage_cbw_t cbw) +{ + struct mstorage *data; + + RT_ASSERT(func != RT_NULL); + RT_ASSERT(func->device != RT_NULL); + + LOG_D("_test_unit_ready"); + + data = (struct mstorage*)func->user_data; + data->csw_response.status = 0; + + return 0; +} + +static rt_ssize_t _allow_removal(ufunction_t func, ustorage_cbw_t cbw) +{ + struct mstorage *data; + + RT_ASSERT(func != RT_NULL); + RT_ASSERT(func->device != RT_NULL); + + LOG_D("_allow_removal"); + + data = (struct mstorage*)func->user_data; + data->csw_response.status = 0; + + return 0; +} + +/** + * This function will handle inquiry command request. + * + * @param func the usb function object. + * @param cbw the command block wrapper. + * + * @return RT_EOK on successful. + */ + +static rt_ssize_t _inquiry_cmd(ufunction_t func, ustorage_cbw_t cbw) +{ + struct mstorage *data; + rt_uint8_t *buf; + + RT_ASSERT(func != RT_NULL); + RT_ASSERT(func->device != RT_NULL); + RT_ASSERT(cbw != RT_NULL); + + LOG_D("_inquiry_cmd"); + + data = (struct mstorage*)func->user_data; + buf = data->ep_in->buffer; + + *(rt_uint32_t*)&buf[0] = 0x0 | (0x80 << 8); + *(rt_uint32_t*)&buf[4] = 31; + + rt_memset(&buf[8], 0x20, 28); + rt_memcpy(&buf[8], "RTT", 3); + rt_memcpy(&buf[16], "USB Disk", 8); + + data->cb_data_size = MIN(data->cb_data_size, SIZEOF_INQUIRY_CMD); + data->ep_in->request.buffer = buf; + data->ep_in->request.size = data->cb_data_size; + data->ep_in->request.req_type = UIO_REQUEST_WRITE; + rt_usbd_io_request(func->device, data->ep_in, &data->ep_in->request); + data->status = STAT_CMD; + + return data->cb_data_size; +} + +/** + * This function will handle sense request. + * + * @param func the usb function object. + * @param cbw the command block wrapper. + * + * @return RT_EOK on successful. + */ +static rt_ssize_t _request_sense(ufunction_t func, ustorage_cbw_t cbw) +{ + struct mstorage *data; + struct request_sense_data *buf; + + RT_ASSERT(func != RT_NULL); + RT_ASSERT(func->device != RT_NULL); + RT_ASSERT(cbw != RT_NULL); + + LOG_D("_request_sense"); + + data = (struct mstorage*)func->user_data; + buf = (struct request_sense_data *)data->ep_in->buffer; + + buf->ErrorCode = 0x70; + buf->Valid = 0; + buf->SenseKey = 2; + buf->Information[0] = 0; + buf->Information[1] = 0; + buf->Information[2] = 0; + buf->Information[3] = 0; + buf->AdditionalSenseLength = 0x0a; + buf->AdditionalSenseCode = 0x3a; + buf->AdditionalSenseCodeQualifier = 0; + + data->cb_data_size = MIN(data->cb_data_size, SIZEOF_REQUEST_SENSE); + data->ep_in->request.buffer = (rt_uint8_t*)data->ep_in->buffer; + data->ep_in->request.size = data->cb_data_size; + data->ep_in->request.req_type = UIO_REQUEST_WRITE; + rt_usbd_io_request(func->device, data->ep_in, &data->ep_in->request); + data->status = STAT_CMD; + + return data->cb_data_size; +} + +/** + * This function will handle mode_sense_6 request. + * + * @param func the usb function object. + * @param cbw the command block wrapper. + * + * @return RT_EOK on successful. + */ +static rt_ssize_t _mode_sense_6(ufunction_t func, ustorage_cbw_t cbw) +{ + struct mstorage *data; + rt_uint8_t *buf; + + RT_ASSERT(func != RT_NULL); + RT_ASSERT(func->device != RT_NULL); + RT_ASSERT(cbw != RT_NULL); + + LOG_D("_mode_sense_6"); + + data = (struct mstorage*)func->user_data; + buf = data->ep_in->buffer; + buf[0] = 3; + buf[1] = 0; + buf[2] = 0; + buf[3] = 0; + + data->cb_data_size = MIN(data->cb_data_size, SIZEOF_MODE_SENSE_6); + data->ep_in->request.buffer = buf; + data->ep_in->request.size = data->cb_data_size; + data->ep_in->request.req_type = UIO_REQUEST_WRITE; + rt_usbd_io_request(func->device, data->ep_in, &data->ep_in->request); + data->status = STAT_CMD; + + return data->cb_data_size; +} + +/** + * This function will handle read_capacities request. + * + * @param func the usb function object. + * @param cbw the command block wrapper. + * + * @return RT_EOK on successful. + */ +static rt_ssize_t _read_capacities(ufunction_t func, ustorage_cbw_t cbw) +{ + struct mstorage *data; + rt_uint8_t *buf; + rt_uint32_t sector_count, sector_size; + + RT_ASSERT(func != RT_NULL); + RT_ASSERT(func->device != RT_NULL); + RT_ASSERT(cbw != RT_NULL); + + LOG_D("_read_capacities"); + + data = (struct mstorage*)func->user_data; + buf = data->ep_in->buffer; + sector_count = data->geometry.sector_count; + sector_size = data->geometry.bytes_per_sector; + + *(rt_uint32_t*)&buf[0] = 0x08000000; + buf[4] = sector_count >> 24; + buf[5] = 0xff & (sector_count >> 16); + buf[6] = 0xff & (sector_count >> 8); + buf[7] = 0xff & (sector_count); + buf[8] = 0x02; + buf[9] = 0xff & (sector_size >> 16); + buf[10] = 0xff & (sector_size >> 8); + buf[11] = 0xff & sector_size; + + data->cb_data_size = MIN(data->cb_data_size, SIZEOF_READ_CAPACITIES); + data->ep_in->request.buffer = buf; + data->ep_in->request.size = data->cb_data_size; + data->ep_in->request.req_type = UIO_REQUEST_WRITE; + rt_usbd_io_request(func->device, data->ep_in, &data->ep_in->request); + data->status = STAT_CMD; + + return data->cb_data_size; +} + +/** + * This function will handle read_capacity request. + * + * @param func the usb function object. + * @param cbw the command block wapper. + * + * @return RT_EOK on successful. + */ +static rt_ssize_t _read_capacity(ufunction_t func, ustorage_cbw_t cbw) +{ + struct mstorage *data; + + rt_uint8_t *buf; + rt_uint32_t sector_count, sector_size; + + RT_ASSERT(func != RT_NULL); + RT_ASSERT(func->device != RT_NULL); + RT_ASSERT(cbw != RT_NULL); + + LOG_D("_read_capacity"); + + data = (struct mstorage*)func->user_data; + buf = data->ep_in->buffer; + sector_count = data->geometry.sector_count - 1; /* Last Logical Block Address */ + sector_size = data->geometry.bytes_per_sector; + + buf[0] = sector_count >> 24; + buf[1] = 0xff & (sector_count >> 16); + buf[2] = 0xff & (sector_count >> 8); + buf[3] = 0xff & (sector_count); + buf[4] = 0x0; + buf[5] = 0xff & (sector_size >> 16); + buf[6] = 0xff & (sector_size >> 8); + buf[7] = 0xff & sector_size; + + data->cb_data_size = MIN(data->cb_data_size, SIZEOF_READ_CAPACITY); + data->ep_in->request.buffer = buf; + data->ep_in->request.size = data->cb_data_size; + data->ep_in->request.req_type = UIO_REQUEST_WRITE; + rt_usbd_io_request(func->device, data->ep_in, &data->ep_in->request); + data->status = STAT_CMD; + + return data->cb_data_size; +} + +/** + * This function will handle read_10 request. + * + * @param func the usb function object. + * @param cbw the command block wrapper. + * + * @return RT_EOK on successful. + */ +static rt_ssize_t _read_10(ufunction_t func, ustorage_cbw_t cbw) +{ + struct mstorage *data; + rt_size_t size; + + RT_ASSERT(func != RT_NULL); + RT_ASSERT(func->device != RT_NULL); + RT_ASSERT(cbw != RT_NULL); + + data = (struct mstorage*)func->user_data; + data->block = cbw->cb[2]<<24 | cbw->cb[3]<<16 | cbw->cb[4]<<8 | + cbw->cb[5]<<0; + data->count = cbw->cb[7]<<8 | cbw->cb[8]<<0; + + RT_ASSERT(data->count < data->geometry.sector_count); + + data->csw_response.data_reside = data->cb_data_size; + size = rt_device_read(data->disk, data->block, data->ep_in->buffer, 1); + if(size == 0) + { + rt_kprintf("read data error\n"); + } + + data->ep_in->request.buffer = data->ep_in->buffer; + data->ep_in->request.size = data->geometry.bytes_per_sector; + data->ep_in->request.req_type = UIO_REQUEST_WRITE; + rt_usbd_io_request(func->device, data->ep_in, &data->ep_in->request); + data->status = STAT_SEND; + + return data->geometry.bytes_per_sector; +} + +/** + * This function will handle write_10 request. + * + * @param func the usb function object. + * @param cbw the command block wrapper. + * + * @return RT_EOK on successful. + */ +static rt_ssize_t _write_10(ufunction_t func, ustorage_cbw_t cbw) +{ + struct mstorage *data; + + RT_ASSERT(func != RT_NULL); + RT_ASSERT(func->device != RT_NULL); + RT_ASSERT(cbw != RT_NULL); + + data = (struct mstorage*)func->user_data; + + data->block = cbw->cb[2]<<24 | cbw->cb[3]<<16 | cbw->cb[4]<<8 | + cbw->cb[5]<<0; + data->count = cbw->cb[7]<<8 | cbw->cb[8]; + data->csw_response.data_reside = cbw->xfer_len; + data->size = data->count * data->geometry.bytes_per_sector; + + LOG_D("_write_10 count 0x%x block 0x%x 0x%x", + data->count, data->block, data->geometry.sector_count); + + data->csw_response.data_reside = data->cb_data_size; + + data->ep_out->request.buffer = data->ep_out->buffer; + data->ep_out->request.size = data->geometry.bytes_per_sector; + data->ep_out->request.req_type = UIO_REQUEST_READ_FULL; + rt_usbd_io_request(func->device, data->ep_out, &data->ep_out->request); + data->status = STAT_RECEIVE; + + return data->geometry.bytes_per_sector; +} + +/** + * This function will handle verify_10 request. + * + * @param func the usb function object. + * + * @return RT_EOK on successful. + */ +static rt_ssize_t _verify_10(ufunction_t func, ustorage_cbw_t cbw) +{ + struct mstorage *data; + + RT_ASSERT(func != RT_NULL); + RT_ASSERT(func->device != RT_NULL); + + LOG_D("_verify_10"); + + data = (struct mstorage*)func->user_data; + data->csw_response.status = 0; + + return 0; +} + +static rt_ssize_t _start_stop(ufunction_t func, + ustorage_cbw_t cbw) +{ + struct mstorage *data; + + RT_ASSERT(func != RT_NULL); + RT_ASSERT(func->device != RT_NULL); + + LOG_D("_start_stop"); + + data = (struct mstorage*)func->user_data; + data->csw_response.status = 0; + + return 0; +} + +static rt_err_t _ep_in_handler(ufunction_t func, rt_size_t size) +{ + struct mstorage *data; + + RT_ASSERT(func != RT_NULL); + RT_ASSERT(func->device != RT_NULL); + + LOG_D("_ep_in_handler"); + + data = (struct mstorage*)func->user_data; + + switch(data->status) + { + case STAT_CSW: + if(data->ep_in->request.size != SIZEOF_CSW) + { + rt_kprintf("Size of csw command error\n"); + rt_usbd_ep_set_stall(func->device, data->ep_in); + } + else + { + LOG_D("return to cbw status"); + data->ep_out->request.buffer = data->ep_out->buffer; + data->ep_out->request.size = SIZEOF_CBW; + data->ep_out->request.req_type = UIO_REQUEST_READ_FULL; + rt_usbd_io_request(func->device, data->ep_out, &data->ep_out->request); + data->status = STAT_CBW; + } + break; + case STAT_CMD: + if(data->csw_response.data_reside == 0xFF) + { + data->csw_response.data_reside = 0; + } + else + { + data->csw_response.data_reside -= data->ep_in->request.size; + if(data->csw_response.data_reside != 0) + { + LOG_D("data_reside %d, request %d", + data->csw_response.data_reside, data->ep_in->request.size); + if(data->processing->dir == DIR_OUT) + { + rt_usbd_ep_set_stall(func->device, data->ep_out); + } + else + { + //rt_kprintf("warning:in stall path but not stall\n"); + + /* FIXME: Disable the operation or the disk cannot work. */ + //rt_usbd_ep_set_stall(func->device, data->ep_in); + } + data->csw_response.data_reside = 0; + } + } + _send_status(func); + break; + case STAT_SEND: + data->csw_response.data_reside -= data->ep_in->request.size; + data->count--; + data->block++; + if(data->count > 0 && data->csw_response.data_reside > 0) + { + if(rt_device_read(data->disk, data->block, data->ep_in->buffer, 1) == 0) + { + rt_kprintf("disk read error\n"); + rt_usbd_ep_set_stall(func->device, data->ep_in); + return -RT_ERROR; + } + + data->ep_in->request.buffer = data->ep_in->buffer; + data->ep_in->request.size = data->geometry.bytes_per_sector; + data->ep_in->request.req_type = UIO_REQUEST_WRITE; + rt_usbd_io_request(func->device, data->ep_in, &data->ep_in->request); + } + else + { + _send_status(func); + } + break; + } + + return RT_EOK; +} + +#ifdef MASS_CBW_DUMP +static void cbw_dump(struct ustorage_cbw* cbw) +{ + RT_ASSERT(cbw != RT_NULL); + + LOG_D("signature 0x%x", cbw->signature); + LOG_D("tag 0x%x", cbw->tag); + LOG_D("xfer_len 0x%x", cbw->xfer_len); + LOG_D("dflags 0x%x", cbw->dflags); + LOG_D("lun 0x%x", cbw->lun); + LOG_D("cb_len 0x%x", cbw->cb_len); + LOG_D("cb[0] 0x%x", cbw->cb[0]); +} +#endif + +static struct scsi_cmd* _find_cbw_command(rt_uint16_t cmd) +{ + int i; + + for(i=0; i<sizeof(cmd_data)/sizeof(struct scsi_cmd); i++) + { + if(cmd_data[i].cmd == cmd) + return &cmd_data[i]; + } + + return RT_NULL; +} + +static void _cb_len_calc(ufunction_t func, struct scsi_cmd* cmd, + ustorage_cbw_t cbw) +{ + struct mstorage *data; + + RT_ASSERT(func != RT_NULL); + RT_ASSERT(cmd != RT_NULL); + RT_ASSERT(cbw != RT_NULL); + + data = (struct mstorage*)func->user_data; + if(cmd->cmd_len == 6) + { + switch(cmd->type) + { + case COUNT: + data->cb_data_size = cbw->cb[4]; + break; + case BLOCK_COUNT: + data->cb_data_size = cbw->cb[4] * data->geometry.bytes_per_sector; + break; + case FIXED: + data->cb_data_size = cmd->data_size; + break; + default: + break; + } + } + else if(cmd->cmd_len == 10) + { + switch(cmd->type) + { + case COUNT: + data->cb_data_size = cbw->cb[7]<<8 | cbw->cb[8]; + break; + case BLOCK_COUNT: + data->cb_data_size = (cbw->cb[7]<<8 | cbw->cb[8]) * + data->geometry.bytes_per_sector; + break; + case FIXED: + data->cb_data_size = cmd->data_size; + break; + default: + break; + } + } + + //workaround: for stability in full-speed mode + else if(cmd->cmd_len == 12) + { + switch(cmd->type) + { + case COUNT: + data->cb_data_size = cbw->cb[4]; + break; + default: + break; + } + } + else + { + rt_kprintf("cmd_len error %d\n", cmd->cmd_len); + } +} + +static rt_bool_t _cbw_verify(ufunction_t func, struct scsi_cmd* cmd, + ustorage_cbw_t cbw) +{ + struct mstorage *data; + + RT_ASSERT(cmd != RT_NULL); + RT_ASSERT(cbw != RT_NULL); + RT_ASSERT(func != RT_NULL); + + data = (struct mstorage*)func->user_data; + if(cmd->cmd_len != cbw->cb_len) + { + rt_kprintf("cb_len error\n"); + cmd->cmd_len = cbw->cb_len; + } + + if(cbw->xfer_len > 0 && data->cb_data_size == 0) + { + rt_kprintf("xfer_len > 0 && data_size == 0\n"); + return RT_FALSE; + } + + if(cbw->xfer_len == 0 && data->cb_data_size > 0) + { + rt_kprintf("xfer_len == 0 && data_size > 0"); + return RT_FALSE; + } + + if(((cbw->dflags & USB_DIR_IN) && (cmd->dir == DIR_OUT)) || + (!(cbw->dflags & USB_DIR_IN) && (cmd->dir == DIR_IN))) + { + rt_kprintf("dir error\n"); + return RT_FALSE; + } + + if(cbw->xfer_len > data->cb_data_size) + { + rt_kprintf("xfer_len > data_size\n"); + return RT_FALSE; + } + + if(cbw->xfer_len < data->cb_data_size) + { + rt_kprintf("xfer_len < data_size\n"); + data->cb_data_size = cbw->xfer_len; + data->csw_response.status = 1; + } + + return RT_TRUE; +} + +static rt_ssize_t _cbw_handler(ufunction_t func, struct scsi_cmd* cmd, + ustorage_cbw_t cbw) +{ + struct mstorage *data; + + RT_ASSERT(func != RT_NULL); + RT_ASSERT(cbw != RT_NULL); + RT_ASSERT(cmd->handler != RT_NULL); + + data = (struct mstorage*)func->user_data; + data->processing = cmd; + return cmd->handler(func, cbw); +} + +/** + * This function will handle mass storage bulk out endpoint request. + * + * @param func the usb function object. + * @param size request size. + * + * @return RT_EOK. + */ +static rt_err_t _ep_out_handler(ufunction_t func, rt_size_t size) +{ + struct mstorage *data; + struct scsi_cmd* cmd; + rt_size_t len; + struct ustorage_cbw* cbw; + + RT_ASSERT(func != RT_NULL); + RT_ASSERT(func->device != RT_NULL); + + LOG_D("_ep_out_handler %d", size); + + data = (struct mstorage*)func->user_data; + cbw = (struct ustorage_cbw*)data->ep_out->buffer; + if(data->status == STAT_CBW) + { + /* dump cbw information */ + if(cbw->signature != CBW_SIGNATURE || size != SIZEOF_CBW) + { + goto exit; + } + + data->csw_response.signature = CSW_SIGNATURE; + data->csw_response.tag = cbw->tag; + data->csw_response.data_reside = cbw->xfer_len; + data->csw_response.status = 0; + + LOG_D("ep_out reside %d", data->csw_response.data_reside); + + cmd = _find_cbw_command(cbw->cb[0]); + if(cmd == RT_NULL) + { + rt_kprintf("can't find cbw command\n"); + goto exit; + } + + _cb_len_calc(func, cmd, cbw); + if(!_cbw_verify(func, cmd, cbw)) + { + goto exit; + } + + len = _cbw_handler(func, cmd, cbw); + if(len == 0) + { + _send_status(func); + } + + return RT_EOK; + } + else if(data->status == STAT_RECEIVE) + { + LOG_D("write size %d block 0x%x oount 0x%x", + size, data->block, data->size); + + data->size -= size; + data->csw_response.data_reside -= size; + + rt_device_write(data->disk, data->block, data->ep_out->buffer, 1); + + if(data->csw_response.data_reside != 0) + { + data->ep_out->request.buffer = data->ep_out->buffer; + data->ep_out->request.size = data->geometry.bytes_per_sector; + data->ep_out->request.req_type = UIO_REQUEST_READ_FULL; + rt_usbd_io_request(func->device, data->ep_out, &data->ep_out->request); + data->block ++; + } + else + { + _send_status(func); + } + + return RT_EOK; + } + +exit: + if(data->csw_response.data_reside) + { + if(cbw->dflags & USB_DIR_IN) + { + rt_usbd_ep_set_stall(func->device, data->ep_in); + } + else + { + rt_usbd_ep_set_stall(func->device, data->ep_in); + rt_usbd_ep_set_stall(func->device, data->ep_out); + } + } + data->csw_response.status = 1; + _send_status(func); + + return -RT_ERROR; +} + +/** + * This function will handle mass storage interface request. + * + * @param func the usb function object. + * @param setup the setup request. + * + * @return RT_EOK on successful. + */ +static rt_err_t _interface_handler(ufunction_t func, ureq_t setup) +{ + rt_uint8_t lun = 0; + + RT_ASSERT(func != RT_NULL); + RT_ASSERT(func->device != RT_NULL); + RT_ASSERT(setup != RT_NULL); + + LOG_D("mstorage_interface_handler"); + + switch(setup->bRequest) + { + case USBREQ_GET_MAX_LUN: + + LOG_D("USBREQ_GET_MAX_LUN"); + + if(setup->wValue || setup->wLength != 1) + { + rt_usbd_ep0_set_stall(func->device); + } + else + { + rt_usbd_ep0_write(func->device, &lun, setup->wLength); + } + break; + case USBREQ_MASS_STORAGE_RESET: + + LOG_D("USBREQ_MASS_STORAGE_RESET"); + + if(setup->wValue || setup->wLength != 0) + { + rt_usbd_ep0_set_stall(func->device); + } + else + { + dcd_ep0_send_status(func->device->dcd); + } + break; + default: + rt_kprintf("unknown interface request\n"); + break; + } + + return RT_EOK; +} + +/** + * This function will run mass storage function, it will be called on handle set configuration request. + * + * @param func the usb function object. + * + * @return RT_EOK on successful. + */ +static rt_err_t _function_enable(ufunction_t func) +{ + struct mstorage *data; + RT_ASSERT(func != RT_NULL); + LOG_D("Mass storage function enabled"); + data = (struct mstorage*)func->user_data; + + data->disk = rt_device_find(RT_USB_MSTORAGE_DISK_NAME); + if(data->disk == RT_NULL) + { + rt_kprintf("no data->disk named %s\n", RT_USB_MSTORAGE_DISK_NAME); + return -RT_ERROR; + } + +#ifdef RT_USING_DFS_MNTTABLE + dfs_unmount_device(data->disk); +#endif + + if(rt_device_open(data->disk, RT_DEVICE_OFLAG_RDWR) != RT_EOK) + { + rt_kprintf("disk open error\n"); + return -RT_ERROR; + } + + if(rt_device_control(data->disk, RT_DEVICE_CTRL_BLK_GETGEOME, + (void*)&data->geometry) != RT_EOK) + { + rt_kprintf("get disk info error\n"); + return -RT_ERROR; + } + + data->ep_in->buffer = (rt_uint8_t*)rt_malloc(data->geometry.bytes_per_sector); + if(data->ep_in->buffer == RT_NULL) + { + rt_kprintf("no memory\n"); + return -RT_ENOMEM; + } + data->ep_out->buffer = (rt_uint8_t*)rt_malloc(data->geometry.bytes_per_sector); + if(data->ep_out->buffer == RT_NULL) + { + rt_free(data->ep_in->buffer); + rt_kprintf("no memory\n"); + return -RT_ENOMEM; + } + + /* prepare to read CBW request */ + data->ep_out->request.buffer = data->ep_out->buffer; + data->ep_out->request.size = SIZEOF_CBW; + data->ep_out->request.req_type = UIO_REQUEST_READ_FULL; + rt_usbd_io_request(func->device, data->ep_out, &data->ep_out->request); + + return RT_EOK; +} + +/** + * This function will stop mass storage function, it will be called on handle set configuration request. + * + * @param device the usb device object. + * + * @return RT_EOK on successful. + */ +static rt_err_t _function_disable(ufunction_t func) +{ + struct mstorage *data; + RT_ASSERT(func != RT_NULL); + + LOG_D("Mass storage function disabled"); + + data = (struct mstorage*)func->user_data; + if(data->ep_in->buffer != RT_NULL) + { + rt_free(data->ep_in->buffer); + data->ep_in->buffer = RT_NULL; + } + + if(data->ep_out->buffer != RT_NULL) + { + rt_free(data->ep_out->buffer); + data->ep_out->buffer = RT_NULL; + } + if(data->disk != RT_NULL) + { + rt_device_close(data->disk); +#ifdef RT_USING_DFS_MNTTABLE + dfs_mount_device(data->disk); +#endif + data->disk = RT_NULL; + } + + data->status = STAT_CBW; + + return RT_EOK; +} + +static struct ufunction_ops ops = +{ + _function_enable, + _function_disable, + RT_NULL, +}; +static rt_err_t _mstorage_descriptor_config(umass_desc_t desc, rt_uint8_t cintf_nr, rt_uint8_t device_is_hs) +{ +#ifdef RT_USB_DEVICE_COMPOSITE + desc->iad_desc.bFirstInterface = cintf_nr; +#endif + desc->ep_out_desc.wMaxPacketSize = device_is_hs ? 512 : 64; + desc->ep_in_desc.wMaxPacketSize = device_is_hs ? 512 : 64; + return RT_EOK; +} +/** + * This function will create a mass storage function instance. + * + * @param device the usb device object. + * + * @return RT_EOK on successful. + */ +ufunction_t rt_usbd_function_mstorage_create(udevice_t device) +{ + uintf_t intf; + struct mstorage *data; + ufunction_t func; + ualtsetting_t setting; + umass_desc_t mass_desc; + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + + /* set usb device string description */ +#ifdef RT_USB_DEVICE_COMPOSITE + rt_usbd_device_set_interface_string(device, MSTRORAGE_INTF_STR_INDEX, _ustring[2]); +#else + rt_usbd_device_set_string(device, _ustring); +#endif + + /* create a mass storage function */ + func = rt_usbd_function_new(device, &dev_desc, &ops); + device->dev_qualifier = &dev_qualifier; + + /* allocate memory for mass storage function data */ + data = (struct mstorage*)rt_malloc(sizeof(struct mstorage)); + rt_memset(data, 0, sizeof(struct mstorage)); + func->user_data = (void*)data; + + /* create an interface object */ + intf = rt_usbd_interface_new(device, _interface_handler); + + /* create an alternate setting object */ + setting = rt_usbd_altsetting_new(sizeof(struct umass_descriptor)); + + /* config desc in alternate setting */ + rt_usbd_altsetting_config_descriptor(setting, &_mass_desc, (rt_off_t)&((umass_desc_t)0)->intf_desc); + + /* configure the msc interface descriptor */ + _mstorage_descriptor_config(setting->desc, intf->intf_num, device->dcd->device_is_hs); + + /* create a bulk out and a bulk in endpoint */ + mass_desc = (umass_desc_t)setting->desc; + data->ep_in = rt_usbd_endpoint_new(&mass_desc->ep_in_desc, _ep_in_handler); + data->ep_out = rt_usbd_endpoint_new(&mass_desc->ep_out_desc, _ep_out_handler); + + /* add the bulk out and bulk in endpoint to the alternate setting */ + rt_usbd_altsetting_add_endpoint(setting, data->ep_out); + rt_usbd_altsetting_add_endpoint(setting, data->ep_in); + + /* add the alternate setting to the interface, then set default setting */ + rt_usbd_interface_add_altsetting(intf, setting); + rt_usbd_set_altsetting(intf, 0); + + /* add the interface to the mass storage function */ + rt_usbd_function_add_interface(func, intf); + + return func; +} +struct udclass msc_class = +{ + .rt_usbd_function_create = rt_usbd_function_mstorage_create +}; + +int rt_usbd_msc_class_register(void) +{ + rt_usbd_class_register(&msc_class); + return 0; +} +INIT_PREV_EXPORT(rt_usbd_msc_class_register); + +#endif diff --git a/components/legacy/usb/usbdevice/class/mstorage.h b/components/legacy/usb/usbdevice/class/mstorage.h new file mode 100644 index 0000000000..812be4c190 --- /dev/null +++ b/components/legacy/usb/usbdevice/class/mstorage.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2012-10-01 Yi Qiu first version + * 2012-12-12 heyuanjie87 add MASS endpoints collection + */ + +#ifndef __MSTORAGE_H__ +#define __MSTORAGE_H__ + +#include <rtthread.h> + +#pragma pack(1) + +struct umass_descriptor +{ +#ifdef RT_USB_DEVICE_COMPOSITE + struct uiad_descriptor iad_desc; +#endif + struct uinterface_descriptor intf_desc; + struct uendpoint_descriptor ep_out_desc; + struct uendpoint_descriptor ep_in_desc; +}; +typedef struct umass_descriptor* umass_desc_t; + +struct capacity_data +{ + rt_uint8_t LastLogicalBlockAddress[4]; + rt_uint8_t BlockLengthInBytes[4]; +}; + +struct request_sense_data +{ + rt_uint8_t ErrorCode:7; + rt_uint8_t Valid:1; + rt_uint8_t Reserved1; + rt_uint8_t SenseKey:4; + rt_uint8_t Reserved2:4; + rt_uint8_t Information[4]; + rt_uint8_t AdditionalSenseLength; + rt_uint8_t Reserved3[4]; + rt_uint8_t AdditionalSenseCode; + rt_uint8_t AdditionalSenseCodeQualifier; + rt_uint8_t Reserved4[4]; +}request_sense_data_t; + +#pragma pack() + +#endif diff --git a/components/legacy/usb/usbdevice/class/ndis.h b/components/legacy/usb/usbdevice/class/ndis.h new file mode 100644 index 0000000000..40a1ffb64e --- /dev/null +++ b/components/legacy/usb/usbdevice/class/ndis.h @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ +/* + * ndis.h + * + * Modified by Colin O'Flynn <[email protected]> + * ntddndis.h modified by Benedikt Spranger <[email protected]> + * + * Thanks to the cygwin development team, + * espacially to Casper S. Hornstrup <[email protected]> + * + * 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 __NDIS_H__ +#define __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 + +/* 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 */ +#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 + +#endif /* __NDIS_H__ */ diff --git a/components/legacy/usb/usbdevice/class/rndis.c b/components/legacy/usb/usbdevice/class/rndis.c new file mode 100644 index 0000000000..d3ea89ee0a --- /dev/null +++ b/components/legacy/usb/usbdevice/class/rndis.c @@ -0,0 +1,1476 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2012-12-24 heyuanjie87 first version + * 2013-04-13 aozima update ethernet driver. + * 2013-04-26 aozima align the desc to 4byte. + * 2013-05-08 aozima pad a dummy when send MAX_PKT_SIZE. + * 2013-05-09 aozima add delay linkup feature. + * 2013-07-09 aozima support respone chain list. + * 2013-07-18 aozima re-initial respone chain list when RNDIS restart. + * 2017-11-25 ZYH fix it and add OS descriptor + * 2019-06-10 ZYH fix hot plug and delay linkup + */ + +#include <rtdevice.h> +#ifdef RT_USB_DEVICE_RNDIS +#include "cdc.h" +#include "rndis.h" +#include "ndis.h" + +/* define RNDIS_DELAY_LINK_UP by menuconfig for delay linkup */ + +#define DBG_LEVEL DBG_WARNING +#define DBG_SECTION_NAME "RNDIS" +#include <rtdbg.h> + +#define RNDIS_INTF_STR_INDEX 12 +/* RT-Thread LWIP ethernet interface */ +#include <netif/ethernetif.h> + + +struct rt_rndis_response +{ + struct rt_list_node list; + const void * buffer; +}; + +#define MAX_ADDR_LEN 6 +struct rt_rndis_eth +{ + /* inherit from ethernet device */ + struct eth_device parent; + struct ufunction *func; + /* interface address info */ + rt_uint8_t host_addr[MAX_ADDR_LEN]; + rt_uint8_t dev_addr[MAX_ADDR_LEN]; + +#ifdef RNDIS_DELAY_LINK_UP + struct rt_timer timer; +#endif /* RNDIS_DELAY_LINK_UP */ + + rt_align(4) + rt_uint8_t rx_pool[512]; + rt_align(4) + rt_uint8_t tx_pool[512]; + + rt_uint32_t cmd_pool[2]; + rt_align(4) + char rx_buffer[sizeof(struct rndis_packet_msg) + USB_ETH_MTU + 14]; + rt_size_t rx_offset; + rt_size_t rx_length; + rt_bool_t rx_flag; + rt_bool_t rx_frist; + + rt_align(4) + char tx_buffer[sizeof(struct rndis_packet_msg) + USB_ETH_MTU + 14]; + struct rt_semaphore tx_buffer_free; + + struct rt_list_node response_list; + rt_bool_t need_notify; + struct cdc_eps eps; +}; +typedef struct rt_rndis_eth * rt_rndis_eth_t; +static rt_uint32_t oid_packet_filter = 0x0000000; + +rt_align(4) +static struct udevice_descriptor _dev_desc = +{ + USB_DESC_LENGTH_DEVICE, /* bLength */ + USB_DESC_TYPE_DEVICE, /* type */ + USB_BCD_VERSION, /* bcdUSB */ + 0xEF, /* bDeviceClass */ + 0x04, /* bDeviceSubClass */ + 0x01, /* bDeviceProtocol */ + USB_CDC_BUFSIZE, /* bMaxPacketSize0 */ + _VENDOR_ID, /* idVendor */ + _PRODUCT_ID, /* idProduct */ + USB_BCD_DEVICE, /* bcdDevice */ + USB_STRING_MANU_INDEX, /* iManufacturer */ + USB_STRING_PRODUCT_INDEX, /* iProduct */ + USB_STRING_SERIAL_INDEX, /* iSerialNumber */ + USB_DYNAMIC /* bNumConfigurations */ +}; + +/* communcation interface descriptor */ +rt_align(4) +const static struct ucdc_comm_descriptor _comm_desc = +{ +#ifdef RT_USB_DEVICE_COMPOSITE + /* Interface Association Descriptor */ + { + USB_DESC_LENGTH_IAD, + USB_DESC_TYPE_IAD, + USB_DYNAMIC, + 0x02, + USB_CDC_CLASS_COMM, + USB_CDC_SUBCLASS_ACM, + USB_CDC_PROTOCOL_VENDOR, + 0x00, + }, +#endif + /* Interface Descriptor */ + { + USB_DESC_LENGTH_INTERFACE, + USB_DESC_TYPE_INTERFACE, + USB_DYNAMIC, + 0x00, + 0x01, + USB_CDC_CLASS_COMM, + USB_CDC_SUBCLASS_ACM, + USB_CDC_PROTOCOL_VENDOR, +#ifdef RT_USB_DEVICE_COMPOSITE + RNDIS_INTF_STR_INDEX, +#else + 0x00, +#endif + }, + /* Header Functional Descriptor */ + { + 0x05, + USB_CDC_CS_INTERFACE, + USB_CDC_SCS_HEADER, + 0x0110, + }, + /* Call Management Functional Descriptor */ + { + 0x05, + USB_CDC_CS_INTERFACE, + USB_CDC_SCS_CALL_MGMT, + 0x00, + USB_DYNAMIC, + }, + /* Abstract Control Management Functional Descriptor */ + { + 0x04, + USB_CDC_CS_INTERFACE, + USB_CDC_SCS_ACM, + 0x02, + }, + /* Union Functional Descriptor */ + { + 0x05, + USB_CDC_CS_INTERFACE, + USB_CDC_SCS_UNION, + USB_DYNAMIC, + USB_DYNAMIC, + }, + /* Endpoint Descriptor */ + { + USB_DESC_LENGTH_ENDPOINT, + USB_DESC_TYPE_ENDPOINT, + USB_DIR_IN | USB_DYNAMIC, + USB_EP_ATTR_INT, + 0x08, + 0x0A, + }, +}; + +/* data interface descriptor */ +rt_align(4) +const static struct ucdc_data_descriptor _data_desc = +{ + /* interface descriptor */ + { + USB_DESC_LENGTH_INTERFACE, + USB_DESC_TYPE_INTERFACE, + USB_DYNAMIC, + 0x00, + 0x02, + USB_CDC_CLASS_DATA, + 0x00, + 0x00, + 0x00, + }, + /* endpoint, bulk out */ + { + USB_DESC_LENGTH_ENDPOINT, + USB_DESC_TYPE_ENDPOINT, + USB_DIR_OUT | USB_DYNAMIC, + USB_EP_ATTR_BULK, + USB_DYNAMIC, + 0x00, + }, + /* endpoint, bulk in */ + { + USB_DESC_LENGTH_ENDPOINT, + USB_DESC_TYPE_ENDPOINT, + USB_DYNAMIC | USB_DIR_IN, + USB_EP_ATTR_BULK, + USB_DYNAMIC, + 0x00, + }, +}; + +rt_align(4) +const static char* _ustring[] = +{ + "Language", /* LANGID */ + "RT-Thread Team.", /* MANU */ + "RT-Thread RNDIS device", /* PRODUCT */ + "1.1.0", /* SERIAL */ + "Configuration", /* CONFIG */ + "Interface", /* INTERFACE */ + USB_STRING_OS +}; + +rt_align(4) +struct usb_os_function_comp_id_descriptor rndis_func_comp_id_desc = +{ + .bFirstInterfaceNumber = USB_DYNAMIC, + .reserved1 = 0x01, + .compatibleID = {'R', 'N', 'D', 'I', 'S', 0x00, 0x00, 0x00}, + .subCompatibleID = {'5', '1', '6', '2', '0', '0', '1', 0x00}, + .reserved2 = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00} +}; + +//FS and HS needed +rt_align(4) +static struct usb_qualifier_descriptor dev_qualifier = +{ + sizeof(dev_qualifier), //bLength + USB_DESC_TYPE_DEVICEQUALIFIER, //bDescriptorType + 0x0200, //bcdUSB + USB_CLASS_CDC, //bDeviceClass + USB_CDC_SUBCLASS_ACM, //bDeviceSubClass + USB_CDC_PROTOCOL_VENDOR, //bDeviceProtocol + 64, //bMaxPacketSize0 + 0x01, //bNumConfigurations + 0, +}; + +/* supported OIDs */ +rt_align(4) +const static rt_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 rt_uint8_t rndis_message_buffer[RNDIS_MESSAGE_BUFFER_SIZE]; + +static void _rndis_response_available(ufunction_t func) +{ + rt_rndis_eth_t device = (rt_rndis_eth_t)func->user_data; + rt_uint32_t * data; + if(device->need_notify == RT_TRUE) + { + device->need_notify = RT_FALSE; + data = (rt_uint32_t *)device->eps.ep_cmd->buffer; + data[0] = RESPONSE_AVAILABLE; + data[1] = 0; + device->eps.ep_cmd->request.buffer = device->eps.ep_cmd->buffer; + device->eps.ep_cmd->request.size = 8; + device->eps.ep_cmd->request.req_type = UIO_REQUEST_WRITE; + rt_usbd_io_request(func->device, device->eps.ep_cmd, &device->eps.ep_cmd->request); + } +} + +static rt_err_t _rndis_init_response(ufunction_t func, rndis_init_msg_t msg) +{ + rndis_init_cmplt_t resp; + struct rt_rndis_response * response; + + response = rt_malloc(sizeof(struct rt_rndis_response)); + resp = rt_malloc(sizeof(struct rndis_init_cmplt)); + + if( (response == RT_NULL) || (resp == RT_NULL) ) + { + LOG_E("%s,%d: no memory!", __func__, __LINE__); + + if(response != RT_NULL) + rt_free(response); + + if(resp != RT_NULL) + rt_free(resp); + + return -RT_ENOMEM; + } + + resp->RequestId = msg->RequestId; + resp->MessageType = REMOTE_NDIS_INITIALIZE_CMPLT; + resp->MessageLength = sizeof(struct rndis_init_cmplt); + resp->MajorVersion = RNDIS_MAJOR_VERSION; + resp->MinorVersion = RNDIS_MAJOR_VERSION; + resp->Status = RNDIS_STATUS_SUCCESS; + resp->DeviceFlags = RNDIS_DF_CONNECTIONLESS; + resp->Medium = RNDIS_MEDIUM_802_3; + resp->MaxPacketsPerTransfer = 1; + resp->MaxTransferSize = USB_ETH_MTU + 58; /* Space for 1280 IP buffer, Ethernet Header, + RNDIS messages */ + resp->PacketAlignmentFactor = 3; + resp->AfListOffset = 0; + resp->AfListSize = 0; + + response->buffer = resp; + + { + rt_base_t level = rt_hw_interrupt_disable(); + rt_list_insert_before(&((rt_rndis_eth_t)func->user_data)->response_list, &response->list); + rt_hw_interrupt_enable(level); + } + + + return RT_EOK; +} + +static rndis_query_cmplt_t _create_resp(rt_size_t size) +{ + rndis_query_cmplt_t resp; + + resp = rt_malloc(sizeof(struct rndis_query_cmplt) + size); + + if(resp == RT_NULL) + { + LOG_E("%s,%d: no memory!", __func__, __LINE__); + return RT_NULL; + } + + resp->InformationBufferLength = size; + + return resp; +} + +static void _copy_resp(rndis_query_cmplt_t resp, const void * buffer) +{ + char * resp_buffer = (char *)resp + sizeof(struct rndis_query_cmplt); + rt_memcpy(resp_buffer, buffer, resp->InformationBufferLength); +} + +static void _set_resp(rndis_query_cmplt_t resp, rt_uint32_t value) +{ + rt_uint32_t * response = (rt_uint32_t *)((char *)resp + sizeof(struct rndis_query_cmplt)); + *response = value; +} + +static rt_err_t _rndis_query_response(ufunction_t func,rndis_query_msg_t msg) +{ + rndis_query_cmplt_t resp = RT_NULL; + struct rt_rndis_response * response; + rt_err_t ret = RT_EOK; + + switch (msg->Oid) + { + /* + * general OIDs + */ + case OID_GEN_SUPPORTED_LIST: + resp = _create_resp(sizeof(oid_supported_list)); + if(resp == RT_NULL) break; + _copy_resp(resp, oid_supported_list); + break; + + case OID_GEN_PHYSICAL_MEDIUM: + resp = _create_resp(4); + if(resp == RT_NULL) break; + _set_resp(resp, NDIS_MEDIUM_802_3); + break; + + case OID_GEN_MAXIMUM_FRAME_SIZE: + case OID_GEN_TRANSMIT_BLOCK_SIZE: + case OID_GEN_RECEIVE_BLOCK_SIZE: + resp = _create_resp(4); + if(resp == RT_NULL) break; + _set_resp(resp, USB_ETH_MTU); + break; + + case OID_GEN_MAXIMUM_TOTAL_SIZE: + resp = _create_resp(4); + if(resp == RT_NULL) break; + _set_resp(resp, USB_ETH_MTU + RNDIS_MESSAGE_BUFFER_SIZE); + break; + + case OID_GEN_LINK_SPEED: + resp = _create_resp(4); + if(resp == RT_NULL) break; + _set_resp(resp, (func->device->dcd->device_is_hs ? (480UL * 1000 *1000) : (12UL * 1000 * 1000)) / 100); + break; + + case OID_GEN_MEDIA_CONNECT_STATUS: + /* link_status */ + resp = _create_resp(4); + if(resp == RT_NULL) break; + +#ifdef RNDIS_DELAY_LINK_UP + if(((rt_rndis_eth_t)func->user_data)->parent.link_status) + { + _set_resp(resp, NDIS_MEDIA_STATE_CONNECTED); + } + else + { + _set_resp(resp, NDIS_MEDIA_STATE_DISCONNECTED); + } +#else + _set_resp(resp, NDIS_MEDIA_STATE_CONNECTED); +#endif /* RNDIS_DELAY_LINK_UP */ + break; + + case OID_GEN_VENDOR_ID: + resp = _create_resp(4); + if(resp == RT_NULL) break; + _set_resp(resp, 0x12345678); /* only for test */ + break; + + case OID_GEN_VENDOR_DESCRIPTION: + { + const char vendor_desc[] = "RT-Thread RNDIS"; + + resp = _create_resp(sizeof(vendor_desc)); + if(resp == RT_NULL) break; + _copy_resp(resp, vendor_desc); + } + break; + + case OID_GEN_VENDOR_DRIVER_VERSION: + resp = _create_resp(4); + if(resp == RT_NULL) break; + _set_resp(resp, 0x0000200); + break; + + /* statistics OIDs */ + case OID_GEN_XMIT_OK: + case OID_GEN_RCV_OK: + resp = _create_resp(4); + if(resp == RT_NULL) break; + _set_resp(resp, 1); + break; + + case OID_GEN_XMIT_ERROR: + case OID_GEN_RCV_ERROR: + case OID_GEN_RCV_NO_BUFFER: + resp = _create_resp(4); + if(resp == RT_NULL) break; + _set_resp(resp, 0); + break; + + /* + * ieee802.3 OIDs + */ + case OID_802_3_MAXIMUM_LIST_SIZE: + resp = _create_resp(4); + if(resp == RT_NULL) break; + _set_resp(resp, 1); + break; + + case OID_802_3_PERMANENT_ADDRESS: + case OID_802_3_CURRENT_ADDRESS: + resp = _create_resp(sizeof(((rt_rndis_eth_t)func->user_data)->host_addr)); + if(resp == RT_NULL) break; + _copy_resp(resp, ((rt_rndis_eth_t)func->user_data)->host_addr); + break; + + case OID_802_3_MULTICAST_LIST: + resp = _create_resp(4); + if(resp == RT_NULL) break; + _set_resp(resp, 0xE000000); + break; + + case OID_802_3_MAC_OPTIONS: + resp = _create_resp(4); + if(resp == RT_NULL) break; + _set_resp(resp, 0); + break; + + default: + LOG_W("Not support OID %X", msg->Oid); + ret = -RT_ERROR; + break; + } + + response = rt_malloc(sizeof(struct rt_rndis_response)); + if( (response == RT_NULL) || (resp == RT_NULL) ) + { + LOG_E("%s,%d: no memory!", __func__, __LINE__); + + if(response != RT_NULL) + rt_free(response); + + if(resp != RT_NULL) + rt_free(resp); + + return -RT_ENOMEM; + } + + resp->RequestId = msg->RequestId; + resp->MessageType = REMOTE_NDIS_QUERY_CMPLT; + resp->InformationBufferOffset = 16; + + resp->Status = RNDIS_STATUS_SUCCESS; + resp->MessageLength = sizeof(struct rndis_query_cmplt) + resp->InformationBufferLength; + + response->buffer = resp; + + { + rt_base_t level = rt_hw_interrupt_disable(); + rt_list_insert_before(&((rt_rndis_eth_t)func->user_data)->response_list, &response->list); + rt_hw_interrupt_enable(level); + } + + return ret; +} + +static rt_err_t _rndis_set_response(ufunction_t func,rndis_set_msg_t msg) +{ + rndis_set_cmplt_t resp; + struct rt_rndis_response * response; + + response = rt_malloc(sizeof(struct rt_rndis_response)); + resp = rt_malloc(sizeof(struct rndis_set_cmplt)); + + if( (response == RT_NULL) || (resp == RT_NULL) ) + { + LOG_E("%s,%d: no memory!", __func__, __LINE__); + + if(response != RT_NULL) + rt_free(response); + + if(resp != RT_NULL) + rt_free(resp); + + return -RT_ENOMEM; + } + + resp->RequestId = msg->RequestId; + resp->MessageType = REMOTE_NDIS_SET_CMPLT; + resp->MessageLength = sizeof(struct rndis_set_cmplt); + + switch (msg->Oid) + { + case OID_GEN_CURRENT_PACKET_FILTER: + oid_packet_filter = *((rt_uint32_t *)((rt_uint8_t *)&(msg->RequestId) + \ + msg->InformationBufferOffset)); + /* TODO: make complier happy */ + oid_packet_filter = oid_packet_filter; + + LOG_D("OID_GEN_CURRENT_PACKET_FILTER"); + +#ifdef RNDIS_DELAY_LINK_UP + /* link up. */ + rt_timer_start(&((rt_rndis_eth_t)func->user_data)->timer); +#else + eth_device_linkchange(&((rt_rndis_eth_t)func->user_data)->parent, RT_TRUE); +#endif /* RNDIS_DELAY_LINK_UP */ + break; + + case OID_802_3_MULTICAST_LIST: + break; + + default: + LOG_W("Unknow rndis set 0x%02X", msg->Oid); + resp->Status = RNDIS_STATUS_FAILURE; + return RT_EOK; + } + + resp->Status = RNDIS_STATUS_SUCCESS; + + response->buffer = resp; + + { + rt_base_t level = rt_hw_interrupt_disable(); + rt_list_insert_before(&((rt_rndis_eth_t)func->user_data)->response_list, &response->list); + rt_hw_interrupt_enable(level); + } + + return RT_EOK; +} + +static rt_err_t _rndis_reset_response(ufunction_t func,rndis_set_msg_t msg) +{ + struct rndis_reset_cmplt * resp; + struct rt_rndis_response * response; + + response = rt_malloc(sizeof(struct rt_rndis_response)); + resp = rt_malloc(sizeof(struct rndis_reset_cmplt)); + + if( (response == RT_NULL) || (resp == RT_NULL) ) + { + LOG_E("%s,%d: no memory!", __func__, __LINE__); + + if(response != RT_NULL) + rt_free(response); + + if(resp != RT_NULL) + rt_free(resp); + + return -RT_ENOMEM; + } + + /* reset packet filter */ + + oid_packet_filter = 0x0000000; + + /* link down eth */ + + eth_device_linkchange(&((rt_rndis_eth_t)func->user_data)->parent, RT_FALSE); + + /* reset eth rx tx */ + ((rt_rndis_eth_t)func->user_data)->rx_frist = RT_TRUE; + ((rt_rndis_eth_t)func->user_data)->rx_flag = RT_FALSE; + + + resp->MessageType = REMOTE_NDIS_RESET_CMPLT; + resp->MessageLength = sizeof(struct rndis_reset_cmplt); + resp->Status = RNDIS_STATUS_SUCCESS; + resp->AddressingReset = 1; + + response->buffer = resp; + + { + rt_base_t level = rt_hw_interrupt_disable(); + rt_list_insert_before(&((rt_rndis_eth_t)func->user_data)->response_list, &response->list); + rt_hw_interrupt_enable(level); + } + + return RT_EOK; +} + +static rt_err_t _rndis_keepalive_response(ufunction_t func,rndis_keepalive_msg_t msg) +{ + rndis_keepalive_cmplt_t resp; + struct rt_rndis_response * response; + + response = rt_malloc(sizeof(struct rt_rndis_response)); + resp = rt_malloc(sizeof(struct rndis_keepalive_cmplt)); + + if( (response == RT_NULL) || (resp == RT_NULL) ) + { + LOG_E("%s,%d: no memory!", __func__, __LINE__); + + if(response != RT_NULL) + rt_free(response); + + if(resp != RT_NULL) + rt_free(resp); + + return -RT_ENOMEM; + } + + resp->MessageType = REMOTE_NDIS_KEEPALIVE_CMPLT; + resp->MessageLength = sizeof(struct rndis_keepalive_cmplt); + resp->Status = RNDIS_STATUS_SUCCESS; + + response->buffer = resp; + + { + rt_base_t level = rt_hw_interrupt_disable(); + rt_list_insert_before(&((rt_rndis_eth_t)func->user_data)->response_list, &response->list); + rt_hw_interrupt_enable(level); + } + + return RT_EOK; +} + +static rt_err_t _rndis_msg_parser(ufunction_t func, rt_uint8_t *msg) +{ + rt_err_t ret = -RT_ERROR; + + switch (((rndis_gen_msg_t) msg)->MessageType) + { + case REMOTE_NDIS_INITIALIZE_MSG: + LOG_D("REMOTE_NDIS_INITIALIZE_MSG"); + ret = _rndis_init_response(func, (rndis_init_msg_t) msg); + break; + + case REMOTE_NDIS_HALT_MSG: + LOG_D("REMOTE_NDIS_HALT_MSG"); + /* link down. */ + eth_device_linkchange(&((rt_rndis_eth_t)func->user_data)->parent, RT_FALSE); + + /* reset eth rx tx */ + ((rt_rndis_eth_t)func->user_data)->rx_frist = RT_TRUE; + ((rt_rndis_eth_t)func->user_data)->rx_flag = RT_FALSE; + break; + + case REMOTE_NDIS_QUERY_MSG: + LOG_D("REMOTE_NDIS_QUERY_MSG"); + ret = _rndis_query_response(func,(rndis_query_msg_t) msg); + break; + + case REMOTE_NDIS_SET_MSG: + LOG_D("REMOTE_NDIS_SET_MSG"); + ret = _rndis_set_response(func,(rndis_set_msg_t) msg); + break; + + case REMOTE_NDIS_RESET_MSG: + LOG_D("REMOTE_NDIS_RESET_MSG"); + ret = _rndis_reset_response(func,(rndis_set_msg_t) msg); + break; + + case REMOTE_NDIS_KEEPALIVE_MSG: + LOG_D("REMOTE_NDIS_KEEPALIVE_MSG"); + ret = _rndis_keepalive_response(func,(rndis_keepalive_msg_t) msg); + break; + + default: + LOG_W("not support RNDIS msg %X", ((rndis_gen_msg_t) msg)->MessageType); + ret = -RT_ERROR; + break; + } + + if (ret == RT_EOK) + _rndis_response_available(func); + + return ret; +} + +static ufunction_t function = RT_NULL; +static rt_err_t send_encapsulated_command_done(udevice_t device, rt_size_t size) +{ + if(function != RT_NULL) + { + dcd_ep0_send_status(device->dcd); + _rndis_msg_parser(function, rndis_message_buffer); + function = RT_NULL; + } + return RT_EOK; +} +//#error here have bug ep 0x82 send failed +static rt_err_t _rndis_send_encapsulated_command(ufunction_t func, ureq_t setup) +{ + RT_ASSERT(setup->wLength <= sizeof(rndis_message_buffer)); + function = func; + rt_usbd_ep0_read(func->device,rndis_message_buffer,setup->wLength,send_encapsulated_command_done); + + return RT_EOK; +} + +static rt_err_t _rndis_get_encapsulated_response(ufunction_t func, ureq_t setup) +{ + rndis_gen_msg_t msg; + struct rt_rndis_response * response; + + if(rt_list_isempty(&((rt_rndis_eth_t)func->user_data)->response_list)) + { + LOG_D("response_list is empty!"); + ((rt_rndis_eth_t)func->user_data)->need_notify = RT_TRUE; + return RT_EOK; + } + + response = (struct rt_rndis_response *)((rt_rndis_eth_t)func->user_data)->response_list.next; + + msg = (rndis_gen_msg_t)response->buffer; + rt_usbd_ep0_write(func->device, (void*)msg, msg->MessageLength); + + { + rt_base_t level = rt_hw_interrupt_disable(); + rt_list_remove(&response->list); + rt_hw_interrupt_enable(level); + } + + rt_free((void *)response->buffer); + rt_free(response); + + if(!rt_list_isempty(&((rt_rndis_eth_t)func->user_data)->response_list)) + { + rt_uint32_t * data; + + LOG_I("auto append next response!"); + data = (rt_uint32_t *)((rt_rndis_eth_t)func->user_data)->eps.ep_cmd->buffer; + data[0] = RESPONSE_AVAILABLE; + data[1] = 0; + ((rt_rndis_eth_t)func->user_data)->eps.ep_cmd->request.buffer = ((rt_rndis_eth_t)func->user_data)->eps.ep_cmd->buffer; + ((rt_rndis_eth_t)func->user_data)->eps.ep_cmd->request.size = 8; + ((rt_rndis_eth_t)func->user_data)->eps.ep_cmd->request.req_type = UIO_REQUEST_WRITE; + rt_usbd_io_request(func->device, ((rt_rndis_eth_t)func->user_data)->eps.ep_cmd, &((rt_rndis_eth_t)func->user_data)->eps.ep_cmd->request); + } + else + { + ((rt_rndis_eth_t)func->user_data)->need_notify = RT_TRUE; + } + + return RT_EOK; +} + +/** + * This function will handle rndis interface request. + * + * @param device the usb device object. + * @param setup the setup request. + * + * @return RT_EOK on successful. + */ +static rt_err_t _interface_handler(ufunction_t func, ureq_t setup) +{ + RT_ASSERT(func != RT_NULL); + RT_ASSERT(setup != RT_NULL); + + switch(setup->bRequest) + { + case CDC_SEND_ENCAPSULATED_COMMAND: + _rndis_send_encapsulated_command(func, setup); + break; + + case CDC_GET_ENCAPSULATED_RESPONSE: + _rndis_get_encapsulated_response(func, setup); + break; + + default: + LOG_W("unkown setup->request 0x%02X !", setup->bRequest); + break; + } + + return RT_EOK; +} + +/** + * This function will handle rndis bulk in endpoint request. + * + * @param device the usb device object. + * @param size request size. + * + * @return RT_EOK. + */ + +static rt_err_t _ep_in_handler(ufunction_t func, rt_size_t size) +{ + rt_sem_release(&((rt_rndis_eth_t)func->user_data)->tx_buffer_free); + return RT_EOK; +} + +/** + * This function will handle RNDIS bulk out endpoint request. + * + * @param device the usb device object. + * @param size request size. + * + * @return RT_EOK. + */ +static rt_err_t _ep_out_handler(ufunction_t func, rt_size_t size) +{ + cdc_eps_t eps; + char* data = RT_NULL; + + eps = (cdc_eps_t)&((rt_rndis_eth_t)func->user_data)->eps; + data = (char*)eps->ep_out->buffer; + + if(((rt_rndis_eth_t)func->user_data)->rx_frist == RT_TRUE) + { + rndis_packet_msg_t msg = (rndis_packet_msg_t)data; + + ((rt_rndis_eth_t)func->user_data)->rx_length = msg->DataLength; + ((rt_rndis_eth_t)func->user_data)->rx_offset = 0; + + if (size >= 44) + { + data += sizeof(struct rndis_packet_msg); + size -= sizeof(struct rndis_packet_msg); + ((rt_rndis_eth_t)func->user_data)->rx_frist = RT_FALSE; + rt_memcpy(&((rt_rndis_eth_t)func->user_data)->rx_buffer[((rt_rndis_eth_t)func->user_data)->rx_offset], data, size); + ((rt_rndis_eth_t)func->user_data)->rx_offset += size; + } + } + else + { + rt_memcpy(&((rt_rndis_eth_t)func->user_data)->rx_buffer[((rt_rndis_eth_t)func->user_data)->rx_offset], data, size); + ((rt_rndis_eth_t)func->user_data)->rx_offset += size; + } + + if(((rt_rndis_eth_t)func->user_data)->rx_offset >= ((rt_rndis_eth_t)func->user_data)->rx_length) + { + ((rt_rndis_eth_t)func->user_data)->rx_frist = RT_TRUE; + ((rt_rndis_eth_t)func->user_data)->rx_flag = RT_TRUE; + eth_device_ready(&(((rt_rndis_eth_t)func->user_data)->parent)); + } + else + { + eps->ep_out->request.buffer = eps->ep_out->buffer; + eps->ep_out->request.size = EP_MAXPACKET(eps->ep_out); + eps->ep_out->request.req_type = UIO_REQUEST_READ_BEST; + rt_usbd_io_request(func->device, eps->ep_out, &eps->ep_out->request); + } + + return RT_EOK; +} + +/** + * This function will handle RNDIS interrupt in endpoint request. + * + * @param device the usb device object. + * @param size request size. + * + * @return RT_EOK. + */ +static rt_err_t _ep_cmd_handler(ufunction_t func, rt_size_t size) +{ +// _rndis_response_available(func); + return RT_EOK; +} + +/** + * This function will run cdc class, it will be called on handle set configuration request. + * + * @param device the usb device object. + * + * @return RT_EOK on successful. + */ +static rt_err_t _function_enable(ufunction_t func) +{ + cdc_eps_t eps; + + LOG_I("plugged in"); + + eps = (cdc_eps_t)&((rt_rndis_eth_t)func->user_data)->eps; + eps->ep_in->buffer = ((rt_rndis_eth_t)func->user_data)->tx_pool; + eps->ep_out->buffer = ((rt_rndis_eth_t)func->user_data)->rx_pool; + eps->ep_cmd->buffer = (rt_uint8_t*)((rt_rndis_eth_t)func->user_data)->cmd_pool; + + eps->ep_out->request.buffer = eps->ep_out->buffer; + eps->ep_out->request.size = EP_MAXPACKET(eps->ep_out); + eps->ep_out->request.req_type = UIO_REQUEST_READ_BEST; + rt_usbd_io_request(func->device, eps->ep_out, &eps->ep_out->request); + + ((rt_rndis_eth_t)func->user_data)->rx_flag = RT_FALSE; + ((rt_rndis_eth_t)func->user_data)->rx_frist = RT_TRUE; + // eth_device_ready(&(((rt_rndis_eth_t)func->user_data)->parent)); + +#ifdef RNDIS_DELAY_LINK_UP + /* stop link up timer. */ + rt_timer_stop(&((rt_rndis_eth_t)func->user_data)->timer); +#endif /* RNDIS_DELAY_LINK_UP */ + + /* clean resp chain list. */ + { + struct rt_rndis_response * response; + rt_base_t level = rt_hw_interrupt_disable(); + + while(!rt_list_isempty(&((rt_rndis_eth_t)func->user_data)->response_list)) + { + response = (struct rt_rndis_response *)((rt_rndis_eth_t)func->user_data)->response_list.next; + + rt_list_remove(&response->list); + rt_free((void *)response->buffer); + rt_free(response); + } + + ((rt_rndis_eth_t)func->user_data)->need_notify = RT_TRUE; + rt_hw_interrupt_enable(level); + } + + return RT_EOK; +} + +/** + * This function will stop cdc class, it will be called on handle set configuration request. + * + * @param device the usb device object. + * + * @return RT_EOK on successful. + */ +static rt_err_t _function_disable(ufunction_t func) +{ + LOG_I("plugged out"); + +#ifdef RNDIS_DELAY_LINK_UP + /* stop link up timer. */ + rt_timer_stop(&((rt_rndis_eth_t)func->user_data)->timer); +#endif /* RNDIS_DELAY_LINK_UP */ + + /* clean resp chain list. */ + { + struct rt_rndis_response * response; + rt_base_t level = rt_hw_interrupt_disable(); + + while(!rt_list_isempty(&((rt_rndis_eth_t)func->user_data)->response_list)) + { + response = (struct rt_rndis_response *)((rt_rndis_eth_t)func->user_data)->response_list.next; + LOG_D("remove resp chain list!"); + + rt_list_remove(&response->list); + rt_free((void *)response->buffer); + rt_free(response); + } + + ((rt_rndis_eth_t)func->user_data)->need_notify = RT_TRUE; + rt_hw_interrupt_enable(level); + } + + + /* link down. */ + eth_device_linkchange(&((rt_rndis_eth_t)func->user_data)->parent, RT_FALSE); + + /* reset eth rx tx */ + ((rt_rndis_eth_t)func->user_data)->rx_frist = RT_TRUE; + ((rt_rndis_eth_t)func->user_data)->rx_flag = RT_FALSE; + + return RT_EOK; +} + + +static struct ufunction_ops ops = +{ + _function_enable, + _function_disable, + RT_NULL, +}; + +/** + * This function will configure cdc descriptor. + * + * @param comm the communication interface number. + * @param data the data interface number. + * + * @return RT_EOK on successful. + */ +static rt_err_t _cdc_descriptor_config(ucdc_comm_desc_t comm, rt_uint8_t cintf_nr, ucdc_data_desc_t data, rt_uint8_t dintf_nr, rt_uint8_t device_is_hs) +{ + comm->call_mgmt_desc.data_interface = dintf_nr; + comm->union_desc.master_interface = cintf_nr; + comm->union_desc.slave_interface0 = dintf_nr; +#ifdef RT_USB_DEVICE_COMPOSITE + comm->iad_desc.bFirstInterface = cintf_nr; +#endif + data->ep_out_desc.wMaxPacketSize = device_is_hs ? 512 : 64; + data->ep_in_desc.wMaxPacketSize = device_is_hs ? 512 : 64; + return RT_EOK; +} + +#ifdef RT_USING_LWIP +/* initialize the interface */ +static rt_err_t rt_rndis_eth_init(rt_device_t dev) +{ + return RT_EOK; +} + +static rt_err_t rt_rndis_eth_open(rt_device_t dev, rt_uint16_t oflag) +{ + return RT_EOK; +} + +static rt_err_t rt_rndis_eth_close(rt_device_t dev) +{ + return RT_EOK; +} + +static rt_ssize_t rt_rndis_eth_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size) +{ + rt_set_errno(-RT_ENOSYS); + return 0; +} + +static rt_ssize_t rt_rndis_eth_write (rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size) +{ + rt_set_errno(-RT_ENOSYS); + return 0; +} +static rt_err_t rt_rndis_eth_control(rt_device_t dev, int cmd, void *args) +{ + rt_rndis_eth_t rndis_eth_dev = (rt_rndis_eth_t)dev; + switch(cmd) + { + case NIOCTL_GADDR: + /* get mac address */ + if(args) rt_memcpy(args, rndis_eth_dev->dev_addr, MAX_ADDR_LEN); + else return -RT_ERROR; + break; + + default : + break; + } + + return RT_EOK; +} + +/* ethernet device interface */ + + +/* reception packet. */ +struct pbuf *rt_rndis_eth_rx(rt_device_t dev) +{ + struct pbuf* p = RT_NULL; + rt_uint32_t offset = 0; + rt_rndis_eth_t device = (rt_rndis_eth_t)dev; + if(device->rx_flag == RT_FALSE) + { + return p; + } + + if(device->rx_length != 0) + { + /* allocate buffer */ + p = pbuf_alloc(PBUF_LINK, device->rx_length, PBUF_RAM); + if (p != RT_NULL) + { + struct pbuf* q; + + for (q = p; q != RT_NULL; q= q->next) + { + /* Copy the received frame into buffer from memory pointed by the current ETHERNET DMA Rx descriptor */ + rt_memcpy(q->payload, + (rt_uint8_t *)((device->rx_buffer) + offset), + q->len); + offset += q->len; + } + } + } + + { + device->rx_flag = RT_FALSE; + device->eps.ep_out->request.buffer = device->eps.ep_out->buffer; + device->eps.ep_out->request.size = EP_MAXPACKET(device->eps.ep_out); + device->eps.ep_out->request.req_type = UIO_REQUEST_READ_BEST; + rt_usbd_io_request(device->func->device, device->eps.ep_out, &device->eps.ep_out->request); + } + + return p; +} + +/* transmit packet. */ +rt_err_t rt_rndis_eth_tx(rt_device_t dev, struct pbuf* p) +{ + struct pbuf* q; + char * buffer; + rt_err_t result = RT_EOK; + rt_rndis_eth_t device = (rt_rndis_eth_t)dev; + + if(!device->parent.link_status) + { + LOG_I("linkdown, drop pkg"); + return RT_EOK; + } + + //RT_ASSERT(p->tot_len < sizeof(device->tx_buffer)); + if(p->tot_len > sizeof(device->tx_buffer)) + { + LOG_W("RNDIS MTU is:%d, but the send packet size is %d", + sizeof(device->tx_buffer), p->tot_len); + p->tot_len = sizeof(device->tx_buffer); + } + + /* wait for buffer free. */ + result = rt_sem_take(&device->tx_buffer_free, rt_tick_from_millisecond(1000)); + if(result != RT_EOK) + { + LOG_W("wait for buffer free timeout"); + /* if cost 1s to wait send done it said that connection is close . drop it */ + rt_sem_release(&device->tx_buffer_free); + return result; + } + + buffer = (char *)&device->tx_buffer + sizeof(struct rndis_packet_msg); + for (q = p; q != NULL; q = q->next) + { + rt_memcpy(buffer, q->payload, q->len); + buffer += q->len; + } + + /* send */ + { + rndis_packet_msg_t msg; + + msg = (rndis_packet_msg_t)&device->tx_buffer; + + msg->MessageType = REMOTE_NDIS_PACKET_MSG; + msg->DataOffset = sizeof(struct rndis_packet_msg) - 8; + msg->DataLength = p->tot_len; + msg->OOBDataLength = 0; + msg->OOBDataOffset = 0; + msg->NumOOBDataElements = 0; + msg->PerPacketInfoOffset = 0; + msg->PerPacketInfoLength = 0; + msg->VcHandle = 0; + msg->Reserved = 0; + msg->MessageLength = sizeof(struct rndis_packet_msg) + p->tot_len; + + if((msg->MessageLength & 0x3F) == 0) + { + /* pad a dummy. */ + msg->MessageLength += 1; + } + + device->eps.ep_in->request.buffer = (void *)&device->tx_buffer; + device->eps.ep_in->request.size = msg->MessageLength; + device->eps.ep_in->request.req_type = UIO_REQUEST_WRITE; + rt_usbd_io_request(device->func->device, device->eps.ep_in, &device->eps.ep_in->request); + } + + return result; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops rndis_device_ops = +{ + rt_rndis_eth_init, + rt_rndis_eth_open, + rt_rndis_eth_close, + rt_rndis_eth_read, + rt_rndis_eth_write, + rt_rndis_eth_control +}; +#endif + +#endif /* RT_USING_LWIP */ + +#ifdef RNDIS_DELAY_LINK_UP + +/** + * This function will set rndis connect status. + * + * @param device the usb device object. + * @param status the connect status. + * + * @return RT_EOK on successful. + */ +static rt_err_t _rndis_indicate_status_msg(ufunction_t func, rt_uint32_t status) +{ + rndis_indicate_status_msg_t resp; + struct rt_rndis_response * response; + + response = rt_malloc(sizeof(struct rt_rndis_response)); + resp = rt_malloc(sizeof(struct rndis_indicate_status_msg)); + + if( (response == RT_NULL) || (resp == RT_NULL) ) + { + LOG_E("%s,%d: no memory!", __func__, __LINE__); + + if(response != RT_NULL) + rt_free(response); + + if(resp != RT_NULL) + rt_free(resp); + + return -RT_ENOMEM; + } + + resp->MessageType = REMOTE_NDIS_INDICATE_STATUS_MSG; + resp->MessageLength = 20; /* sizeof(struct rndis_indicate_status_msg) */ + resp->Status = status; + resp->StatusBufferLength = 0; + resp->StatusBufferOffset = 0; + + response->buffer = resp; + { + rt_base_t level = rt_hw_interrupt_disable(); + rt_list_insert_before(&((rt_rndis_eth_t)func->user_data)->response_list, &response->list); + rt_hw_interrupt_enable(level); + } + + _rndis_response_available(func); + + return RT_EOK; +} + +/* the delay linkup timer handler. */ +static void timer_timeout(void* parameter) +{ + LOG_I("delay link up!"); + _rndis_indicate_status_msg(((rt_rndis_eth_t)parameter)->func, + RNDIS_STATUS_MEDIA_CONNECT); + eth_device_linkchange(&((rt_rndis_eth_t)parameter)->parent, RT_TRUE); +} +#endif /* RNDIS_DELAY_LINK_UP */ + +/** + * This function will create a cdc rndis class instance. + * + * @param device the usb device object. + * + * @return RT_EOK on successful. + */ +ufunction_t rt_usbd_function_rndis_create(udevice_t device) +{ + ufunction_t cdc; + rt_rndis_eth_t _rndis; + cdc_eps_t eps; + uintf_t intf_comm, intf_data; + ualtsetting_t comm_setting, data_setting; + ucdc_data_desc_t data_desc; + ucdc_comm_desc_t comm_desc; + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + + /* set usb device string description */ +#ifdef RT_USB_DEVICE_COMPOSITE + rt_usbd_device_set_interface_string(device, RNDIS_INTF_STR_INDEX, _ustring[2]); +#else + rt_usbd_device_set_string(device, _ustring); +#endif + /* create a cdc class */ + cdc = rt_usbd_function_new(device, &_dev_desc, &ops); + rt_usbd_device_set_qualifier(device, &dev_qualifier); + _rndis= rt_malloc(sizeof(struct rt_rndis_eth)); + + if(_rndis == RT_NULL) + { + LOG_E("%s,%d: no memory!", __func__, __LINE__); + return RT_NULL; + } + + rt_memset(_rndis, 0, sizeof(struct rt_rndis_eth)); + cdc->user_data = _rndis; + + _rndis->func = cdc; + /* create a cdc class endpoints collection */ + eps = &_rndis->eps; + /* create a cdc communication interface and a cdc data interface */ + intf_comm = rt_usbd_interface_new(device, _interface_handler); + intf_data = rt_usbd_interface_new(device, _interface_handler); + + if((intf_comm == RT_NULL) || (intf_data == RT_NULL)) + { + LOG_E("%s,%d: no memory!", __func__, __LINE__); + + if(intf_comm != RT_NULL) + rt_free(intf_comm); + + if(intf_data != RT_NULL) + rt_free(intf_data); + + return RT_NULL; + } + + /* create a communication alternate setting and a data alternate setting */ + comm_setting = rt_usbd_altsetting_new(sizeof(struct ucdc_comm_descriptor)); + data_setting = rt_usbd_altsetting_new(sizeof(struct ucdc_data_descriptor)); + + /* config desc in alternate setting */ + rt_usbd_altsetting_config_descriptor(comm_setting, &_comm_desc, + (rt_off_t)&((ucdc_comm_desc_t)0)->intf_desc); + rt_usbd_altsetting_config_descriptor(data_setting, &_data_desc, 0); + /* configure the cdc interface descriptor */ + _cdc_descriptor_config(comm_setting->desc, intf_comm->intf_num, data_setting->desc, intf_data->intf_num, device->dcd->device_is_hs); + + /* create a command endpoint */ + comm_desc = (ucdc_comm_desc_t)comm_setting->desc; + eps->ep_cmd = rt_usbd_endpoint_new(&comm_desc->ep_desc, _ep_cmd_handler); + /* add the command endpoint to the cdc communication interface */ + rt_usbd_altsetting_add_endpoint(comm_setting, eps->ep_cmd); + + /* add the communication alternate setting to the communication interface, + then set default setting of the interface */ + rt_usbd_interface_add_altsetting(intf_comm, comm_setting); + rt_usbd_set_altsetting(intf_comm, 0); + /* add the communication interface to the cdc class */ + rt_usbd_function_add_interface(cdc, intf_comm); + + /* create a bulk in and a bulk out endpoint */ + data_desc = (ucdc_data_desc_t)data_setting->desc; + eps->ep_out = rt_usbd_endpoint_new(&data_desc->ep_out_desc, _ep_out_handler); + eps->ep_in = rt_usbd_endpoint_new(&data_desc->ep_in_desc, _ep_in_handler); + + /* add the bulk out and bulk in endpoints to the data alternate setting */ + rt_usbd_altsetting_add_endpoint(data_setting, eps->ep_in); + rt_usbd_altsetting_add_endpoint(data_setting, eps->ep_out); + + /* add the data alternate setting to the data interface + then set default setting of the interface */ + rt_usbd_interface_add_altsetting(intf_data, data_setting); + rt_usbd_set_altsetting(intf_data, 0); + + /* add the cdc data interface to cdc class */ + rt_usbd_function_add_interface(cdc, intf_data); + + rt_usbd_os_comp_id_desc_add_os_func_comp_id_desc(device->os_comp_id_desc, &rndis_func_comp_id_desc); + + +#ifdef RT_USING_LWIP + + rt_list_init(&_rndis->response_list); + _rndis->need_notify = RT_TRUE; + + rt_sem_init(&_rndis->tx_buffer_free, "ue_tx", 1, RT_IPC_FLAG_FIFO); + +#ifdef RNDIS_DELAY_LINK_UP + rt_timer_init(&_rndis->timer, + "RNDIS", + timer_timeout, + _rndis, + RT_TICK_PER_SECOND * 2, + RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_SOFT_TIMER); +#endif /* RNDIS_DELAY_LINK_UP */ + + /* OUI 00-00-00, only for test. */ + _rndis->dev_addr[0] = 0x34; + _rndis->dev_addr[1] = 0x97; + _rndis->dev_addr[2] = 0xF6; + /* generate random MAC. */ + _rndis->dev_addr[3] = 0x94;//*(const rt_uint8_t *)(0x1fff7a10); + _rndis->dev_addr[4] = 0xEA;//*(const rt_uint8_t *)(0x1fff7a14); + _rndis->dev_addr[5] = 0x12;//(const rt_uint8_t *)(0x1fff7a18); + /* OUI 00-00-00, only for test. */ + _rndis->host_addr[0] = 0x34; + _rndis->host_addr[1] = 0x97; + _rndis->host_addr[2] = 0xF6; + /* generate random MAC. */ + _rndis->host_addr[3] = 0x94;//*(const rt_uint8_t *)(0x0FE081F0); + _rndis->host_addr[4] = 0xEA;//*(const rt_uint8_t *)(0x0FE081F1); + _rndis->host_addr[5] = 0x13;//*(const rt_uint8_t *)(0x0FE081F2); + +#ifdef RT_USING_DEVICE_OPS + _rndis->parent.parent.ops = &rndis_device_ops; +#else + _rndis->parent.parent.init = rt_rndis_eth_init; + _rndis->parent.parent.open = rt_rndis_eth_open; + _rndis->parent.parent.close = rt_rndis_eth_close; + _rndis->parent.parent.read = rt_rndis_eth_read; + _rndis->parent.parent.write = rt_rndis_eth_write; + _rndis->parent.parent.control = rt_rndis_eth_control; +#endif + _rndis->parent.parent.user_data = device; + + _rndis->parent.eth_rx = rt_rndis_eth_rx; + _rndis->parent.eth_tx = rt_rndis_eth_tx; + + /* register eth device */ + eth_device_init(&((rt_rndis_eth_t)cdc->user_data)->parent, "u0"); + +#endif /* RT_USING_LWIP */ + + return cdc; +} + +struct udclass rndis_class = +{ + .rt_usbd_function_create = rt_usbd_function_rndis_create +}; + +int rt_usbd_rndis_class_register(void) +{ + rt_usbd_class_register(&rndis_class); + return 0; +} +INIT_PREV_EXPORT(rt_usbd_rndis_class_register); + +#endif /* RT_USB_DEVICE_RNDIS */ diff --git a/components/legacy/usb/usbdevice/class/rndis.h b/components/legacy/usb/usbdevice/class/rndis.h new file mode 100644 index 0000000000..c862b82c01 --- /dev/null +++ b/components/legacy/usb/usbdevice/class/rndis.h @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2012-12-24 heyuanjie87 first version + */ + +#ifndef __RNDIS_H__ +#define __RNDIS_H__ + +#include <rtthread.h> + +#define USB_ETH_MTU 1500+14 +#define RNDIS_MESSAGE_BUFFER_SIZE 128 + +#define RESPONSE_AVAILABLE 0x00000001 + +/* Remote NDIS version numbers */ +#define RNDIS_MAJOR_VERSION 1 +#define RNDIS_MINOR_VERSION 0 + +/* common status values */ +#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 + +/* Remote NDIS message types */ +#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 + +/* device flags */ +#define RNDIS_DF_CONNECTIONLESS 0x00000001 +#define RNDIS_DF_CONNECTION_ORIENTED 0x00000002 +/* mediums */ +#define RNDIS_MEDIUM_802_3 0x00000000 + +struct ucls_rndis +{ + uep_t notify; + rt_uint32_t filter; + rt_bool_t header; + rt_uint8_t rndis_state; + rt_uint8_t media_state; + rt_uint8_t ethaddr[6]; +}; + +/* Remote NDIS generic message type */ +struct rndis_gen_msg +{ + rt_uint32_t MessageType; + rt_uint32_t MessageLength; +}; +typedef struct rndis_gen_msg* rndis_gen_msg_t; + +struct rndis_packet_msg +{ + rt_uint32_t MessageType; + rt_uint32_t MessageLength; + rt_uint32_t DataOffset; + rt_uint32_t DataLength; + rt_uint32_t OOBDataOffset; + rt_uint32_t OOBDataLength; + rt_uint32_t NumOOBDataElements; + rt_uint32_t PerPacketInfoOffset; + rt_uint32_t PerPacketInfoLength; + rt_uint32_t VcHandle; + rt_uint32_t Reserved; +}; +typedef struct rndis_packet_msg* rndis_packet_msg_t; + +/* Remote NDIS Initialize Message */ +struct rndis_init_msg +{ + rt_uint32_t MessageType; + rt_uint32_t MessageLength; + rt_uint32_t RequestId; + rt_uint32_t MajorVersion; + rt_uint32_t MinorVersion; + rt_uint32_t MaxTransferSize; +}; +typedef struct rndis_init_msg* rndis_init_msg_t; + +/* Response */ +struct rndis_init_cmplt +{ + rt_uint32_t MessageType; + rt_uint32_t MessageLength; + rt_uint32_t RequestId; + rt_uint32_t Status; + rt_uint32_t MajorVersion; + rt_uint32_t MinorVersion; + rt_uint32_t DeviceFlags; + rt_uint32_t Medium; + rt_uint32_t MaxPacketsPerTransfer; + rt_uint32_t MaxTransferSize; + rt_uint32_t PacketAlignmentFactor; + rt_uint32_t AfListOffset; + rt_uint32_t AfListSize; +}; +typedef struct rndis_init_cmplt* rndis_init_cmplt_t; + +/* Remote NDIS Halt Message */ +struct rndis_halt_msg +{ + rt_uint32_t MessageType; + rt_uint32_t MessageLength; + rt_uint32_t RequestId; +}; + +/* Remote NDIS Query Message */ +struct rndis_query_msg +{ + rt_uint32_t MessageType; + rt_uint32_t MessageLength; + rt_uint32_t RequestId; + rt_uint32_t Oid; + rt_uint32_t InformationBufferLength; + rt_uint32_t InformationBufferOffset; + rt_uint32_t DeviceVcHandle; +}; +typedef struct rndis_query_msg* rndis_query_msg_t; + +/* Response */ +struct rndis_query_cmplt +{ + rt_uint32_t MessageType; + rt_uint32_t MessageLength; + rt_uint32_t RequestId; + rt_uint32_t Status; + rt_uint32_t InformationBufferLength; + rt_uint32_t InformationBufferOffset; +}; +typedef struct rndis_query_cmplt* rndis_query_cmplt_t; + +/* Remote NDIS Set Message */ +struct rndis_set_msg +{ + rt_uint32_t MessageType; + rt_uint32_t MessageLength; + rt_uint32_t RequestId; + rt_uint32_t Oid; + rt_uint32_t InformationBufferLength; + rt_uint32_t InformationBufferOffset; + rt_uint32_t DeviceVcHandle; +}; +typedef struct rndis_set_msg* rndis_set_msg_t; + +/* Response */ +struct rndis_set_cmplt +{ + rt_uint32_t MessageType; + rt_uint32_t MessageLength; + rt_uint32_t RequestId; + rt_uint32_t Status; +}; +typedef struct rndis_set_cmplt* rndis_set_cmplt_t; + +/* Remote NDIS Soft Reset Message */ +struct rndis_reset_msg +{ + rt_uint32_t MessageType; + rt_uint32_t MessageLength; + rt_uint32_t Reserved; +}; + +/* Remote NDIS Soft Reset Response */ +struct rndis_reset_cmplt +{ + rt_uint32_t MessageType; + rt_uint32_t MessageLength; + rt_uint32_t Status; + rt_uint32_t AddressingReset; +}; + +/* Remote NDIS Indicate Status Message */ +struct rndis_indicate_status_msg +{ + rt_uint32_t MessageType; + rt_uint32_t MessageLength; + rt_uint32_t Status; + rt_uint32_t StatusBufferLength; + rt_uint32_t StatusBufferOffset; +}; +typedef struct rndis_indicate_status_msg* rndis_indicate_status_msg_t; + +struct rndis_keepalive_msg +{ + rt_uint32_t MessageType; + rt_uint32_t MessageLength; + rt_uint32_t RequestID; +}; +typedef struct rndis_keepalive_msg* rndis_keepalive_msg_t; + +/* Response: */ +struct rndis_keepalive_cmplt +{ + rt_uint32_t MessageType; + rt_uint32_t MessageLength; + rt_uint32_t RequestId; + rt_uint32_t Status; +}; +typedef struct rndis_keepalive_cmplt* rndis_keepalive_cmplt_t; + + + + + + + +#endif diff --git a/components/legacy/usb/usbdevice/class/uaudioreg.h b/components/legacy/usb/usbdevice/class/uaudioreg.h new file mode 100644 index 0000000000..eea3c2f053 --- /dev/null +++ b/components/legacy/usb/usbdevice/class/uaudioreg.h @@ -0,0 +1,419 @@ +/* $NetBSD: uaudioreg.h,v 1.15.38.1 2012/06/02 11:09:29 mrg Exp $ */ + +/* + * Copyright (c) 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson ([email protected]) at + * Carlstedt Research & Technology. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#include <rtdef.h> +#include <stdint.h> + +typedef uint8_t uByte; +typedef uint16_t uWord; + +#define UPACKED __attribute__ ((packed)) + +#define UAUDIO_VERSION 0x100 + +#define USB_SUBCLASS_AUDIOCONTROL 1 +#define USB_SUBCLASS_AUDIOSTREAMING 2 +#define USB_SUBCLASS_AUDIOMIDISTREAM 3 + +#define UDESC_CS_CONFIG 0x22 +#define UDESC_CS_STRING 0x23 +#define UDESC_CS_INTERFACE 0x24 +#define UDESC_CS_ENDPOINT 0x25 + +#define UDESCSUB_AC_HEADER 1 +#define UDESCSUB_AC_INPUT 2 +#define UDESCSUB_AC_OUTPUT 3 +#define UDESCSUB_AC_MIXER 4 +#define UDESCSUB_AC_SELECTOR 5 +#define UDESCSUB_AC_FEATURE 6 +#define UDESCSUB_AC_PROCESSING 7 +#define UDESCSUB_AC_EXTENSION 8 + +#ifndef AUFMT_MAX_FREQUENCIES +#define AUFMT_MAX_FREQUENCIES 1 +#endif + +/* The first fields are identical to usb_endpoint_descriptor_t */ +typedef struct { + uByte bLength; + uByte bDescriptorType; + uByte bEndpointAddress; + uByte bmAttributes; + uWord wMaxPacketSize; + uByte bInterval; + /* + * The following two entries are only used by the Audio Class. + * And according to the specs the Audio Class is the only one + * allowed to extend the endpoint descriptor. + * Who knows what goes on in the minds of the people in the USB + * standardization? :-( + */ + uByte bRefresh; + uByte bSynchAddress; +} UPACKED usb_endpoint_descriptor_audio_t; + +/* generic, for iteration */ +typedef struct { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; +} UPACKED uaudio_cs_descriptor_t; + +struct usb_audio_control_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uWord bcdADC; + uWord wTotalLength; + uByte bInCollection; + uByte baInterfaceNr[1]; +} UPACKED; + +struct usb_audio_streaming_interface_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bTerminalLink; + uByte bDelay; + uWord wFormatTag; +} UPACKED; + +struct usb_audio_streaming_endpoint_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bmAttributes; +#define UA_SED_FREQ_CONTROL 0x01 +#define UA_SED_PITCH_CONTROL 0x02 +#define UA_SED_MAXPACKETSONLY 0x80 + uByte bLockDelayUnits; + uWord wLockDelay; +} UPACKED; + +struct usb_audio_streaming_type1_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bFormatType; + uByte bNrChannels; + uByte bSubFrameSize; + uByte bBitResolution; + uByte bSamFreqType; +#define UA_SAMP_CONTNUOUS 0 + uByte tSamFreq[3*AUFMT_MAX_FREQUENCIES]; +#define UA_GETSAMP(p, n) ((p)->tSamFreq[(n)*3+0] | ((p)->tSamFreq[(n)*3+1] << 8) | ((p)->tSamFreq[(n)*3+2] << 16)) +#define UA_SAMP_LO(p) UA_GETSAMP(p, 0) +#define UA_SAMP_HI(p) UA_GETSAMP(p, 1) +} UPACKED; + +struct usb_audio_cluster { + uByte bNrChannels; + uWord wChannelConfig; +#define UA_CHANNEL_LEFT 0x0001 +#define UA_CHANNEL_RIGHT 0x0002 +#define UA_CHANNEL_CENTER 0x0004 +#define UA_CHANNEL_LFE 0x0008 +#define UA_CHANNEL_L_SURROUND 0x0010 +#define UA_CHANNEL_R_SURROUND 0x0020 +#define UA_CHANNEL_L_CENTER 0x0040 +#define UA_CHANNEL_R_CENTER 0x0080 +#define UA_CHANNEL_SURROUND 0x0100 +#define UA_CHANNEL_L_SIDE 0x0200 +#define UA_CHANNEL_R_SIDE 0x0400 +#define UA_CHANNEL_TOP 0x0800 + uByte iChannelNames; +} UPACKED; + +/* Shared by all units and terminals */ +struct usb_audio_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; +}; + +/* UDESCSUB_AC_INPUT */ +struct usb_audio_input_terminal { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bTerminalId; + uWord wTerminalType; + uByte bAssocTerminal; + uByte bNrChannels; + uWord wChannelConfig; + uByte iChannelNames; + uByte iTerminal; +} UPACKED; + +/* UDESCSUB_AC_OUTPUT */ +struct usb_audio_output_terminal { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bTerminalId; + uWord wTerminalType; + uByte bAssocTerminal; + uByte bSourceId; + uByte iTerminal; +} UPACKED; + +/* UDESCSUB_AC_MIXER */ +struct usb_audio_mixer_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uByte bNrInPins; + uByte baSourceId[255]; /* [bNrInPins] */ + /* struct usb_audio_mixer_unit_1 */ +} UPACKED; +struct usb_audio_mixer_unit_1 { + uByte bNrChannels; + uWord wChannelConfig; + uByte iChannelNames; + uByte bmControls[255]; /* [bNrChannels] */ + /*uByte iMixer;*/ +} UPACKED; + +/* UDESCSUB_AC_SELECTOR */ +struct usb_audio_selector_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uByte bNrInPins; + uByte baSourceId[255]; /* [bNrInPins] */ + /* uByte iSelector; */ +} UPACKED; + +/* UDESCSUB_AC_FEATURE */ +struct usb_audio_feature_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uByte bSourceId; + uByte bControlSize; + uByte bmaControls[2]; /* size for more than enough */ + /* uByte iFeature; */ +} UPACKED; + +/* UDESCSUB_AC_PROCESSING */ +struct usb_audio_processing_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uWord wProcessType; + uByte bNrInPins; + uByte baSourceId[255]; /* [bNrInPins] */ + /* struct usb_audio_processing_unit_1 */ +} UPACKED; +struct usb_audio_processing_unit_1{ + uByte bNrChannels; + uWord wChannelConfig; + uByte iChannelNames; + uByte bControlSize; + uByte bmControls[255]; /* [bControlSize] */ +#define UA_PROC_ENABLE_MASK 1 +} UPACKED; + +struct usb_audio_processing_unit_updown { + uByte iProcessing; + uByte bNrModes; + uWord waModes[255]; /* [bNrModes] */ +} UPACKED; + +/* UDESCSUB_AC_EXTENSION */ +struct usb_audio_extension_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uWord wExtensionCode; + uByte bNrInPins; + uByte baSourceId[255]; /* [bNrInPins] */ + /* struct usb_audio_extension_unit_1 */ +} UPACKED; +struct usb_audio_extension_unit_1 { + uByte bNrChannels; + uWord wChannelConfig; + uByte iChannelNames; + uByte bControlSize; + uByte bmControls[255]; /* [bControlSize] */ +#define UA_EXT_ENABLE_MASK 1 +#define UA_EXT_ENABLE 1 + /*uByte iExtension;*/ +} UPACKED; + +/* USB terminal types */ +#define UAT_UNDEFINED 0x0100 +#define UAT_STREAM 0x0101 +#define UAT_VENDOR 0x01ff +/* input terminal types */ +#define UATI_UNDEFINED 0x0200 +#define UATI_MICROPHONE 0x0201 +#define UATI_DESKMICROPHONE 0x0202 +#define UATI_PERSONALMICROPHONE 0x0203 +#define UATI_OMNIMICROPHONE 0x0204 +#define UATI_MICROPHONEARRAY 0x0205 +#define UATI_PROCMICROPHONEARR 0x0206 +/* output terminal types */ +#define UATO_UNDEFINED 0x0300 +#define UATO_SPEAKER 0x0301 +#define UATO_HEADPHONES 0x0302 +#define UATO_DISPLAYAUDIO 0x0303 +#define UATO_DESKTOPSPEAKER 0x0304 +#define UATO_ROOMSPEAKER 0x0305 +#define UATO_COMMSPEAKER 0x0306 +#define UATO_SUBWOOFER 0x0307 +/* bidir terminal types */ +#define UATB_UNDEFINED 0x0400 +#define UATB_HANDSET 0x0401 +#define UATB_HEADSET 0x0402 +#define UATB_SPEAKERPHONE 0x0403 +#define UATB_SPEAKERPHONEESUP 0x0404 +#define UATB_SPEAKERPHONEECANC 0x0405 +/* telephony terminal types */ +#define UATT_UNDEFINED 0x0500 +#define UATT_PHONELINE 0x0501 +#define UATT_TELEPHONE 0x0502 +#define UATT_DOWNLINEPHONE 0x0503 +/* external terminal types */ +#define UATE_UNDEFINED 0x0600 +#define UATE_ANALOGCONN 0x0601 +#define UATE_DIGITALAUIFC 0x0602 +#define UATE_LINECONN 0x0603 +#define UATE_LEGACYCONN 0x0604 +#define UATE_SPDIF 0x0605 +#define UATE_1394DA 0x0606 +#define UATE_1394DV 0x0607 +/* embedded function terminal types */ +#define UATF_UNDEFINED 0x0700 +#define UATF_CALIBNOISE 0x0701 +#define UATF_EQUNOISE 0x0702 +#define UATF_CDPLAYER 0x0703 +#define UATF_DAT 0x0704 +#define UATF_DCC 0x0705 +#define UATF_MINIDISK 0x0706 +#define UATF_ANALOGTAPE 0x0707 +#define UATF_PHONOGRAPH 0x0708 +#define UATF_VCRAUDIO 0x0709 +#define UATF_VIDEODISCAUDIO 0x070a +#define UATF_DVDAUDIO 0x070b +#define UATF_TVTUNERAUDIO 0x070c +#define UATF_SATELLITE 0x070d +#define UATF_CABLETUNER 0x070e +#define UATF_DSS 0x070f +#define UATF_RADIORECV 0x0710 +#define UATF_RADIOXMIT 0x0711 +#define UATF_MULTITRACK 0x0712 +#define UATF_SYNTHESIZER 0x0713 + + +#define SET_CUR 0x01 +#define GET_CUR 0x81 +#define SET_MIN 0x02 +#define GET_MIN 0x82 +#define SET_MAX 0x03 +#define GET_MAX 0x83 +#define SET_RES 0x04 +#define GET_RES 0x84 +#define SET_MEM 0x05 +#define GET_MEM 0x85 +#define GET_STAT 0xff + +#define MUTE_CONTROL 0x01 +#define VOLUME_CONTROL 0x02 +#define BASS_CONTROL 0x03 +#define MID_CONTROL 0x04 +#define TREBLE_CONTROL 0x05 +#define GRAPHIC_EQUALIZER_CONTROL 0x06 +#define AGC_CONTROL 0x07 +#define DELAY_CONTROL 0x08 +#define BASS_BOOST_CONTROL 0x09 +#define LOUDNESS_CONTROL 0x0a + +#define FU_MASK(u) (1 << ((u)-1)) + +#define MASTER_CHAN 0 + +#define AS_GENERAL 1 +#define FORMAT_TYPE 2 +#define FORMAT_SPECIFIC 3 + +#define UA_FMT_PCM 1 +#define UA_FMT_PCM8 2 +#define UA_FMT_IEEE_FLOAT 3 +#define UA_FMT_ALAW 4 +#define UA_FMT_MULAW 5 +#define UA_FMT_MPEG 0x1001 +#define UA_FMT_AC3 0x1002 + +#define SAMPLING_FREQ_CONTROL 0x01 +#define PITCH_CONTROL 0x02 + +#define FORMAT_TYPE_UNDEFINED 0 +#define FORMAT_TYPE_I 1 +#define FORMAT_TYPE_II 2 +#define FORMAT_TYPE_III 3 + +#define UA_PROC_MASK(n) (1<< ((n)-1)) +#define PROCESS_UNDEFINED 0 +#define XX_ENABLE_CONTROL 1 +#define UPDOWNMIX_PROCESS 1 +#define UD_ENABLE_CONTROL 1 +#define UD_MODE_SELECT_CONTROL 2 +#define DOLBY_PROLOGIC_PROCESS 2 +#define DP_ENABLE_CONTROL 1 +#define DP_MODE_SELECT_CONTROL 2 +#define P3D_STEREO_EXTENDER_PROCESS 3 +#define P3D_ENABLE_CONTROL 1 +#define P3D_SPACIOUSNESS_CONTROL 2 +#define REVERBATION_PROCESS 4 +#define RV_ENABLE_CONTROL 1 +#define RV_LEVEL_CONTROL 2 +#define RV_TIME_CONTROL 3 +#define RV_FEEDBACK_CONTROL 4 +#define CHORUS_PROCESS 5 +#define CH_ENABLE_CONTROL 1 +#define CH_LEVEL_CONTROL 2 +#define CH_RATE_CONTROL 3 +#define CH_DEPTH_CONTROL 4 +#define DYN_RANGE_COMP_PROCESS 6 +#define DR_ENABLE_CONTROL 1 +#define DR_COMPRESSION_RATE_CONTROL 2 +#define DR_MAXAMPL_CONTROL 3 +#define DR_THRESHOLD_CONTROL 4 +#define DR_ATTACK_TIME_CONTROL 5 +#define DR_RELEASE_TIME_CONTROL 6 diff --git a/components/legacy/usb/usbdevice/class/winusb.c b/components/legacy/usb/usbdevice/class/winusb.c new file mode 100644 index 0000000000..f39dab9f06 --- /dev/null +++ b/components/legacy/usb/usbdevice/class/winusb.c @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2017-11-16 ZYH first version + */ +#include <rthw.h> +#include <rtdevice.h> +#include <drivers/usb_device.h> +#include "winusb.h" +struct winusb_device +{ + struct rt_device parent; + void (*cmd_handler)(rt_uint8_t *buffer,rt_size_t size); + rt_uint8_t cmd_buff[256]; + uep_t ep_out; + uep_t ep_in; +}; +#define WINUSB_INTF_STR_INDEX 13 +typedef struct winusb_device * winusb_device_t; + +rt_align(4) +static struct udevice_descriptor dev_desc = +{ + USB_DESC_LENGTH_DEVICE, //bLength; + USB_DESC_TYPE_DEVICE, //type; + USB_BCD_VERSION, //bcdUSB; + 0x00, //bDeviceClass; + 0x00, //bDeviceSubClass; + 0x00, //bDeviceProtocol; + 0x40, //bMaxPacketSize0; + _VENDOR_ID, //idVendor; + _PRODUCT_ID, //idProduct; + USB_BCD_DEVICE, //bcdDevice; + USB_STRING_MANU_INDEX, //iManufacturer; + USB_STRING_PRODUCT_INDEX, //iProduct; + USB_STRING_SERIAL_INDEX, //iSerialNumber; + USB_DYNAMIC, //bNumConfigurations; +}; + +//FS and HS needed +rt_align(4) +static struct usb_qualifier_descriptor dev_qualifier = +{ + sizeof(dev_qualifier), //bLength + USB_DESC_TYPE_DEVICEQUALIFIER, //bDescriptorType + 0x0200, //bcdUSB + 0xFF, //bDeviceClass + 0x00, //bDeviceSubClass + 0x00, //bDeviceProtocol + 64, //bMaxPacketSize0 + 0x01, //bNumConfigurations + 0, +}; + +rt_align(4) +struct winusb_descriptor _winusb_desc = +{ +#ifdef RT_USB_DEVICE_COMPOSITE + /* Interface Association Descriptor */ + { + USB_DESC_LENGTH_IAD, + USB_DESC_TYPE_IAD, + USB_DYNAMIC, + 0x01, + 0xFF, + 0x00, + 0x00, + 0x00, + }, +#endif + /*interface descriptor*/ + { + USB_DESC_LENGTH_INTERFACE, //bLength; + USB_DESC_TYPE_INTERFACE, //type; + USB_DYNAMIC, //bInterfaceNumber; + 0x00, //bAlternateSetting; + 0x02, //bNumEndpoints + 0xFF, //bInterfaceClass; + 0x00, //bInterfaceSubClass; + 0x00, //bInterfaceProtocol; +#ifdef RT_USB_DEVICE_COMPOSITE + WINUSB_INTF_STR_INDEX, +#else + 0x00, //iInterface; +#endif + }, + /*endpoint descriptor*/ + { + USB_DESC_LENGTH_ENDPOINT, + USB_DESC_TYPE_ENDPOINT, + USB_DYNAMIC | USB_DIR_OUT, + USB_EP_ATTR_BULK, + USB_DYNAMIC, + 0x00, + }, + /*endpoint descriptor*/ + { + USB_DESC_LENGTH_ENDPOINT, + USB_DESC_TYPE_ENDPOINT, + USB_DYNAMIC | USB_DIR_IN, + USB_EP_ATTR_BULK, + USB_DYNAMIC, + 0x00, + }, +}; + +rt_align(4) +const static char* _ustring[] = +{ + "Language", + "RT-Thread Team.", + "RTT Win USB", + "32021919830108", + "Configuration", + "Interface", + USB_STRING_OS//must be +}; + +rt_align(4) +struct usb_os_proerty winusb_proerty[] = +{ + USB_OS_PROPERTY_DESC(USB_OS_PROPERTY_TYPE_REG_SZ,"DeviceInterfaceGUID",RT_WINUSB_GUID), +}; + +rt_align(4) +struct usb_os_function_comp_id_descriptor winusb_func_comp_id_desc = +{ + .bFirstInterfaceNumber = USB_DYNAMIC, + .reserved1 = 0x01, + .compatibleID = {'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00}, + .subCompatibleID = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + .reserved2 = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00} +}; + +static rt_err_t _ep_out_handler(ufunction_t func, rt_size_t size) +{ + winusb_device_t winusb_device = (winusb_device_t)func->user_data; + if(winusb_device->parent.rx_indicate != RT_NULL) + { + winusb_device->parent.rx_indicate(&winusb_device->parent, size); + } + return RT_EOK; +} + +static rt_err_t _ep_in_handler(ufunction_t func, rt_size_t size) +{ + winusb_device_t winusb_device = (winusb_device_t)func->user_data; + if(winusb_device->parent.tx_complete != RT_NULL) + { + winusb_device->parent.tx_complete(&winusb_device->parent, winusb_device->ep_in->buffer); + } + return RT_EOK; +} +static ufunction_t cmd_func = RT_NULL; +static rt_err_t _ep0_cmd_handler(udevice_t device, rt_size_t size) +{ + winusb_device_t winusb_device; + + if(cmd_func != RT_NULL) + { + winusb_device = (winusb_device_t)cmd_func->user_data; + cmd_func = RT_NULL; + if(winusb_device->cmd_handler != RT_NULL) + { + winusb_device->cmd_handler(winusb_device->cmd_buff,size); + } + } + dcd_ep0_send_status(device->dcd); + return RT_EOK; +} +static rt_err_t _ep0_cmd_read(ufunction_t func, ureq_t setup) +{ + winusb_device_t winusb_device = (winusb_device_t)func->user_data; + cmd_func = func; + rt_usbd_ep0_read(func->device,winusb_device->cmd_buff,setup->wLength,_ep0_cmd_handler); + return RT_EOK; +} +static rt_err_t _interface_handler(ufunction_t func, ureq_t setup) +{ + switch(setup->bRequest) + { + case 'A': + switch(setup->wIndex) + { + case 0x05: + usbd_os_proerty_descriptor_send(func,setup,winusb_proerty,sizeof(winusb_proerty)/sizeof(winusb_proerty[0])); + break; + } + break; + case 0x0A://customer + _ep0_cmd_read(func, setup); + break; + } + + return RT_EOK; +} +static rt_err_t _function_enable(ufunction_t func) +{ + RT_ASSERT(func != RT_NULL); + return RT_EOK; +} +static rt_err_t _function_disable(ufunction_t func) +{ + RT_ASSERT(func != RT_NULL); + return RT_EOK; +} + +static struct ufunction_ops ops = +{ + _function_enable, + _function_disable, + RT_NULL, +}; + +static rt_err_t _winusb_descriptor_config(winusb_desc_t winusb, rt_uint8_t cintf_nr, rt_uint8_t device_is_hs) +{ +#ifdef RT_USB_DEVICE_COMPOSITE + winusb->iad_desc.bFirstInterface = cintf_nr; +#endif + winusb->ep_out_desc.wMaxPacketSize = device_is_hs ? 512 : 64; + winusb->ep_in_desc.wMaxPacketSize = device_is_hs ? 512 : 64; + winusb_func_comp_id_desc.bFirstInterfaceNumber = cintf_nr; + return RT_EOK; +} + +static rt_ssize_t win_usb_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) +{ + if(((ufunction_t)dev->user_data)->device->state != USB_STATE_CONFIGURED) + { + return 0; + } + winusb_device_t winusb_device = (winusb_device_t)dev; + winusb_device->ep_out->buffer = buffer; + winusb_device->ep_out->request.buffer = buffer; + winusb_device->ep_out->request.size = size; + winusb_device->ep_out->request.req_type = UIO_REQUEST_READ_FULL; + rt_usbd_io_request(((ufunction_t)dev->user_data)->device,winusb_device->ep_out,&winusb_device->ep_out->request); + return size; +} +static rt_ssize_t win_usb_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) +{ + if(((ufunction_t)dev->user_data)->device->state != USB_STATE_CONFIGURED) + { + return 0; + } + winusb_device_t winusb_device = (winusb_device_t)dev; + winusb_device->ep_in->buffer = (void *)buffer; + winusb_device->ep_in->request.buffer = winusb_device->ep_in->buffer; + winusb_device->ep_in->request.size = size; + winusb_device->ep_in->request.req_type = UIO_REQUEST_WRITE; + rt_usbd_io_request(((ufunction_t)dev->user_data)->device,winusb_device->ep_in,&winusb_device->ep_in->request); + return size; +} +static rt_err_t win_usb_control(rt_device_t dev, int cmd, void *args) +{ + winusb_device_t winusb_device = (winusb_device_t)dev; + if(RT_DEVICE_CTRL_CONFIG == cmd) + { + winusb_device->cmd_handler = (void(*)(rt_uint8_t*,rt_size_t))args; + } + return RT_EOK; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops winusb_device_ops = +{ + RT_NULL, + RT_NULL, + RT_NULL, + win_usb_read, + win_usb_write, + win_usb_control, +}; +#endif + +static rt_err_t rt_usb_winusb_init(ufunction_t func) +{ + winusb_device_t winusb_device = (winusb_device_t)func->user_data; + winusb_device->parent.type = RT_Device_Class_Miscellaneous; + +#ifdef RT_USING_DEVICE_OPS + winusb_device->parent.ops = &winusb_device_ops; +#else + winusb_device->parent.init = RT_NULL; + winusb_device->parent.open = RT_NULL; + winusb_device->parent.close = RT_NULL; + winusb_device->parent.read = win_usb_read; + winusb_device->parent.write = win_usb_write; + winusb_device->parent.control = win_usb_control; +#endif + + winusb_device->parent.user_data = func; + + + return rt_device_register(&winusb_device->parent, "winusb", RT_DEVICE_FLAG_RDWR); +} + +ufunction_t rt_usbd_function_winusb_create(udevice_t device) +{ + ufunction_t func; + winusb_device_t winusb_device; + + uintf_t winusb_intf; + ualtsetting_t winusb_setting; + winusb_desc_t winusb_desc; + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + + /* set usb device string description */ +#ifdef RT_USB_DEVICE_COMPOSITE + rt_usbd_device_set_interface_string(device, WINUSB_INTF_STR_INDEX, _ustring[2]); +#else + rt_usbd_device_set_string(device, _ustring); +#endif + + /* create a cdc function */ + func = rt_usbd_function_new(device, &dev_desc, &ops); + rt_usbd_device_set_qualifier(device, &dev_qualifier); + + /* allocate memory for cdc vcom data */ + winusb_device = (winusb_device_t)rt_malloc(sizeof(struct winusb_device)); + if (winusb_device == NULL) + return RT_NULL; + rt_memset((void *)winusb_device, 0, sizeof(struct winusb_device)); + func->user_data = (void*)winusb_device; + /* create an interface object */ + winusb_intf = rt_usbd_interface_new(device, _interface_handler); + + /* create an alternate setting object */ + winusb_setting = rt_usbd_altsetting_new(sizeof(struct winusb_descriptor)); + + /* config desc in alternate setting */ + rt_usbd_altsetting_config_descriptor(winusb_setting, &_winusb_desc, (rt_off_t)&((winusb_desc_t)0)->intf_desc); + + /* configure the hid interface descriptor */ + _winusb_descriptor_config(winusb_setting->desc, winusb_intf->intf_num, device->dcd->device_is_hs); + + /* create endpoint */ + winusb_desc = (winusb_desc_t)winusb_setting->desc; + winusb_device->ep_out = rt_usbd_endpoint_new(&winusb_desc->ep_out_desc, _ep_out_handler); + winusb_device->ep_in = rt_usbd_endpoint_new(&winusb_desc->ep_in_desc, _ep_in_handler); + + /* add the int out and int in endpoint to the alternate setting */ + rt_usbd_altsetting_add_endpoint(winusb_setting, winusb_device->ep_out); + rt_usbd_altsetting_add_endpoint(winusb_setting, winusb_device->ep_in); + + /* add the alternate setting to the interface, then set default setting */ + rt_usbd_interface_add_altsetting(winusb_intf, winusb_setting); + rt_usbd_set_altsetting(winusb_intf, 0); + + /* add the interface to the mass storage function */ + rt_usbd_function_add_interface(func, winusb_intf); + + rt_usbd_os_comp_id_desc_add_os_func_comp_id_desc(device->os_comp_id_desc, &winusb_func_comp_id_desc); + /* initilize winusb */ + rt_usb_winusb_init(func); + return func; +} + +struct udclass winusb_class = +{ + .rt_usbd_function_create = rt_usbd_function_winusb_create +}; + +int rt_usbd_winusb_class_register(void) +{ + rt_usbd_class_register(&winusb_class); + return 0; +} +INIT_PREV_EXPORT(rt_usbd_winusb_class_register); diff --git a/components/legacy/usb/usbdevice/class/winusb.h b/components/legacy/usb/usbdevice/class/winusb.h new file mode 100644 index 0000000000..06325a0b03 --- /dev/null +++ b/components/legacy/usb/usbdevice/class/winusb.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2017-11-16 ZYH first version + */ +#ifndef __WINUSB_H__ +#define __WINUSB_H__ +#include <rtthread.h> +struct winusb_descriptor +{ +#ifdef RT_USB_DEVICE_COMPOSITE + struct uiad_descriptor iad_desc; +#endif + struct uinterface_descriptor intf_desc; + struct uendpoint_descriptor ep_out_desc; + struct uendpoint_descriptor ep_in_desc; +}; +typedef struct winusb_descriptor* winusb_desc_t; + +#endif diff --git a/components/legacy/usb/usbdevice/core/usbdevice.c b/components/legacy/usb/usbdevice/core/usbdevice.c new file mode 100644 index 0000000000..eaa80217c8 --- /dev/null +++ b/components/legacy/usb/usbdevice/core/usbdevice.c @@ -0,0 +1,164 @@ +/* + * File : hid.c + * COPYRIGHT (C) 2006 - 2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2012-10-02 Yi Qiu first version + */ + +#include <rtthread.h> +#include <rtdevice.h> +#include <rtservice.h> + +#ifdef RT_USING_USB_DEVICE + +#define USB_DEVICE_CONTROLLER_NAME "usbd" + +#ifdef RT_USB_DEVICE_COMPOSITE +const static char* ustring[] = +{ + "Language", + "RT-Thread Team.", + "RTT Composite Device", + "320219198301", + "Configuration", + "Interface", + USB_STRING_OS +}; + +static struct udevice_descriptor compsit_desc = +{ + USB_DESC_LENGTH_DEVICE, //bLength; + USB_DESC_TYPE_DEVICE, //type; + USB_BCD_VERSION, //bcdUSB; + USB_CLASS_MISC, //bDeviceClass; + 0x02, //bDeviceSubClass; + 0x01, //bDeviceProtocol; + 0x40, //bMaxPacketSize0; + _VENDOR_ID, //idVendor; + _PRODUCT_ID, //idProduct; + USB_BCD_DEVICE, //bcdDevice; + USB_STRING_MANU_INDEX, //iManufacturer; + USB_STRING_PRODUCT_INDEX, //iProduct; + USB_STRING_SERIAL_INDEX, //iSerialNumber; + USB_DYNAMIC, //bNumConfigurations; +}; + +//FS and HS needed +static struct usb_qualifier_descriptor dev_qualifier = +{ + sizeof(dev_qualifier), //bLength + USB_DESC_TYPE_DEVICEQUALIFIER, //bDescriptorType + 0x0200, //bcdUSB + USB_CLASS_MISC, //bDeviceClass + 0x02, //bDeviceSubClass + 0x01, //bDeviceProtocol + 64, //bMaxPacketSize0 + 0x01, //bNumConfigurations + 0, +}; +#endif + +struct usb_os_comp_id_descriptor usb_comp_id_desc = +{ + //head section + { + USB_DYNAMIC, + 0x0100, + 0x04, + USB_DYNAMIC, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + }, +}; +static rt_list_t class_list; +int rt_usbd_class_list_init(void) +{ + rt_list_init(&class_list); + return 0; +} +INIT_BOARD_EXPORT(rt_usbd_class_list_init); + +rt_err_t rt_usbd_class_register(udclass_t udclass) +{ +#ifndef RT_USB_DEVICE_COMPOSITE + if(!rt_list_isempty(&class_list)) + { + rt_kprintf("[D/USBD] If you want to use usb composite device please define RT_USB_DEVICE_COMPOSITE\n"); + return -RT_ERROR; + } +#endif + rt_list_insert_before(&class_list,&udclass->list); + return RT_EOK; +} + +rt_err_t rt_usb_device_init(void) +{ + rt_device_t udc; + udevice_t udevice; + uconfig_t cfg; + ufunction_t func; + rt_list_t *i; + udclass_t udclass; + + if(rt_list_isempty(&class_list)) + { + rt_kprintf("[D/USBD] No class register on usb device\n"); + return -RT_ERROR; + } + /* create and startup usb device thread */ + rt_usbd_core_init(); + + /* create a device object */ + udevice = rt_usbd_device_new(); + + udc = rt_device_find(USB_DEVICE_CONTROLLER_NAME); + if(udc == RT_NULL) + { + rt_kprintf("can't find usb device controller %s\n", USB_DEVICE_CONTROLLER_NAME); + return -RT_ERROR; + } + + /* set usb controller driver to the device */ + rt_usbd_device_set_controller(udevice, (udcd_t)udc); + + /* create a configuration object */ + cfg = rt_usbd_config_new(); + + rt_usbd_device_set_os_comp_id_desc(udevice, &usb_comp_id_desc); + + for(i = class_list.next; i!= &class_list; i = i->next) + { + /* get a class creater */ + udclass = rt_list_entry(i, struct udclass, list); + /* create a function object */ + func = udclass->rt_usbd_function_create(udevice); + /* add the function to the configuration */ + rt_usbd_config_add_function(cfg, func); + } + /* set device descriptor to the device */ +#ifdef RT_USB_DEVICE_COMPOSITE + rt_usbd_device_set_descriptor(udevice, &compsit_desc); + rt_usbd_device_set_string(udevice, ustring); + if(udevice->dcd->device_is_hs) + { + rt_usbd_device_set_qualifier(udevice, &dev_qualifier); + } +#else + rt_usbd_device_set_descriptor(udevice, func->dev_desc); +#endif + + /* add the configuration to the device */ + rt_usbd_device_add_config(udevice, cfg); + + /* initialize usb device controller */ + rt_device_init(udc); + + /* set default configuration to 1 */ + rt_usbd_set_config(udevice, 1); + + return RT_EOK; +} +#endif diff --git a/components/legacy/usb/usbdevice/core/usbdevice_core.c b/components/legacy/usb/usbdevice/core/usbdevice_core.c new file mode 100644 index 0000000000..015473ffde --- /dev/null +++ b/components/legacy/usb/usbdevice/core/usbdevice_core.c @@ -0,0 +1,2269 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2012-10-01 Yi Qiu first version + * 2012-12-12 heyuanjie87 change endpoint and function handler + * 2012-12-30 heyuanjie87 change inferface handler + * 2013-04-26 aozima add DEVICEQUALIFIER support. + * 2013-07-25 Yi Qiu update for USB CV test + * 2017-11-15 ZYH fix ep0 transform error + */ + +#include <rtthread.h> +#include "drivers/usb_common.h" +#include "drivers/usb_device.h" + +#define DBG_TAG "usbdevice.core" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + +static rt_list_t device_list; + +static rt_ssize_t rt_usbd_ep_write(udevice_t device, uep_t ep, void *buffer, rt_size_t size); +static rt_ssize_t rt_usbd_ep_read_prepare(udevice_t device, uep_t ep, void *buffer, rt_size_t size); +static rt_err_t rt_usbd_ep_assign(udevice_t device, uep_t ep); +rt_err_t rt_usbd_ep_unassign(udevice_t device, uep_t ep); + +/** + * This function will handle get_device_descriptor bRequest. + * + * @param device the usb device object. + * @param setup the setup bRequest. + * + * @return RT_EOK on successful. + */ +static rt_err_t _get_device_descriptor(struct udevice* device, ureq_t setup) +{ + rt_size_t size; + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(setup != RT_NULL); + + LOG_D("_get_device_descriptor"); + + /* device descriptor wLength should less than USB_DESC_LENGTH_DEVICE*/ + size = (setup->wLength > USB_DESC_LENGTH_DEVICE) ? + USB_DESC_LENGTH_DEVICE : setup->wLength; + + /* send device descriptor to endpoint 0 */ + rt_usbd_ep0_write(device, (rt_uint8_t*) &device->dev_desc, size); + + return RT_EOK; +} + +/** + * This function will handle get_config_descriptor bRequest. + * + * @param device the usb device object. + * @param setup the setup bRequest. + * + * @return RT_EOK on successful. + */ +static rt_err_t _get_config_descriptor(struct udevice* device, ureq_t setup) +{ + rt_size_t size; + ucfg_desc_t cfg_desc; + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(setup != RT_NULL); + + LOG_D("_get_config_descriptor"); + + cfg_desc = &device->curr_cfg->cfg_desc; + size = (setup->wLength > cfg_desc->wTotalLength) ? + cfg_desc->wTotalLength : setup->wLength; + + /* send configuration descriptor to endpoint 0 */ + rt_usbd_ep0_write(device, (rt_uint8_t*)cfg_desc, size); + + return RT_EOK; +} + +/** + * This function will handle get_string_descriptor bRequest. + * + * @param device the usb device object. + * @param setup the setup bRequest. + * + * @return RT_EOK on successful, -RT_ERROR on invalid bRequest. + */ +static rt_err_t _get_string_descriptor(struct udevice* device, ureq_t setup) +{ + struct ustring_descriptor str_desc; + rt_uint8_t index, i; + rt_uint32_t len; + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(setup != RT_NULL); + + LOG_D("_get_string_descriptor"); + + str_desc.type = USB_DESC_TYPE_STRING; + index = setup->wValue & 0xFF; + + if(index == 0xEE) + { + index = USB_STRING_OS_INDEX; + } + + if(index > USB_STRING_MAX) + { + rt_kprintf("unknown string index\n"); + rt_usbd_ep0_set_stall(device); + return -RT_ERROR; + } + else if(index == USB_STRING_LANGID_INDEX) + { + str_desc.bLength = 4; + str_desc.String[0] = 0x09; + str_desc.String[1] = 0x04; + } + else + { + if(index < 5) + len = rt_strlen(device->str[index]); + else + len = rt_strlen(device->str_intf[index]); + str_desc.bLength = len*2 + 2; + + for(i=0; i<len; i++) + { + if(index < 5) + str_desc.String[i*2] = device->str[index][i]; + else + str_desc.String[i*2] = device->str_intf[index][i]; + str_desc.String[i*2 + 1] = 0; + } + } + + if (setup->wLength > str_desc.bLength) + len = str_desc.bLength; + else + len = setup->wLength; + + /* send string descriptor to endpoint 0 */ + rt_usbd_ep0_write(device, (rt_uint8_t*)&str_desc, len); + + return RT_EOK; +} + +static rt_err_t _get_qualifier_descriptor(struct udevice* device, ureq_t setup) +{ + LOG_D("_get_qualifier_descriptor"); + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(setup != RT_NULL); + + if(device->dev_qualifier && device->dcd->device_is_hs) + { + /* send device qualifier descriptor to endpoint 0 */ + rt_usbd_ep0_write(device, (rt_uint8_t*)device->dev_qualifier, + sizeof(struct usb_qualifier_descriptor)); + } + else + { + rt_usbd_ep0_set_stall(device); + } + + return RT_EOK; +} + +/** + * This function will handle get_descriptor bRequest. + * + * @param device the usb device object. + * @param setup the setup bRequest. + * + * @return RT_EOK on successful. + */ +static rt_err_t _get_descriptor(struct udevice* device, ureq_t setup) +{ + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(setup != RT_NULL); + + if(setup->request_type == USB_REQ_TYPE_DIR_IN) + { + switch(setup->wValue >> 8) + { + case USB_DESC_TYPE_DEVICE: + _get_device_descriptor(device, setup); + break; + case USB_DESC_TYPE_CONFIGURATION: + _get_config_descriptor(device, setup); + break; + case USB_DESC_TYPE_STRING: + _get_string_descriptor(device, setup); + break; + case USB_DESC_TYPE_DEVICEQUALIFIER: + /* If a full-speed only device (with a device descriptor version number equal to 0200H) receives a + GetDescriptor() request for a device_qualifier, it must respond with a request error. The host must not make + a request for an other_speed_configuration descriptor unless it first successfully retrieves the + device_qualifier descriptor. */ + if(device->dcd->device_is_hs) + { + _get_qualifier_descriptor(device, setup); + } + else + { + rt_usbd_ep0_set_stall(device); + } + break; + case USB_DESC_TYPE_OTHERSPEED: + _get_config_descriptor(device, setup); + break; + default: + rt_kprintf("unsupported descriptor request\n"); + rt_usbd_ep0_set_stall(device); + break; + } + } + else + { + rt_kprintf("request direction error\n"); + rt_usbd_ep0_set_stall(device); + } + + return RT_EOK; +} + +/** + * This function will handle get_interface bRequest. + * + * @param device the usb device object. + * @param setup the setup bRequest. + * + * @return RT_EOK on successful. + */ +static rt_err_t _get_interface(struct udevice* device, ureq_t setup) +{ + rt_uint8_t value; + uintf_t intf; + ufunction_t func; + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(setup != RT_NULL); + + LOG_D("_get_interface"); + + if (device->state != USB_STATE_CONFIGURED) + { + rt_usbd_ep0_set_stall(device); + return -RT_ERROR; + } + + /* find the specified interface and its alternate setting */ + intf = rt_usbd_find_interface(device, setup->wIndex & 0xFF, &func); + value = intf->curr_setting->intf_desc->bAlternateSetting; + + /* send the interface alternate setting to endpoint 0*/ + rt_usbd_ep0_write(device, &value, 1); + + if (intf->handler) + { + intf->handler(func, setup); + } + + return RT_EOK; +} + +/** + * This function will handle set_interface bRequest. + * + * @param device the usb device object. + * @param setup the setup bRequest. + * + * @return RT_EOK on successful. + */ +static rt_err_t _set_interface(struct udevice* device, ureq_t setup) +{ + ufunction_t func; + uintf_t intf; + uep_t ep; + struct rt_list_node* i; + ualtsetting_t setting; + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(setup != RT_NULL); + + LOG_D("_set_interface"); + + if (device->state != USB_STATE_CONFIGURED) + { + rt_usbd_ep0_set_stall(device); + return -RT_ERROR; + } + + /* find the specified interface */ + intf = rt_usbd_find_interface(device, setup->wIndex & 0xFF, &func); + + /* set alternate setting to the interface */ + rt_usbd_set_altsetting(intf, setup->wValue & 0xFF); + setting = intf->curr_setting; + + /* start all endpoints of the interface alternate setting */ + for(i=setting->ep_list.next; i != &setting->ep_list; i=i->next) + { + ep = (uep_t)rt_list_entry(i, struct uendpoint, list); + dcd_ep_disable(device->dcd, ep); + dcd_ep_enable(device->dcd, ep); + } + dcd_ep0_send_status(device->dcd); + + if (intf->handler) + { + intf->handler(func, setup); + } + + return RT_EOK; +} + +/** + * This function will handle get_config bRequest. + * + * @param device the usb device object. + * @param setup the setup bRequest. + * + * @return RT_EOK on successful. + */ +static rt_err_t _get_config(struct udevice* device, ureq_t setup) +{ + rt_uint8_t value; + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(setup != RT_NULL); + RT_ASSERT(device->curr_cfg != RT_NULL); + + LOG_D("_get_config"); + + if (device->state == USB_STATE_CONFIGURED) + { + /* get current configuration */ + value = device->curr_cfg->cfg_desc.bConfigurationValue; + } + else + { + value = 0; + } + /* write the current configuration to endpoint 0 */ + rt_usbd_ep0_write(device, &value, 1); + + return RT_EOK; +} + +/** + * This function will handle set_config bRequest. + * + * @param device the usb device object. + * @param setup the setup bRequest. + * + * @return RT_EOK on successful. + */ +static rt_err_t _set_config(struct udevice* device, ureq_t setup) +{ + struct rt_list_node *i, *j, *k; + uconfig_t cfg; + uintf_t intf; + ualtsetting_t setting; + uep_t ep; + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(setup != RT_NULL); + + LOG_D("_set_config"); + + if (setup->wValue > device->dev_desc.bNumConfigurations) + { + rt_usbd_ep0_set_stall(device); + return -RT_ERROR; + } + + if (setup->wValue == 0) + { + LOG_D("address state"); + device->state = USB_STATE_ADDRESS; + + goto _exit; + } + + /* set current configuration */ + rt_usbd_set_config(device, setup->wValue); + cfg = device->curr_cfg; + + for (i=cfg->func_list.next; i!=&cfg->func_list; i=i->next) + { + /* run all functiones and their endpoints in the configuration */ + ufunction_t func = (ufunction_t)rt_list_entry(i, struct ufunction, list); + for(j=func->intf_list.next; j!=&func->intf_list; j=j->next) + { + intf = (uintf_t)rt_list_entry(j, struct uinterface, list); + setting = intf->curr_setting; + for(k=setting->ep_list.next; k != &setting->ep_list; k=k->next) + { + ep = (uep_t)rt_list_entry(k, struct uendpoint, list); + + /* first disable then enable an endpoint */ + dcd_ep_disable(device->dcd, ep); + dcd_ep_enable(device->dcd, ep); + } + } + /* after enabled endpoints, then enable function */ + FUNC_ENABLE(func); + } + + device->state = USB_STATE_CONFIGURED; + +_exit: + /* issue status stage */ + dcd_ep0_send_status(device->dcd); + + return RT_EOK; +} + +/** + * This function will handle set_address bRequest. + * + * @param device the usb device object. + * @param setup the setup bRequest. + * + * @return RT_EOK on successful. + */ +static rt_err_t _set_address(struct udevice* device, ureq_t setup) +{ + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(setup != RT_NULL); + + /* set address in device control driver */ + dcd_set_address(device->dcd, setup->wValue); + + /* issue status stage */ + dcd_ep0_send_status(device->dcd); + + LOG_D("_set_address"); + + device->state = USB_STATE_ADDRESS; + + return RT_EOK; +} + +/** + * This function will handle standard bRequest to + * interface that defined in function-specifics + * + * @param device the usb device object. + * @param setup the setup bRequest. + * + * @return RT_EOK on successful. + */ +static rt_err_t _request_interface(struct udevice* device, ureq_t setup) +{ + uintf_t intf; + ufunction_t func; + rt_err_t ret; + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(setup != RT_NULL); + + LOG_D("_request_interface"); + + intf = rt_usbd_find_interface(device, setup->wIndex & 0xFF, &func); + if (intf != RT_NULL) + { + ret = intf->handler(func, setup); + } + else + { + ret = -RT_ERROR; + } + + return ret; +} + +/** + * This function will handle standard bRequest. + * + * @param device the usb device object. + * @param setup the setup bRequest. + * + * @return RT_EOK on successful. + */ +static rt_err_t _standard_request(struct udevice* device, ureq_t setup) +{ + udcd_t dcd; + rt_uint16_t value = 0; + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(setup != RT_NULL); + + dcd = device->dcd; + + switch(setup->request_type & USB_REQ_TYPE_RECIPIENT_MASK) + { + case USB_REQ_TYPE_DEVICE: + switch(setup->bRequest) + { + case USB_REQ_GET_STATUS: + rt_usbd_ep0_write(device, &value, 2); + break; + case USB_REQ_CLEAR_FEATURE: + rt_usbd_clear_feature(device, setup->wValue, setup->wIndex); + dcd_ep0_send_status(dcd); + break; + case USB_REQ_SET_FEATURE: + rt_usbd_set_feature(device, setup->wValue, setup->wIndex); + break; + case USB_REQ_SET_ADDRESS: + _set_address(device, setup); + break; + case USB_REQ_GET_DESCRIPTOR: + _get_descriptor(device, setup); + break; + case USB_REQ_SET_DESCRIPTOR: + rt_usbd_ep0_set_stall(device); + break; + case USB_REQ_GET_CONFIGURATION: + _get_config(device, setup); + break; + case USB_REQ_SET_CONFIGURATION: + _set_config(device, setup); + break; + default: + rt_kprintf("unknown device request\n"); + rt_usbd_ep0_set_stall(device); + break; + } + break; + case USB_REQ_TYPE_INTERFACE: + switch(setup->bRequest) + { + case USB_REQ_GET_INTERFACE: + _get_interface(device, setup); + break; + case USB_REQ_SET_INTERFACE: + _set_interface(device, setup); + break; + default: + if (_request_interface(device, setup) != RT_EOK) + { + rt_kprintf("unknown interface request\n"); + rt_usbd_ep0_set_stall(device); + return -RT_ERROR; + } + else + break; + } + break; + case USB_REQ_TYPE_ENDPOINT: + switch(setup->bRequest) + { + case USB_REQ_GET_STATUS: + { + uep_t ep; + + ep = rt_usbd_find_endpoint(device, RT_NULL, setup->wIndex); + value = ep->stalled; + rt_usbd_ep0_write(device, &value, 2); + } + break; + case USB_REQ_CLEAR_FEATURE: + { + uep_t ep; + uio_request_t req; + struct rt_list_node *node; + + ep = rt_usbd_find_endpoint(device, RT_NULL, setup->wIndex); + if(USB_EP_HALT == setup->wValue && ep->stalled == RT_TRUE) + { + rt_usbd_clear_feature(device, setup->wValue, setup->wIndex); + dcd_ep0_send_status(dcd); + ep->stalled = RT_FALSE; + + for (node = ep->request_list.next; node != &ep->request_list; node = node->next) + { + req = (uio_request_t)rt_list_entry(node, struct uio_request, list); + rt_usbd_io_request(device, ep, req); + LOG_D("fired a request"); + } + + rt_list_init(&ep->request_list); + } + } + break; + case USB_REQ_SET_FEATURE: + { + uep_t ep; + + if(USB_EP_HALT == setup->wValue) + { + ep = rt_usbd_find_endpoint(device, RT_NULL, setup->wIndex); + ep->stalled = RT_TRUE; + rt_usbd_set_feature(device, setup->wValue, setup->wIndex); + dcd_ep0_send_status(dcd); + } + } + break; + case USB_REQ_SYNCH_FRAME: + break; + default: + rt_kprintf("unknown endpoint request\n"); + rt_usbd_ep0_set_stall(device); + break; + } + break; + case USB_REQ_TYPE_OTHER: + rt_kprintf("unknown other type request\n"); + rt_usbd_ep0_set_stall(device); + break; + default: + rt_kprintf("unknown type request\n"); + rt_usbd_ep0_set_stall(device); + break; + } + + return RT_EOK; +} + +/** + * This function will handle function bRequest. + * + * @param device the usb device object. + * @param setup the setup bRequest. + * + * @return RT_EOK on successful, -RT_ERROR on invalid bRequest. + */ +static rt_err_t _function_request(udevice_t device, ureq_t setup) +{ + uintf_t intf; + ufunction_t func; + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(setup != RT_NULL); + + /* verify bRequest wValue */ + if(setup->wIndex > device->curr_cfg->cfg_desc.bNumInterfaces) + { + rt_usbd_ep0_set_stall(device); + return -RT_ERROR; + } + + switch(setup->request_type & USB_REQ_TYPE_RECIPIENT_MASK) + { + case USB_REQ_TYPE_INTERFACE: + intf = rt_usbd_find_interface(device, setup->wIndex & 0xFF, &func); + if(intf == RT_NULL) + { + rt_kprintf("unkwown interface request\n"); + rt_usbd_ep0_set_stall(device); + } + else + { + intf->handler(func, setup); + } + break; + case USB_REQ_TYPE_ENDPOINT: + break; + default: + rt_kprintf("unknown function request type\n"); + rt_usbd_ep0_set_stall(device); + break; + } + + return RT_EOK; +} +static rt_err_t _vendor_request(udevice_t device, ureq_t setup) +{ + static rt_uint8_t * usb_comp_id_desc = RT_NULL; + static rt_uint32_t usb_comp_id_desc_size = 0; + usb_os_func_comp_id_desc_t func_comp_id_desc; + uintf_t intf; + ufunction_t func; + switch(setup->bRequest) + { + case 'A': + switch(setup->wIndex) + { + case 0x04: + if(rt_list_len(&device->os_comp_id_desc->func_desc) == 0) + { + rt_usbd_ep0_set_stall(device); + return RT_EOK; + } + if(usb_comp_id_desc == RT_NULL) + { + rt_uint8_t * pusb_comp_id_desc; + rt_list_t *p; + usb_comp_id_desc_size = sizeof(struct usb_os_header_comp_id_descriptor) + + (sizeof(struct usb_os_function_comp_id_descriptor)-sizeof(rt_list_t))*rt_list_len(&device->os_comp_id_desc->func_desc); + + usb_comp_id_desc = (rt_uint8_t *)rt_malloc(usb_comp_id_desc_size); + RT_ASSERT(usb_comp_id_desc != RT_NULL); + device->os_comp_id_desc->head_desc.dwLength = usb_comp_id_desc_size; + pusb_comp_id_desc = usb_comp_id_desc; + rt_memcpy((void *)pusb_comp_id_desc,(void *)&device->os_comp_id_desc->head_desc,sizeof(struct usb_os_header_comp_id_descriptor)); + pusb_comp_id_desc += sizeof(struct usb_os_header_comp_id_descriptor); + + for (p = device->os_comp_id_desc->func_desc.next; p != &device->os_comp_id_desc->func_desc; p = p->next) + { + func_comp_id_desc = rt_list_entry(p,struct usb_os_function_comp_id_descriptor,list); + rt_memcpy(pusb_comp_id_desc,(void *)&func_comp_id_desc->bFirstInterfaceNumber, + sizeof(struct usb_os_function_comp_id_descriptor)-sizeof(rt_list_t)); + pusb_comp_id_desc += sizeof(struct usb_os_function_comp_id_descriptor)-sizeof(rt_list_t); + } + } + rt_usbd_ep0_write(device, (void*)usb_comp_id_desc, setup->wLength); + break; + case 0x05: + intf = rt_usbd_find_interface(device, setup->wValue & 0xFF, &func); + if(intf != RT_NULL) + { + intf->handler(func, setup); + } + break; + } + + break; + } + return RT_EOK; +} +static rt_err_t _dump_setup_packet(ureq_t setup) +{ + LOG_D("["); + LOG_D(" setup_request : 0x%x", + setup->request_type); + LOG_D(" value : 0x%x", setup->wValue); + LOG_D(" length : 0x%x", setup->wLength); + LOG_D(" index : 0x%x", setup->wIndex); + LOG_D(" request : 0x%x", setup->bRequest); + LOG_D("]"); + + return RT_EOK; +} + +/** + * This function will handle setup bRequest. + * + * @param device the usb device object. + * @param setup the setup bRequest. + * + * @return RT_EOK on successful, -RT_ERROR on invalid bRequest. + */ +static rt_err_t _setup_request(udevice_t device, ureq_t setup) +{ + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(setup != RT_NULL); + + _dump_setup_packet(setup); + + switch((setup->request_type & USB_REQ_TYPE_MASK)) + { + case USB_REQ_TYPE_STANDARD: + _standard_request(device, setup); + break; + case USB_REQ_TYPE_CLASS: + _function_request(device, setup); + break; + case USB_REQ_TYPE_VENDOR: + _vendor_request(device, setup); + break; + default: + rt_kprintf("unknown setup request type\n"); + rt_usbd_ep0_set_stall(device); + return -RT_ERROR; + } + + return RT_EOK; +} + +/** + * This function will hanle data notify event. + * + * @param device the usb device object. + * @param ep_msg the endpoint message. + * + * @return RT_EOK. + */ +static rt_err_t _data_notify(udevice_t device, struct ep_msg* ep_msg) +{ + uep_t ep; + ufunction_t func; + rt_size_t size = 0; + + RT_ASSERT(device != RT_NULL); + RT_ASSERT(ep_msg != RT_NULL); + + if (device->state != USB_STATE_CONFIGURED) + { + return -RT_ERROR; + } + + ep = rt_usbd_find_endpoint(device, &func, ep_msg->ep_addr); + if(ep == RT_NULL) + { + rt_kprintf("invalid endpoint\n"); + return -RT_ERROR; + } + + if(EP_ADDRESS(ep) & USB_DIR_IN) + { + size = ep_msg->size; + if(ep->request.remain_size >= EP_MAXPACKET(ep)) + { + dcd_ep_write(device->dcd, EP_ADDRESS(ep), ep->request.buffer, EP_MAXPACKET(ep)); + ep->request.remain_size -= EP_MAXPACKET(ep); + ep->request.buffer += EP_MAXPACKET(ep); + } + else if(ep->request.remain_size > 0) + { + dcd_ep_write(device->dcd, EP_ADDRESS(ep), ep->request.buffer, ep->request.remain_size); + ep->request.remain_size = 0; + } + else + { + EP_HANDLER(ep, func, size); + } + } + else + { + size = ep_msg->size; + if(ep->request.remain_size == 0) + { + return RT_EOK; + } + + if(size == 0) + { + size = dcd_ep_read(device->dcd, EP_ADDRESS(ep), ep->request.buffer); + } + ep->request.remain_size -= size; + ep->request.buffer += size; + + if(ep->request.req_type == UIO_REQUEST_READ_BEST) + { + EP_HANDLER(ep, func, size); + } + else if(ep->request.remain_size == 0) + { + EP_HANDLER(ep, func, ep->request.size); + } + else + { + dcd_ep_read_prepare(device->dcd, EP_ADDRESS(ep), ep->request.buffer, ep->request.remain_size > EP_MAXPACKET(ep) ? EP_MAXPACKET(ep) : ep->request.remain_size); + } + } + + return RT_EOK; +} + +static rt_err_t _ep0_out_notify(udevice_t device, struct ep_msg* ep_msg) +{ + uep_t ep0; + rt_size_t size; + + RT_ASSERT(device != RT_NULL); + RT_ASSERT(ep_msg != RT_NULL); + RT_ASSERT(device->dcd != RT_NULL); + + ep0 = &device->dcd->ep0; + size = ep_msg->size; + + if(ep0->request.remain_size == 0) + { + return RT_EOK; + } + if(size == 0) + { + size = dcd_ep_read(device->dcd, EP0_OUT_ADDR, ep0->request.buffer); + if(size == 0) + { + return RT_EOK; + } + } + + ep0->request.remain_size -= size; + ep0->request.buffer += size; + if(ep0->request.remain_size == 0) + { + /* invoke callback */ + if(ep0->rx_indicate != RT_NULL) + { + ep0->rx_indicate(device, size); + } + } + else + { + rt_usbd_ep0_read(device, ep0->request.buffer, ep0->request.remain_size,ep0->rx_indicate); + } + + return RT_EOK; +} + +/** + * This function will notity sof event to all of function. + * + * @param device the usb device object. + * + * @return RT_EOK. + */ +static rt_err_t _sof_notify(udevice_t device) +{ + struct rt_list_node *i; + ufunction_t func; + + RT_ASSERT(device != RT_NULL); + + /* to notity every function that sof event comes */ + for (i=device->curr_cfg->func_list.next; + i!=&device->curr_cfg->func_list; i=i->next) + { + func = (ufunction_t)rt_list_entry(i, struct ufunction, list); + if(func->ops->sof_handler != RT_NULL) + func->ops->sof_handler(func); + } + + return RT_EOK; +} + +/** + * This function will disable all USB functions. + * + * @param device the usb device object. + * + * @return RT_EOK. + */ +static rt_err_t _stop_notify(udevice_t device) +{ + struct rt_list_node *i; + ufunction_t func; + + RT_ASSERT(device != RT_NULL); + + /* to notity every function */ + for (i = device->curr_cfg->func_list.next; + i != &device->curr_cfg->func_list; + i = i->next) + { + func = (ufunction_t)rt_list_entry(i, struct ufunction, list); + FUNC_DISABLE(func); + } + + return RT_EOK; +} + +static rt_ssize_t rt_usbd_ep_write(udevice_t device, uep_t ep, void *buffer, rt_size_t size) +{ + rt_uint16_t maxpacket; + + RT_ASSERT(device != RT_NULL); + RT_ASSERT(device->dcd != RT_NULL); + RT_ASSERT(ep != RT_NULL); + + rt_enter_critical(); + maxpacket = EP_MAXPACKET(ep); + if(ep->request.remain_size >= maxpacket) + { + dcd_ep_write(device->dcd, EP_ADDRESS(ep), ep->request.buffer, maxpacket); + ep->request.remain_size -= maxpacket; + ep->request.buffer += maxpacket; + } + else + { + dcd_ep_write(device->dcd, EP_ADDRESS(ep), ep->request.buffer, + ep->request.remain_size); + ep->request.remain_size = 0; + } + rt_exit_critical(); + return size; +} + +static rt_ssize_t rt_usbd_ep_read_prepare(udevice_t device, uep_t ep, void *buffer, rt_size_t size) +{ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(device->dcd != RT_NULL); + RT_ASSERT(ep != RT_NULL); + RT_ASSERT(buffer != RT_NULL); + RT_ASSERT(ep->ep_desc != RT_NULL); + + return dcd_ep_read_prepare(device->dcd, EP_ADDRESS(ep), buffer, size > EP_MAXPACKET(ep) ? EP_MAXPACKET(ep) : size); +} + +/** + * This function will create an usb device object. + * + * @param ustring the usb string array to contain string descriptor. + * + * @return an usb device object on success, RT_NULL on fail. + */ +udevice_t rt_usbd_device_new(void) +{ + udevice_t udevice; + + LOG_D("rt_usbd_device_new"); + + /* allocate memory for the object */ + udevice = (udevice_t)rt_malloc(sizeof(struct udevice)); + if(udevice == RT_NULL) + { + rt_kprintf("alloc memory failed\n"); + return RT_NULL; + } + rt_memset(udevice, 0, sizeof(struct udevice)); + + /* to initialize configuration list */ + rt_list_init(&udevice->cfg_list); + + /* insert the device object to device list */ + rt_list_insert_before(&device_list, &udevice->list); + + return udevice; +} + +/** + * This function will set usb device string description. + * + * @param device the usb device object. + * @param ustring pointer to string pointer array. + * + * @return RT_EOK. + */ +rt_err_t rt_usbd_device_set_string(udevice_t device, const char** ustring) +{ + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(ustring != RT_NULL); + + /* set string descriptor array to the device object */ + device->str = ustring; + + return RT_EOK; +} + +/** + * This function will set usb device interface string description. + * + * @param device the usb device object. + * @param index of interface string + * @param string pointer to interface string description. + * + * @return RT_EOK. + */ +rt_err_t rt_usbd_device_set_interface_string(udevice_t device, int index, const char* string) +{ + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(string != RT_NULL); + RT_ASSERT(index < MAX_INTF_STR); + + /* set string descriptor array to the device object */ + device->str_intf[index] = string; + + return RT_EOK; +} + +rt_err_t rt_usbd_device_set_os_comp_id_desc(udevice_t device, usb_os_comp_id_desc_t os_comp_id_desc) +{ + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(os_comp_id_desc != RT_NULL); + + /* set string descriptor array to the device object */ + device->os_comp_id_desc = os_comp_id_desc; + rt_list_init(&device->os_comp_id_desc->func_desc); + return RT_EOK; +} + +rt_err_t rt_usbd_device_set_qualifier(udevice_t device, struct usb_qualifier_descriptor* qualifier) +{ + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(qualifier != RT_NULL); + + device->dev_qualifier = qualifier; + + return RT_EOK; +} + +/** + * This function will set an usb controller driver to a device. + * + * @param device the usb device object. + * @param dcd the usb device controller driver. + * + * @return RT_EOK on successful. + */ +rt_err_t rt_usbd_device_set_controller(udevice_t device, udcd_t dcd) +{ + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(dcd != RT_NULL); + + /* set usb device controller driver to the device */ + device->dcd = dcd; + + return RT_EOK; +} + +/** + * This function will set an usb device descriptor to a device. + * + * @param device the usb device object. + * @param dev_desc the usb device descriptor. + * + * @return RT_EOK on successful. + */ +rt_err_t rt_usbd_device_set_descriptor(udevice_t device, udev_desc_t dev_desc) +{ + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(dev_desc != RT_NULL); + + /* copy the usb device descriptor to the device */ + rt_memcpy((void *)&device->dev_desc, (void *)dev_desc, USB_DESC_LENGTH_DEVICE); + + return RT_EOK; +} + +/** + * This function will create an usb configuration object. + * + * @param none. + * + * @return an usb configuration object. + */ +uconfig_t rt_usbd_config_new(void) +{ + uconfig_t cfg; + + LOG_D("rt_usbd_config_new"); + + /* allocate memory for the object */ + cfg = (uconfig_t)rt_malloc(sizeof(struct uconfig)); + if(cfg == RT_NULL) + { + rt_kprintf("alloc memory failed\n"); + return RT_NULL; + } + rt_memset(cfg, 0, sizeof(struct uconfig)); + + /* set default wValue */ + cfg->cfg_desc.bLength = USB_DESC_LENGTH_CONFIG; + cfg->cfg_desc.type = USB_DESC_TYPE_CONFIGURATION; + cfg->cfg_desc.wTotalLength = USB_DESC_LENGTH_CONFIG; + cfg->cfg_desc.bmAttributes = 0xC0; + cfg->cfg_desc.MaxPower = 0x32; + + /* to initialize function object list */ + rt_list_init(&cfg->func_list); + + return cfg; +} + +/** + * This function will create an usb interface object. + * + * @param device the usb device object. + * @handler the callback handler of object + * + * @return an usb interface object on success, RT_NULL on fail. + */ +uintf_t rt_usbd_interface_new(udevice_t device, uintf_handler_t handler) +{ + uintf_t intf; + + LOG_D("rt_usbd_interface_new"); + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + + /* allocate memory for the object */ + intf = (uintf_t)rt_malloc(sizeof(struct uinterface)); + if(intf == RT_NULL) + { + rt_kprintf("alloc memory failed\n"); + return RT_NULL; + } + intf->intf_num = device->nr_intf; + device->nr_intf++; + intf->handler = handler; + intf->curr_setting = RT_NULL; + + /* to initialize the alternate setting object list */ + rt_list_init(&intf->setting_list); + + return intf; +} + +/** + * This function will create an usb alternate setting object. + * + * @param intf_desc the interface descriptor. + * @desc_size the size of the interface descriptor. + * + * @return an usb alternate setting object on success, RT_NULL on fail. + */ +ualtsetting_t rt_usbd_altsetting_new(rt_size_t desc_size) +{ + ualtsetting_t setting; + + LOG_D("rt_usbd_altsetting_new"); + + /* parameter check */ + RT_ASSERT(desc_size > 0); + + /* allocate memory for the object */ + setting = (ualtsetting_t)rt_malloc(sizeof(struct ualtsetting)); + if(setting == RT_NULL) + { + rt_kprintf("alloc memory failed\n"); + return RT_NULL; + } + /* allocate memory for the desc */ + setting->desc = rt_malloc(desc_size); + if (setting->desc == RT_NULL) + { + rt_kprintf("alloc desc memory failed\n"); + rt_free(setting); + return RT_NULL; + } + + setting->desc_size = desc_size; + setting->intf_desc = RT_NULL; + + /* to initialize endpoint list */ + rt_list_init(&setting->ep_list); + + return setting; +} + +/** + * This function will config an desc in alternate setting object. + * + * @param setting the altsetting to be config. + * @param desc use it to init desc in setting. + * @param intf_pos the offset of interface descriptor in desc. + * + * @return RT_EOK. + */ +rt_err_t rt_usbd_altsetting_config_descriptor(ualtsetting_t setting, const void* desc, rt_off_t intf_pos) +{ + RT_ASSERT(setting != RT_NULL); + RT_ASSERT(setting->desc !=RT_NULL); + + rt_memcpy(setting->desc, desc, setting->desc_size); + setting->intf_desc = (uintf_desc_t)((char*)setting->desc + intf_pos); + + return RT_EOK; +} + +/** + * This function will create an usb function object. + * + * @param device the usb device object. + * @param dev_desc the device descriptor. + * @param ops the operation set. + * + * @return an usb function object on success, RT_NULL on fail. + */ +ufunction_t rt_usbd_function_new(udevice_t device, udev_desc_t dev_desc, + ufunction_ops_t ops) +{ + ufunction_t func; + + LOG_D("rt_usbd_function_new"); + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(dev_desc != RT_NULL); + + /* allocate memory for the object */ + func = (ufunction_t)rt_malloc(sizeof(struct ufunction)); + if(func == RT_NULL) + { + rt_kprintf("alloc memory failed\n"); + return RT_NULL; + } + func->dev_desc = dev_desc; + func->ops = ops; + func->device = device; + func->enabled = RT_FALSE; + + /* to initialize interface list */ + rt_list_init(&func->intf_list); + + return func; +} + +/** + * This function will create an usb endpoint object. + * + * @param ep_desc the endpoint descriptor. + * @handler the callback handler of object + * + * @return an usb endpoint object on success, RT_NULL on fail. + */ +uep_t rt_usbd_endpoint_new(uep_desc_t ep_desc, udep_handler_t handler) +{ + uep_t ep; + + LOG_D("rt_usbd_endpoint_new"); + + /* parameter check */ + RT_ASSERT(ep_desc != RT_NULL); + + /* allocate memory for the object */ + ep = (uep_t)rt_malloc(sizeof(struct uendpoint)); + if(ep == RT_NULL) + { + rt_kprintf("alloc memory failed\n"); + return RT_NULL; + } + ep->ep_desc = ep_desc; + ep->handler = handler; + ep->buffer = RT_NULL; + ep->stalled = RT_FALSE; + rt_list_init(&ep->request_list); + + return ep; +} + +/** + * This function will find an usb device object. + * + * @dcd usd device controller driver. + * + * @return an usb device object on found or RT_NULL on not found. + */ +udevice_t rt_usbd_find_device(udcd_t dcd) +{ + struct rt_list_node* node; + udevice_t device; + + /* parameter check */ + RT_ASSERT(dcd != RT_NULL); + + /* search a device in the the device list */ + for (node = device_list.next; node != &device_list; node = node->next) + { + device = (udevice_t)rt_list_entry(node, struct udevice, list); + if(device->dcd == dcd) return device; + } + + rt_kprintf("can't find device\n"); + return RT_NULL; +} + +/** + * This function will find an usb configuration object. + * + * @param device the usb device object. + * @param wValue the configuration number. + * + * @return an usb configuration object on found or RT_NULL on not found. + */ +uconfig_t rt_usbd_find_config(udevice_t device, rt_uint8_t value) +{ + struct rt_list_node* node; + uconfig_t cfg = RT_NULL; + + LOG_D("rt_usbd_find_config"); + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(value <= device->dev_desc.bNumConfigurations); + + /* search a configration in the the device */ + for (node = device->cfg_list.next; node != &device->cfg_list; node = node->next) + { + cfg = (uconfig_t)rt_list_entry(node, struct udevice, list); + if(cfg->cfg_desc.bConfigurationValue == value) + { + return cfg; + } + } + + rt_kprintf("can't find configuration %d\n", value); + return RT_NULL; +} + +/** + * This function will find an usb interface object. + * + * @param device the usb device object. + * @param wValue the interface number. + * + * @return an usb configuration object on found or RT_NULL on not found. + */ +uintf_t rt_usbd_find_interface(udevice_t device, rt_uint8_t value, ufunction_t *pfunc) +{ + struct rt_list_node *i, *j; + ufunction_t func; + uintf_t intf; + + LOG_D("rt_usbd_find_interface"); + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(value < device->nr_intf); + + /* search an interface in the current configuration */ + for (i=device->curr_cfg->func_list.next; + i!=&device->curr_cfg->func_list; i=i->next) + { + func = (ufunction_t)rt_list_entry(i, struct ufunction, list); + for(j=func->intf_list.next; j!=&func->intf_list; j=j->next) + { + intf = (uintf_t)rt_list_entry(j, struct uinterface, list); + if(intf->intf_num == value) + { + if (pfunc != RT_NULL) + *pfunc = func; + return intf; + } + } + } + + rt_kprintf("can't find interface %d\n", value); + return RT_NULL; +} + +/** + * This function will find an usb interface alternate setting object. + * + * @param device the usb device object. + * @param wValue the alternate setting number. + * + * @return an usb interface alternate setting object on found or RT_NULL on not found. + */ +ualtsetting_t rt_usbd_find_altsetting(uintf_t intf, rt_uint8_t value) +{ + struct rt_list_node *i; + ualtsetting_t setting; + + LOG_D("rt_usbd_find_altsetting"); + + /* parameter check */ + RT_ASSERT(intf != RT_NULL); + + if(intf->curr_setting != RT_NULL) + { + /* if the wValue equal to the current alternate setting, then do not search */ + if(intf->curr_setting->intf_desc->bAlternateSetting == value) + return intf->curr_setting; + } + + /* search a setting in the alternate setting list */ + for(i=intf->setting_list.next; i!=&intf->setting_list; i=i->next) + { + setting =(ualtsetting_t)rt_list_entry(i, struct ualtsetting, list); + if(setting->intf_desc->bAlternateSetting == value) + return setting; + } + + rt_kprintf("can't find alternate setting %d\n", value); + return RT_NULL; +} + +/** + * This function will find an usb endpoint object. + * + * @param device the usb device object. + * @param ep_addr endpoint address. + * + * @return an usb endpoint object on found or RT_NULL on not found. + */ +uep_t rt_usbd_find_endpoint(udevice_t device, ufunction_t* pfunc, rt_uint8_t ep_addr) +{ + uep_t ep; + struct rt_list_node *i, *j, *k; + ufunction_t func; + uintf_t intf; + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + + /* search a endpoint in the current configuration */ + for (i=device->curr_cfg->func_list.next; i!=&device->curr_cfg->func_list; i=i->next) + { + func = (ufunction_t)rt_list_entry(i, struct ufunction, list); + for(j=func->intf_list.next; j!=&func->intf_list; j=j->next) + { + intf = (uintf_t)rt_list_entry(j, struct uinterface, list); + for(k=intf->curr_setting->ep_list.next; + k!=&intf->curr_setting->ep_list; k=k->next) + { + ep = (uep_t)rt_list_entry(k, struct uendpoint, list); + if(EP_ADDRESS(ep) == ep_addr) + { + if (pfunc != RT_NULL) + *pfunc = func; + return ep; + } + } + } + } + + rt_kprintf("can't find endpoint 0x%x\n", ep_addr); + return RT_NULL; +} + +/** + * This function will add a configuration to an usb device. + * + * @param device the usb device object. + * @param cfg the configuration object. + * + * @return RT_EOK. + */ +rt_err_t rt_usbd_device_add_config(udevice_t device, uconfig_t cfg) +{ + struct rt_list_node *i, *j, *k, *m; + ufunction_t func; + uintf_t intf; + ualtsetting_t altsetting; + uep_t ep; + + LOG_D("rt_usbd_device_add_config"); + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(cfg != RT_NULL); + + /* set configuration number to the configuration descriptor */ + cfg->cfg_desc.bConfigurationValue = device->dev_desc.bNumConfigurations + 1; + device->dev_desc.bNumConfigurations++; + + for (i=cfg->func_list.next; i!=&cfg->func_list; i=i->next) + { + func = (ufunction_t)rt_list_entry(i, struct ufunction, list); + + for(j=func->intf_list.next; j!=&func->intf_list; j=j->next) + { + intf = (uintf_t)rt_list_entry(j, struct uinterface, list); + cfg->cfg_desc.bNumInterfaces++; + + for(k=intf->setting_list.next; k!=&intf->setting_list;k=k->next) + { + altsetting = (ualtsetting_t)rt_list_entry(k, struct ualtsetting, list); + + /* allocate address for every endpoint in the interface alternate setting */ + for(m=altsetting->ep_list.next; m!=&altsetting->ep_list; m=m->next) + { + ep = (uep_t)rt_list_entry(m, struct uendpoint, list); + if(rt_usbd_ep_assign(device, ep) != RT_EOK) + { + rt_kprintf("endpoint assign error\n"); + } + } + + /* construct complete configuration descriptor */ + rt_memcpy((void*)&cfg->cfg_desc.data[cfg->cfg_desc.wTotalLength - USB_DESC_LENGTH_CONFIG], + (void*)altsetting->desc, + altsetting->desc_size); + cfg->cfg_desc.wTotalLength += altsetting->desc_size; + } + } + } + + /* insert the configuration to the list */ + rt_list_insert_before(&device->cfg_list, &cfg->list); + + return RT_EOK; +} + +/** + * This function will add a function to a configuration. + * + * @param cfg the configuration object. + * @param func the function object. + * + * @return RT_EOK. + */ +rt_err_t rt_usbd_config_add_function(uconfig_t cfg, ufunction_t func) +{ + LOG_D("rt_usbd_config_add_function"); + + /* parameter check */ + RT_ASSERT(cfg != RT_NULL); + RT_ASSERT(func != RT_NULL); + + /* insert the function to the list */ + rt_list_insert_before(&cfg->func_list, &func->list); + + return RT_EOK; +} + +/** + * This function will add an interface to a function. + * + * @param func the function object. + * @param intf the interface object. + * + * @return RT_EOK. + */ +rt_err_t rt_usbd_function_add_interface(ufunction_t func, uintf_t intf) +{ + + LOG_D("rt_usbd_function_add_interface"); + + /* parameter check */ + RT_ASSERT(func != RT_NULL); + RT_ASSERT(intf != RT_NULL); + + /* insert the interface to the list */ + rt_list_insert_before(&func->intf_list, &intf->list); + + return RT_EOK; +} + +/** + * This function will add an alternate setting to an interface. + * + * @param intf the interface object. + * @param setting the alternate setting object. + * + * @return RT_EOK. + */ +rt_err_t rt_usbd_interface_add_altsetting(uintf_t intf, ualtsetting_t setting) +{ + LOG_D("rt_usbd_interface_add_altsetting"); + + /* parameter check */ + RT_ASSERT(intf != RT_NULL); + RT_ASSERT(setting != RT_NULL); + + setting->intf_desc->bInterfaceNumber = intf->intf_num; + + /* insert the alternate setting to the list */ + rt_list_insert_before(&intf->setting_list, &setting->list); + + return RT_EOK; +} + +/** + * This function will add an endpoint to an alternate setting. + * + * @param setting the alternate setting object. + * @param ep the endpoint object. + * + * @return RT_EOK. + */ +rt_err_t rt_usbd_altsetting_add_endpoint(ualtsetting_t setting, uep_t ep) +{ + LOG_D("rt_usbd_altsetting_add_endpoint"); + + /* parameter check */ + RT_ASSERT(setting != RT_NULL); + RT_ASSERT(ep != RT_NULL); + + /* insert the endpoint to the list */ + rt_list_insert_before(&setting->ep_list, &ep->list); + + return RT_EOK; +} + +rt_err_t rt_usbd_os_comp_id_desc_add_os_func_comp_id_desc(usb_os_comp_id_desc_t os_comp_id_desc, usb_os_func_comp_id_desc_t os_func_comp_id_desc) +{ + RT_ASSERT(os_comp_id_desc != RT_NULL); + RT_ASSERT(os_func_comp_id_desc != RT_NULL); + rt_list_insert_before(&os_comp_id_desc->func_desc, &os_func_comp_id_desc->list); + os_comp_id_desc->head_desc.bCount++; + return RT_EOK; +} + +/** + * This function will set an alternate setting for an interface. + * + * @param intf_desc the interface descriptor. + * @param wValue the alternate setting number. + * + * @return RT_EOK. + */ +rt_err_t rt_usbd_set_altsetting(uintf_t intf, rt_uint8_t value) +{ + ualtsetting_t setting; + + LOG_D("rt_usbd_set_altsetting"); + + /* parameter check */ + RT_ASSERT(intf != RT_NULL); + + /* find an alternate setting */ + setting = rt_usbd_find_altsetting(intf, value); + + /* set as current alternate setting */ + intf->curr_setting = setting; + + return RT_EOK; +} + +/** + * This function will set a configuration for an usb device. + * + * @param device the usb device object. + * @param wValue the configuration number. + * + * @return RT_EOK. + */ +rt_err_t rt_usbd_set_config(udevice_t device, rt_uint8_t value) +{ + uconfig_t cfg; + + LOG_D("rt_usbd_set_config"); + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(value <= device->dev_desc.bNumConfigurations); + + /* find a configuration */ + cfg = rt_usbd_find_config(device, value); + + /* set as current configuration */ + device->curr_cfg = cfg; + + dcd_set_config(device->dcd, value); + + return RT_TRUE; +} + +/** + * This function will bRequest an IO transaction. + * + * @param device the usb device object. + * @param ep the endpoint object. + * @param req IO bRequest. + * + * @return RT_EOK. + */ +rt_size_t rt_usbd_io_request(udevice_t device, uep_t ep, uio_request_t req) +{ + rt_size_t size = 0; + + RT_ASSERT(device != RT_NULL); + RT_ASSERT(req != RT_NULL); + + if(ep->stalled == RT_FALSE) + { + switch(req->req_type) + { + case UIO_REQUEST_READ_BEST: + case UIO_REQUEST_READ_FULL: + ep->request.remain_size = ep->request.size; + size = rt_usbd_ep_read_prepare(device, ep, req->buffer, req->size); + break; + case UIO_REQUEST_WRITE: + ep->request.remain_size = ep->request.size; + size = rt_usbd_ep_write(device, ep, req->buffer, req->size); + break; + default: + rt_kprintf("unknown request type\n"); + break; + } + } + else + { + rt_list_insert_before(&ep->request_list, &req->list); + LOG_D("suspend a request"); + } + + return size; +} + +/** + * This function will set feature for an usb device. + * + * @param device the usb device object. + * @param wValue the configuration number. + * + * @return RT_EOK. + */ +rt_err_t rt_usbd_set_feature(udevice_t device, rt_uint16_t value, rt_uint16_t index) +{ + RT_ASSERT(device != RT_NULL); + + if (value == USB_FEATURE_DEV_REMOTE_WAKEUP) + { + LOG_D("set feature remote wakeup"); + } + else if (value == USB_FEATURE_ENDPOINT_HALT) + { + LOG_D("set feature stall"); + dcd_ep_set_stall(device->dcd, (rt_uint32_t)(index & 0xFF)); + } + + return RT_EOK; +} + +/** + * This function will clear feature for an usb device. + * + * @param device the usb device object. + * @param wValue the configuration number. + * + * @return RT_EOK. + */ +rt_err_t rt_usbd_clear_feature(udevice_t device, rt_uint16_t value, rt_uint16_t index) +{ + RT_ASSERT(device != RT_NULL); + + if (value == USB_FEATURE_DEV_REMOTE_WAKEUP) + { + LOG_D("clear feature remote wakeup"); + } + else if (value == USB_FEATURE_ENDPOINT_HALT) + { + LOG_D("clear feature stall"); + dcd_ep_clear_stall(device->dcd, (rt_uint32_t)(index & 0xFF)); + } + + return RT_EOK; +} + +rt_err_t rt_usbd_ep0_set_stall(udevice_t device) +{ + RT_ASSERT(device != RT_NULL); + + return dcd_ep_set_stall(device->dcd, 0x80); +} + +rt_err_t rt_usbd_ep0_clear_stall(udevice_t device) +{ + RT_ASSERT(device != RT_NULL); + + return dcd_ep_clear_stall(device->dcd, 0x80); +} + +rt_err_t rt_usbd_ep_set_stall(udevice_t device, uep_t ep) +{ + rt_err_t ret; + + RT_ASSERT(device != RT_NULL); + RT_ASSERT(ep != RT_NULL); + RT_ASSERT(ep->ep_desc != RT_NULL); + + ret = dcd_ep_set_stall(device->dcd, EP_ADDRESS(ep)); + if(ret == RT_EOK) + { + ep->stalled = RT_TRUE; + } + + return ret; +} + +rt_err_t rt_usbd_ep_clear_stall(udevice_t device, uep_t ep) +{ + rt_err_t ret; + + RT_ASSERT(device != RT_NULL); + RT_ASSERT(ep != RT_NULL); + RT_ASSERT(ep->ep_desc != RT_NULL); + + ret = dcd_ep_clear_stall(device->dcd, EP_ADDRESS(ep)); + if(ret == RT_EOK) + { + ep->stalled = RT_FALSE; + } + + return ret; +} + +static rt_err_t rt_usbd_ep_assign(udevice_t device, uep_t ep) +{ + int i = 0; + + RT_ASSERT(device != RT_NULL); + RT_ASSERT(device->dcd != RT_NULL); + RT_ASSERT(device->dcd->ep_pool != RT_NULL); + RT_ASSERT(ep != RT_NULL); + RT_ASSERT(ep->ep_desc != RT_NULL); + + while(device->dcd->ep_pool[i].addr != 0xFF) + { + if(device->dcd->ep_pool[i].status == ID_UNASSIGNED && + ep->ep_desc->bmAttributes == device->dcd->ep_pool[i].type && (EP_ADDRESS(ep) & 0x80) == device->dcd->ep_pool[i].dir) + { + EP_ADDRESS(ep) |= device->dcd->ep_pool[i].addr; + ep->id = &device->dcd->ep_pool[i]; + device->dcd->ep_pool[i].status = ID_ASSIGNED; + + LOG_D("assigned %d", device->dcd->ep_pool[i].addr); + return RT_EOK; + } + + i++; + } + + return -RT_ERROR; +} + +rt_err_t rt_usbd_ep_unassign(udevice_t device, uep_t ep) +{ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(device->dcd != RT_NULL); + RT_ASSERT(device->dcd->ep_pool != RT_NULL); + RT_ASSERT(ep != RT_NULL); + RT_ASSERT(ep->ep_desc != RT_NULL); + + ep->id->status = ID_UNASSIGNED; + + return RT_EOK; +} + +rt_err_t rt_usbd_ep0_setup_handler(udcd_t dcd, struct urequest* setup) +{ + struct udev_msg msg; + rt_size_t size; + + RT_ASSERT(dcd != RT_NULL); + + if(setup == RT_NULL) + { + size = dcd_ep_read(dcd, EP0_OUT_ADDR, (void*)&msg.content.setup); + if(size != sizeof(struct urequest)) + { + rt_kprintf("read setup packet error\n"); + return -RT_ERROR; + } + } + else + { + rt_memcpy((void*)&msg.content.setup, (void*)setup, sizeof(struct urequest)); + } + + msg.type = USB_MSG_SETUP_NOTIFY; + msg.dcd = dcd; + rt_usbd_event_signal(&msg); + + return RT_EOK; +} + +rt_err_t rt_usbd_ep0_in_handler(udcd_t dcd) +{ + rt_int32_t remain, mps; + + RT_ASSERT(dcd != RT_NULL); + + if (dcd->stage != STAGE_DIN) + return RT_EOK; + + mps = dcd->ep0.id->maxpacket; + dcd->ep0.request.remain_size -= mps; + remain = dcd->ep0.request.remain_size; + + if (remain > 0) + { + if (remain >= mps) + { + remain = mps; + } + + dcd->ep0.request.buffer += mps; + dcd_ep_write(dcd, EP0_IN_ADDR, dcd->ep0.request.buffer, remain); + } + else + { + /* last packet is MPS multiple, so send ZLP packet */ + if ((remain == 0) && (dcd->ep0.request.size > 0)) + { + dcd->ep0.request.size = 0; + dcd_ep_write(dcd, EP0_IN_ADDR, RT_NULL, 0); + } + else + { + /* receive status */ + dcd->stage = STAGE_STATUS_OUT; + dcd_ep_read_prepare(dcd, EP0_OUT_ADDR, RT_NULL, 0); + } + } + + return RT_EOK; +} + +rt_err_t rt_usbd_ep0_out_handler(udcd_t dcd, rt_size_t size) +{ + struct udev_msg msg; + + RT_ASSERT(dcd != RT_NULL); + + msg.type = USB_MSG_EP0_OUT; + msg.dcd = dcd; + msg.content.ep_msg.size = size; + rt_usbd_event_signal(&msg); + + return RT_EOK; +} + +rt_err_t rt_usbd_ep_in_handler(udcd_t dcd, rt_uint8_t address, rt_size_t size) +{ + struct udev_msg msg; + + RT_ASSERT(dcd != RT_NULL); + + msg.type = USB_MSG_DATA_NOTIFY; + msg.dcd = dcd; + msg.content.ep_msg.ep_addr = address; + msg.content.ep_msg.size = size; + rt_usbd_event_signal(&msg); + + return RT_EOK; +} + +rt_err_t rt_usbd_ep_out_handler(udcd_t dcd, rt_uint8_t address, rt_size_t size) +{ + struct udev_msg msg; + + RT_ASSERT(dcd != RT_NULL); + + msg.type = USB_MSG_DATA_NOTIFY; + msg.dcd = dcd; + msg.content.ep_msg.ep_addr = address; + msg.content.ep_msg.size = size; + rt_usbd_event_signal(&msg); + + return RT_EOK; +} + +rt_err_t rt_usbd_reset_handler(udcd_t dcd) +{ + struct udev_msg msg; + + RT_ASSERT(dcd != RT_NULL); + + msg.type = USB_MSG_RESET; + msg.dcd = dcd; + rt_usbd_event_signal(&msg); + + return RT_EOK; +} + +rt_err_t rt_usbd_connect_handler(udcd_t dcd) +{ + struct udev_msg msg; + + RT_ASSERT(dcd != RT_NULL); + + msg.type = USB_MSG_PLUG_IN; + msg.dcd = dcd; + rt_usbd_event_signal(&msg); + + return RT_EOK; +} + +rt_err_t rt_usbd_disconnect_handler(udcd_t dcd) +{ + struct udev_msg msg; + + RT_ASSERT(dcd != RT_NULL); + + msg.type = USB_MSG_PLUG_OUT; + msg.dcd = dcd; + rt_usbd_event_signal(&msg); + + return RT_EOK; +} + +rt_err_t rt_usbd_sof_handler(udcd_t dcd) +{ + struct udev_msg msg; + + RT_ASSERT(dcd != RT_NULL); + + msg.type = USB_MSG_SOF; + msg.dcd = dcd; + rt_usbd_event_signal(&msg); + + return RT_EOK; +} + +rt_size_t rt_usbd_ep0_write(udevice_t device, void *buffer, rt_size_t size) +{ + uep_t ep0; + rt_size_t sent_size = 0; + + RT_ASSERT(device != RT_NULL); + RT_ASSERT(device->dcd != RT_NULL); + RT_ASSERT(buffer != RT_NULL); + RT_ASSERT(size > 0); + + ep0 = &device->dcd->ep0; + ep0->request.size = size; + ep0->request.buffer = (rt_uint8_t *)buffer; + ep0->request.remain_size = size; + if(size >= ep0->id->maxpacket) + { + sent_size = ep0->id->maxpacket; + } + else + { + sent_size = size; + } + device->dcd->stage = STAGE_DIN; + + return dcd_ep_write(device->dcd, EP0_IN_ADDR, ep0->request.buffer, sent_size); +} + +rt_size_t rt_usbd_ep0_read(udevice_t device, void *buffer, rt_size_t size, + rt_err_t (*rx_ind)(udevice_t device, rt_size_t size)) +{ + uep_t ep0; + rt_size_t read_size = 0; + + RT_ASSERT(device != RT_NULL); + RT_ASSERT(device->dcd != RT_NULL); + RT_ASSERT(buffer != RT_NULL); + + ep0 = &device->dcd->ep0; + ep0->request.buffer = (rt_uint8_t *)buffer; + ep0->request.remain_size = size; + ep0->rx_indicate = rx_ind; + if(size >= ep0->id->maxpacket) + { + read_size = ep0->id->maxpacket; + } + else + { + read_size = size; + } + device->dcd->stage = STAGE_DOUT; + dcd_ep_read_prepare(device->dcd, EP0_OUT_ADDR, buffer, read_size); + + return size; +} + +static struct rt_messagequeue usb_mq; + +/** + * This function is the main entry of usb device thread, it is in charge of + * processing all messages received from the usb message buffer. + * + * @param parameter the parameter of the usb device thread. + * + * @return none. + */ +static void rt_usbd_thread_entry(void* parameter) +{ + while(1) + { + struct udev_msg msg; + udevice_t device; + + /* receive message */ + if(rt_mq_recv(&usb_mq, &msg, sizeof(struct udev_msg), + RT_WAITING_FOREVER) < 0) + continue; + + device = rt_usbd_find_device(msg.dcd); + if(device == RT_NULL) + { + rt_kprintf("invalid usb device\n"); + continue; + } + + LOG_D("message type %d", msg.type); + + switch (msg.type) + { + case USB_MSG_SOF: + _sof_notify(device); + break; + case USB_MSG_DATA_NOTIFY: + /* some buggy drivers will have USB_MSG_DATA_NOTIFY before the core + * got configured. */ + _data_notify(device, &msg.content.ep_msg); + break; + case USB_MSG_SETUP_NOTIFY: + _setup_request(device, &msg.content.setup); + break; + case USB_MSG_EP0_OUT: + _ep0_out_notify(device, &msg.content.ep_msg); + break; + case USB_MSG_RESET: + LOG_D("reset %d", device->state); + if (device->state == USB_STATE_ADDRESS || device->state == USB_STATE_CONFIGURED) + _stop_notify(device); + device->state = USB_STATE_NOTATTACHED; + break; + case USB_MSG_PLUG_IN: + device->state = USB_STATE_ATTACHED; + break; + case USB_MSG_PLUG_OUT: + device->state = USB_STATE_NOTATTACHED; + _stop_notify(device); + break; + default: + rt_kprintf("unknown msg type %d\n", msg.type); + break; + } + } +} + +/** + * This function will post an message to usb message queue, + * + * @param msg the message to be posted + * @param size the size of the message . + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbd_event_signal(struct udev_msg* msg) +{ + RT_ASSERT(msg != RT_NULL); + + /* send message to usb message queue */ + return rt_mq_send(&usb_mq, (void*)msg, sizeof(struct udev_msg)); +} + + +rt_align(RT_ALIGN_SIZE) +static rt_uint8_t usb_thread_stack[RT_USBD_THREAD_STACK_SZ]; +static struct rt_thread usb_thread; +#define USBD_MQ_MSG_SZ 32 +#define USBD_MQ_MAX_MSG 16 +/* internal of the message queue: every message is associated with a pointer, + * so in order to recveive USBD_MQ_MAX_MSG messages, we have to allocate more + * than USBD_MQ_MSG_SZ*USBD_MQ_MAX_MSG memory. */ +static rt_uint8_t usb_mq_pool[(USBD_MQ_MSG_SZ+sizeof(void*))*USBD_MQ_MAX_MSG]; + +/** + * This function will initialize usb device thread. + * + * @return none. + * + */ +rt_err_t rt_usbd_core_init(void) +{ + rt_list_init(&device_list); + + /* create an usb message queue */ + rt_mq_init(&usb_mq, + "usbd", + usb_mq_pool, USBD_MQ_MSG_SZ, + sizeof(usb_mq_pool), + RT_IPC_FLAG_FIFO); + + /* init usb device thread */ + rt_thread_init(&usb_thread, + "usbd", + rt_usbd_thread_entry, RT_NULL, + usb_thread_stack, RT_USBD_THREAD_STACK_SZ, + RT_USBD_THREAD_PRIO, 20); + /* rt_thread_init should always be OK, so start the thread without further + * checking. */ + return rt_thread_startup(&usb_thread); +} + diff --git a/components/legacy/usb/usbhost/SConscript b/components/legacy/usb/usbhost/SConscript new file mode 100644 index 0000000000..aa5385d52a --- /dev/null +++ b/components/legacy/usb/usbhost/SConscript @@ -0,0 +1,34 @@ +Import('RTT_ROOT') +from building import * + +cwd = GetCurrentDir() +src = Split(""" +core/usbhost_core.c +core/driver.c +core/usbhost.c +core/hub.c +""") + +if GetDepend('RT_USBH_ADK'): + src += Glob('class/adk.c') + src += Glob('class/adkapp.c') + +if GetDepend('RT_USBH_MSTORAGE'): + src += Glob('class/mass.c') + src += Glob('class/udisk.c') + +if GetDepend('RT_USBH_HID'): + src += Glob('class/hid.c') + +if GetDepend('RT_USBH_HID_MOUSE'): + src += Glob('class/umouse.c') + +if GetDepend('RT_USBH_HID_KEYBOARD'): + src += Glob('class/ukbd.c') + +CPPPATH = [cwd, cwd + '/class', cwd + '/core', \ + cwd + '/include', cwd + '../../../include'] + +group = DefineGroup('rt_usbh', src, depend = ['RT_USING_USB_HOST'], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/legacy/usb/usbhost/class/adk.c b/components/legacy/usb/usbhost/class/adk.c new file mode 100644 index 0000000000..4c51fa8692 --- /dev/null +++ b/components/legacy/usb/usbhost/class/adk.c @@ -0,0 +1,426 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2011-12-12 Yi Qiu first version + */ + +#include <rtthread.h> +#include <drivers/usb_host.h> +#include "adk.h" + +#ifdef RT_USBH_ADK + +#define DBG_TAG "usbhost.adk" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + +static struct uclass_driver adk_driver; +static const char* _adk_manufacturer = RT_NULL; +static const char* _adk_model = RT_NULL; +static const char* _adk_description = RT_NULL; +static const char* _adk_version = RT_NULL; +static const char* _adk_uri = RT_NULL; +static const char* _adk_serial = RT_NULL; + +rt_err_t rt_usbh_adk_set_string(const char* manufacturer, const char* model, + const char* description, const char* _version, const char* uri, + const char* serial) +{ + _adk_manufacturer = manufacturer; + _adk_model = model; + _adk_description = description; + _adk_version = _version; + _adk_uri = uri; + _adk_serial = serial; + + return RT_EOK; +} + +#ifdef RT_USING_MODULE +#include <rtm.h> + +RTM_EXPORT(rt_usbh_adk_set_string); +#endif + +/** + * This function will do USB_REQ_GET_PROTOCOL request to set idle period to the usb adk device + * + * @param intf the interface instance. + * @duration the idle period of requesting data. + * @report_id the report id + * + * @return the error code, RT_EOK on successfully. +*/ +static rt_err_t rt_usbh_adk_get_protocol(struct uintf* intf, rt_uint16_t *protocol) +{ + struct urequest setup; + uinst_t device; + int timeout = USB_TIMEOUT_BASIC; + + /* parameter check */ + RT_ASSERT(intf != RT_NULL); + RT_ASSERT(intf->device != RT_NULL); + + device = intf->device; + + setup.request_type = USB_REQ_TYPE_DIR_IN | USB_REQ_TYPE_VENDOR | + USB_REQ_TYPE_DEVICE; + setup.request = USB_REQ_GET_PROTOCOL; + setup.index = 0; + setup.length = 2; + setup.value = 0; + + if(rt_usb_hcd_control_xfer(device->hcd, device, &setup, (void*)protocol, 2, + timeout) == 0) return RT_EOK; + else return -RT_FALSE; +} + +/** + * This function will do USB_REQ_SEND_STRING request to set idle period to the usb adk device + * + * @param intf the interface instance. + * @duration the idle period of requesting data. + * @report_id the report id + * + * @return the error code, RT_EOK on successfully. +*/ +static rt_err_t rt_usbh_adk_send_string(struct uintf* intf, rt_uint16_t index, + const char* str) +{ + struct urequest setup; + uinst_t device; + int timeout = USB_TIMEOUT_BASIC; + + /* parameter check */ + RT_ASSERT(intf != RT_NULL); + RT_ASSERT(intf->device != RT_NULL); + + device = intf->device; + + setup.request_type = USB_REQ_TYPE_DIR_OUT | USB_REQ_TYPE_VENDOR | + USB_REQ_TYPE_DEVICE; + setup.request = USB_REQ_SEND_STRING; + setup.index = index; + setup.length = rt_strlen(str) + 1; + setup.value = 0; + + if(rt_usb_hcd_control_xfer(device->hcd, device, &setup, (void*)str, + rt_strlen(str) + 1, timeout) == 0) return RT_EOK; + else return -RT_FALSE; +} + +/** + * This function will do USB_REQ_START request to set idle period to the usb adk device + * + * @param intf the interface instance. + * @duration the idle period of requesting data. + * @report_id the report id + * + * @return the error code, RT_EOK on successfully. +*/ +static rt_err_t rt_usbh_adk_start(struct uintf* intf) +{ + struct urequest setup; + uinst_t device; + int timeout = USB_TIMEOUT_BASIC; + + /* parameter check */ + RT_ASSERT(intf != RT_NULL); + RT_ASSERT(intf->device != RT_NULL); + + device = intf->device; + + setup.request_type = USB_REQ_TYPE_DIR_OUT | USB_REQ_TYPE_VENDOR | + USB_REQ_TYPE_DEVICE; + setup.request = USB_REQ_START; + setup.index = 0; + setup.length = 0; + setup.value = 0; + + if(rt_usb_hcd_control_xfer(device->hcd, device, &setup, RT_NULL, 0, + timeout) == 0) return RT_EOK; + else return -RT_FALSE; +} + +/** + * This function will read data from usb adk device + * + * @param intf the interface instance. + * + * @return the error code, RT_EOK on successfully. +*/ +static rt_ssize_t rt_usbh_adk_read(rt_device_t device, rt_off_t pos, void* buffer, + rt_size_t size) +{ + uadk_t adk; + rt_size_t length; + struct uintf* intf; + + /* check parameter */ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(buffer != RT_NULL); + + intf = (struct uintf*)device->user_data; + adk = (uadk_t)intf->user_data; + + length = rt_usb_hcd_bulk_xfer(intf->device->hcd, adk->pipe_in, + buffer, size, 300); + + return length; + +} + +/** + * This function will write data to usb adk device + * + * @param intf the interface instance. + * + * @return the error code, RT_EOK on successfully. +*/ +static rt_ssize_t rt_usbh_adk_write (rt_device_t device, rt_off_t pos, const void* buffer, + rt_size_t size) +{ + uadk_t adk; + rt_size_t length; + struct uintf* intf; + + RT_ASSERT(buffer != RT_NULL); + + intf = (struct uintf*)device->user_data; + adk = (uadk_t)intf->user_data; + + length = rt_usb_hcd_bulk_xfer(intf->device->hcd, adk->pipe_out, + (void*)buffer, size, 300); + + return length; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops adk_device_ops = +{ + RT_NULL; + RT_NULL; + RT_NULL; + rt_usbh_adk_read; + rt_usbh_adk_write; + RT_NULL; +}; +#endif + +/** + * This function will run adk class driver when usb device is detected and identified + * as a adk class device, it will continue the enumulate process. + * + * @param arg the argument. + * + * @return the error code, RT_EOK on successfully. + */ +static rt_err_t rt_usbh_adk_enable(void* arg) +{ + int i = 0; + uadk_t adk; + struct uintf* intf = (struct uintf*)arg; + udev_desc_t dev_desc; + rt_uint16_t protocol; + rt_err_t ret; + + /* parameter check */ + if(intf == RT_NULL) + { + rt_kprintf("the interface is not available\n"); + return -RT_EIO; + } + + LOG_D("rt_usbh_adk_run"); + + dev_desc = &intf->device->dev_desc; + if(dev_desc->idVendor == USB_ACCESSORY_VENDOR_ID && + (dev_desc->idProduct == USB_ACCESSORY_PRODUCT_ID || + dev_desc->idProduct == USB_ACCESSORY_ADB_PRODUCT_ID)) + { + if(intf->intf_desc->bInterfaceSubClass != 0xFF) return -RT_ERROR; + + LOG_D("found android accessory device"); + } + else + { + LOG_D("switch device"); + + if((ret = rt_usbh_adk_get_protocol(intf, &protocol)) != RT_EOK) + { + rt_kprintf("rt_usbh_adk_get_protocol failed\n"); + return ret; + } + + if(protocol != 1) + { + rt_kprintf("read protocol failed\n"); + return -RT_ERROR; + } + + rt_usbh_adk_send_string(intf, + ACCESSORY_STRING_MANUFACTURER, _adk_manufacturer); + rt_usbh_adk_send_string(intf, + ACCESSORY_STRING_MODEL, _adk_model); + rt_usbh_adk_send_string(intf, + ACCESSORY_STRING_DESCRIPTION, _adk_description); + rt_usbh_adk_send_string(intf, + ACCESSORY_STRING_VERSION, _adk_version); + rt_usbh_adk_send_string(intf, + ACCESSORY_STRING_URI, _adk_uri); + rt_usbh_adk_send_string(intf, + ACCESSORY_STRING_SERIAL, _adk_serial); + + LOG_D("manufacturer %s", _adk_manufacturer); + LOG_D("model %s", _adk_model); + LOG_D("description %s", _adk_description); + LOG_D("version %s", _adk_version); + LOG_D("uri %s", _adk_uri); + LOG_D("serial %s", _adk_serial); + + if((ret = rt_usbh_adk_start(intf)) != RT_EOK) + { + rt_kprintf("rt_usbh_adk_start failed\n"); + return ret; + } + + return RT_EOK; + } + + adk = rt_malloc(sizeof(struct uadkinst)); + RT_ASSERT(adk != RT_NULL); + + /* initilize the data structure */ + rt_memset(adk, 0, sizeof(struct uadkinst)); + intf->user_data = (void*)adk; + + for(i=0; i<intf->intf_desc->bNumEndpoints; i++) + { + uep_desc_t ep_desc; + + /* get endpoint descriptor from interface descriptor */ + rt_usbh_get_endpoint_descriptor(intf->intf_desc, i, &ep_desc); + if(ep_desc == RT_NULL) + { + rt_kprintf("rt_usb_get_endpoint_descriptor error\n"); + return -RT_ERROR; + } + + /* the endpoint type of adk class should be BULK */ + if((ep_desc->bmAttributes & USB_EP_ATTR_TYPE_MASK) != USB_EP_ATTR_BULK) + continue; + + /* allocate pipes according to the endpoint type */ + if(ep_desc->bEndpointAddress & USB_DIR_IN) + { + /* allocate an in pipe for the adk instance */ + ret = rt_usb_hcd_alloc_pipe(intf->device->hcd, &adk->pipe_in, + intf, ep_desc, RT_NULL); + if(ret != RT_EOK) return ret; + } + else + { + /* allocate an output pipe for the adk instance */ + ret = rt_usb_hcd_alloc_pipe(intf->device->hcd, &adk->pipe_out, + intf, ep_desc, RT_NULL); + if(ret != RT_EOK) return ret; + } + } + + /* check pipes infomation */ + if(adk->pipe_in == RT_NULL || adk->pipe_out == RT_NULL) + { + rt_kprintf("pipe error, unsupported device\n"); + return -RT_ERROR; + } + + /* set configuration */ + ret = rt_usbh_set_configure(intf->device, 1); + if(ret != RT_EOK) return ret; + + /* register adk device */ + adk->device.type = RT_Device_Class_Char; +#ifdef RT_USING_DEVICE_OPS + adk->device.ops = &adk_device_ops; +#else + adk->device.init = RT_NULL; + adk->device.open = RT_NULL; + adk->device.close = RT_NULL; + adk->device.read = rt_usbh_adk_read; + adk->device.write = rt_usbh_adk_write; + adk->device.control = RT_NULL; +#endif + adk->device.user_data = (void*)intf; + + rt_device_register(&adk->device, "adkdev", RT_DEVICE_FLAG_RDWR); + + return RT_EOK; +} + +/** + * This function will be invoked when usb device plug out is detected and it would clean + * and release all hub class related resources. + * + * @param arg the argument. + * + * @return the error code, RT_EOK on successfully. + */ +static rt_err_t rt_usbh_adk_disable(void* arg) +{ + uadk_t adk; + struct uintf* intf = (struct uintf*)arg; + + RT_ASSERT(intf != RT_NULL); + + LOG_D("rt_usbh_adk_stop"); + + adk = (uadk_t)intf->user_data; + if(adk == RT_NULL) + { + rt_free(intf); + return RT_EOK; + } + + if(adk->pipe_in != RT_NULL) + rt_usb_hcd_free_pipe(intf->device->hcd, adk->pipe_in); + + if(adk->pipe_out != RT_NULL) + rt_usb_hcd_free_pipe(intf->device->hcd, adk->pipe_out); + + /* unregister adk device */ + rt_device_unregister(&adk->device); + + /* free adk instance */ + if(adk != RT_NULL) + { + rt_free(adk); + } + + /* free interface instance */ + rt_free(intf); + + return RT_EOK; +} + +/** + * This function will register adk class driver to the usb class driver manager. + * and it should be invoked in the usb system initialization. + * + * @return the error code, RT_EOK on successfully. + */ +ucd_t rt_usbh_class_driver_adk(void) +{ + adk_driver.class_code = USB_CLASS_ADK; + + adk_driver.enable = rt_usbh_adk_enable; + adk_driver.disable = rt_usbh_adk_disable; + + return &adk_driver; +} + +#endif + diff --git a/components/legacy/usb/usbhost/class/adk.h b/components/legacy/usb/usbhost/class/adk.h new file mode 100644 index 0000000000..67c3392d7a --- /dev/null +++ b/components/legacy/usb/usbhost/class/adk.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2011-12-12 Yi Qiu first version + */ + +#ifndef __ADK_H__ +#define __ADK_H__ + +#include <rtthread.h> + +struct uadkinst +{ + upipe_t pipe_in; + upipe_t pipe_out; + + struct rt_device device; +}; +typedef struct uadkinst* uadk_t; + +#define USB_ACCESSORY_VENDOR_ID 0x18D1 +#define USB_ACCESSORY_PRODUCT_ID 0x2D00 +#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01 + +#define ACCESSORY_STRING_MANUFACTURER 0 +#define ACCESSORY_STRING_MODEL 1 +#define ACCESSORY_STRING_DESCRIPTION 2 +#define ACCESSORY_STRING_VERSION 3 +#define ACCESSORY_STRING_URI 4 +#define ACCESSORY_STRING_SERIAL 5 + +#define USB_REQ_GET_PROTOCOL 51 +#define USB_REQ_SEND_STRING 52 +#define USB_REQ_START 53 + +#define USB_CLASS_ADK 0xff + +#endif + diff --git a/components/legacy/usb/usbhost/class/hid.c b/components/legacy/usb/usbhost/class/hid.c new file mode 100644 index 0000000000..5911faf1aa --- /dev/null +++ b/components/legacy/usb/usbhost/class/hid.c @@ -0,0 +1,410 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2011-12-12 Yi Qiu first version + * 2021-02-23 Leslie Lee update with current usb api + */ + +#include <rtthread.h> +#include <drivers/usb_host.h> +#include "hid.h" + +#ifdef RT_USBH_HID + +#define DBG_TAG "usbhost.hid" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + +static struct uclass_driver hid_driver; +static rt_list_t _protocal_list; + +/** + * This function will do USB_REQ_SET_IDLE request to set idle period to the usb hid device + * + * @param intf the interface instance. + * @duration the idle period of requesting data. + * @report_id the report id + * + * @return the error code, RT_EOK on successfully. +*/ +rt_err_t rt_usbh_hid_set_idle(struct uhintf* intf, int duration, int report_id) +{ + struct urequest setup; + struct uinstance* device; + int timeout = USB_TIMEOUT_BASIC; + + /* parameter check */ + RT_ASSERT(intf != RT_NULL); + RT_ASSERT(intf->device != RT_NULL); + + device = intf->device; + + setup.request_type = USB_REQ_TYPE_DIR_OUT | USB_REQ_TYPE_CLASS | + USB_REQ_TYPE_INTERFACE; + setup.bRequest = USB_REQ_SET_IDLE; + setup.wIndex = 0; + setup.wLength = 0; + setup.wValue = (duration << 8 )| report_id; + + if (rt_usb_hcd_setup_xfer(device->hcd, device->pipe_ep0_out, &setup, timeout) == 8) + if (rt_usb_hcd_pipe_xfer(device->hcd, device->pipe_ep0_in, RT_NULL, 0, timeout) == 0) + return RT_EOK; + + return -RT_FALSE; +} + +/** + * This function will do USB_REQ_GET_REPORT request to get report from the usb hid device + * + * @param intf the interface instance. + * @buffer the data buffer to save usb report descriptor. + * @param nbytes the size of buffer + * + * @return the error code, RT_EOK on successfully. +*/ +rt_err_t rt_usbh_hid_get_report(struct uhintf* intf, rt_uint8_t type, + rt_uint8_t id, rt_uint8_t *buffer, rt_size_t size) +{ + struct urequest setup; + struct uinstance* device; + int timeout = USB_TIMEOUT_BASIC; + + /* parameter check */ + RT_ASSERT(intf != RT_NULL); + RT_ASSERT(intf->device != RT_NULL); + + device = intf->device; + + setup.request_type = USB_REQ_TYPE_DIR_IN | USB_REQ_TYPE_CLASS | + USB_REQ_TYPE_INTERFACE; + setup.bRequest = USB_REQ_GET_REPORT; + setup.wIndex = intf->intf_desc->bInterfaceNumber; + setup.wLength = size; + setup.wValue = (type << 8 ) + id; + + if (rt_usb_hcd_setup_xfer(device->hcd, device->pipe_ep0_out, &setup, timeout) == 8) + { + if (rt_usb_hcd_pipe_xfer(device->hcd, device->pipe_ep0_in, buffer, size, timeout) == size) + { + if (rt_usb_hcd_pipe_xfer(device->hcd, device->pipe_ep0_out, RT_NULL, 0, timeout) == 0) + { + return RT_EOK; + } + } + } + else + return -RT_FALSE; + return -RT_FALSE; +} + +/** + * This function will do USB_REQ_SET_REPORT request to set report to the usb hid device + * + * @param intf the interface instance. + * @buffer the data buffer to save usb report descriptor. + * @param nbytes the size of buffer + * + * @return the error code, RT_EOK on successfully. +*/ +rt_err_t rt_usbh_hid_set_report(struct uhintf* intf, rt_uint8_t *buffer, rt_size_t size) +{ + struct urequest setup; + struct uinstance* device; + int timeout = USB_TIMEOUT_BASIC; + + /* parameter check */ + RT_ASSERT(intf != RT_NULL); + RT_ASSERT(intf->device != RT_NULL); + + device = intf->device; + + setup.request_type = USB_REQ_TYPE_DIR_OUT | USB_REQ_TYPE_CLASS | + USB_REQ_TYPE_INTERFACE; + setup.bRequest = USB_REQ_SET_REPORT; + setup.wIndex = intf->intf_desc->bInterfaceNumber; + setup.wLength = size; + setup.wValue = 0x02 << 8; + + if (rt_usb_hcd_setup_xfer(device->hcd, device->pipe_ep0_out, &setup, timeout) == 8) + return RT_EOK; + else + return -RT_FALSE; +} + +/** + * This function will do USB_REQ_SET_PROTOCOL request to set protocal to the usb hid device. + * + * @param intf the interface instance. + * @param protocol the protocol id. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_hid_set_protocal(struct uhintf* intf, int protocol) +{ + struct urequest setup; + struct uinstance* device; + int timeout = USB_TIMEOUT_BASIC; + + /* parameter check */ + RT_ASSERT(intf != RT_NULL); + RT_ASSERT(intf->device != RT_NULL); + + device = intf->device; + + setup.request_type = USB_REQ_TYPE_DIR_OUT | USB_REQ_TYPE_CLASS | + USB_REQ_TYPE_INTERFACE; + setup.bRequest = USB_REQ_SET_PROTOCOL; + setup.wIndex = 0; + setup.wLength = 0; + setup.wValue = protocol; + + if (rt_usb_hcd_setup_xfer(device->hcd, device->pipe_ep0_out, &setup, timeout) == 8) + return RT_EOK; + else + return -RT_FALSE; +} + +/** + * This function will do USB_REQ_GET_DESCRIPTOR request for the device instance + * to set feature of the hub port. + * + * @param intf the interface instance. + * @buffer the data buffer to save usb report descriptor. + * @param nbytes the size of buffer + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_hid_get_report_descriptor(struct uhintf* intf, + rt_uint8_t *buffer, rt_size_t size) +{ + struct urequest setup; + struct uinstance* device; + int timeout = USB_TIMEOUT_BASIC; + + /* parameter check */ + RT_ASSERT(intf != RT_NULL); + RT_ASSERT(intf->device != RT_NULL); + + device = intf->device; + + setup.request_type = USB_REQ_TYPE_DIR_IN | USB_REQ_TYPE_STANDARD| + USB_REQ_TYPE_INTERFACE; + setup.bRequest = USB_REQ_GET_DESCRIPTOR; + setup.wIndex = 0; + setup.wLength = size; + setup.wValue = USB_DESC_TYPE_REPORT << 8; + + if (rt_usb_hcd_setup_xfer(device->hcd, device->pipe_ep0_out, &setup, timeout) == 8) + { + if (rt_usb_hcd_pipe_xfer(device->hcd, device->pipe_ep0_in, buffer, size, timeout) == size) + { + if (rt_usb_hcd_pipe_xfer(device->hcd, device->pipe_ep0_out, RT_NULL, 0, timeout) == 0) + { + return RT_EOK; + } + } + } + else + return -RT_FALSE; + return -RT_FALSE; +} + +/** + * This function will register specified hid protocal to protocal list + * + * @param protocal the specified protocal. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_hid_protocal_register(uprotocal_t protocal) +{ + RT_ASSERT(protocal != RT_NULL); + + if (protocal == RT_NULL) return -RT_ERROR; + + /* insert class driver into driver list */ + rt_list_insert_after(&_protocal_list, &(protocal->list)); + + return RT_EOK; +} + +/** + * This function is the callback function of hid's int endpoint, it is invoked when data comes. + * + * @param context the context of the callback function. + * + * @return none. + */ +static void rt_usbh_hid_callback(void* context) +{ + upipe_t pipe; + struct uhid* hid; + int timeout = USB_TIMEOUT_LONG; + + /* parameter check */ + RT_ASSERT(context != RT_NULL); + + pipe = (upipe_t)context; + hid = (struct uhid*)((struct uhintf*)pipe->inst)->user_data; + + /* invoke protocal callback function */ + hid->protocal->callback((void*)hid); + + /* parameter check */ + RT_ASSERT(((struct uhintf*)pipe->inst)->device->hcd != RT_NULL); + + rt_usb_hcd_pipe_xfer(((struct uhintf*)pipe->inst)->device->hcd, pipe, + hid->buffer, pipe->ep.wMaxPacketSize, timeout); +} + +/** + * This function will find specified hid protocal from protocal list + * + * @param pro_id the protocal id. + * + * @return the found protocal or RT_NULL if there is no this protocal. + */ +static uprotocal_t rt_usbh_hid_protocal_find(int pro_id) +{ + struct rt_list_node *node; + + /* try to find protocal object */ + for (node = _protocal_list.next; node != &_protocal_list; node = node->next) + { + uprotocal_t protocal = + (uprotocal_t)rt_list_entry(node, struct uprotocal, list); + if (protocal->pro_id == pro_id) return protocal; + } + + /* not found */ + return RT_NULL; +} + +/** + * This function will run hid class driver when usb device is detected and identified + * as a hid class device, it will continue the enumulate process. + * + * @param arg the argument. + * + * @return the error code, RT_EOK on successfully. + */ +static rt_err_t rt_usbh_hid_enable(void* arg) +{ + int i = 0, pro_id; + uprotocal_t protocal; + struct uhid* hid; + struct uhintf* intf = (struct uhintf*)arg; + + /* parameter check */ + if(intf == RT_NULL) + { + rt_kprintf("the interface is not available\n"); + return -RT_EIO; + } + + pro_id = intf->intf_desc->bInterfaceProtocol; + + LOG_D("HID device enable, protocal id %d", pro_id); + + protocal = rt_usbh_hid_protocal_find(pro_id); + if(protocal == RT_NULL) + { + rt_kprintf("can't find hid protocal %d\n", pro_id); + intf->user_data = RT_NULL; + return -RT_ERROR; + } + + hid = rt_malloc(sizeof(struct uhid)); + RT_ASSERT(hid != RT_NULL); + + /* initilize the data structure */ + rt_memset(hid, 0, sizeof(struct uhid)); + intf->user_data = (void*)hid; + hid->protocal = protocal; + + for(i=0; i<intf->intf_desc->bNumEndpoints; i++) + { + rt_err_t ret; + uep_desc_t ep_desc; + + /* get endpoint descriptor */ + rt_usbh_get_endpoint_descriptor(intf->intf_desc, i, &ep_desc); + if(ep_desc == RT_NULL) + { + rt_kprintf("rt_usbh_get_endpoint_descriptor error\n"); + return -RT_ERROR; + } + + if(USB_EP_ATTR(ep_desc->bmAttributes) != USB_EP_ATTR_INT) + continue; + + if(!(ep_desc->bEndpointAddress & USB_DIR_IN)) continue; + + ret = rt_usb_hcd_alloc_pipe(intf->device->hcd, &hid->pipe_in, + intf->device, ep_desc); + if(ret != RT_EOK) return ret; + } + + /* initialize hid protocal */ + hid->protocal->init((void*)intf); + + return RT_EOK; +} + +/** + * This function will be invoked when usb device plug out is detected and it would clean + * and release all hub class related resources. + * + * @param arg the argument. + * + * @return the error code, RT_EOK on successfully. + */ +static rt_err_t rt_usbh_hid_disable(void* arg) +{ + struct uhid* hid; + struct uhintf* intf = (struct uhintf*)arg; + + RT_ASSERT(intf != RT_NULL); + + LOG_D("rt_usbh_hid_disable"); + + hid = (struct uhid*)intf->user_data; + if(hid != RT_NULL) + { + if(hid->pipe_in != RT_NULL) + { + /* free the HID in pipe */ + rt_usb_hcd_free_pipe(intf->device->hcd, hid->pipe_in); + } + + /* free the hid instance */ + rt_free(hid); + } + + return RT_EOK; +} + +/** + * This function will register hid class driver to the usb class driver manager. + * and it should be invoked in the usb system initialization. + * + * @return the error code, RT_EOK on successfully. + */ +ucd_t rt_usbh_class_driver_hid(void) +{ + rt_list_init(&_protocal_list); + + hid_driver.class_code = USB_CLASS_HID; + + hid_driver.enable = rt_usbh_hid_enable; + hid_driver.disable = rt_usbh_hid_disable; + + return &hid_driver; +} + +#endif + diff --git a/components/legacy/usb/usbhost/class/hid.h b/components/legacy/usb/usbhost/class/hid.h new file mode 100644 index 0000000000..c7e88b66b8 --- /dev/null +++ b/components/legacy/usb/usbhost/class/hid.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2011-12-12 Yi Qiu first version + */ + +#ifndef __HID_H__ +#define __HID_H__ + +#include <rtthread.h> + +struct uhid +{ + upipe_t pipe_in; + rt_uint8_t buffer[8]; + uprotocal_t protocal; +}; +typedef struct uhid uhid_t; + +#define USB_REQ_GET_REPORT 0x01 +#define USB_REQ_GET_IDLE 0x02 +#define USB_REQ_GET_PROTOCOL 0x03 +#define USB_REQ_SET_REPORT 0x09 +#define USB_REQ_SET_IDLE 0x0a +#define USB_REQ_SET_PROTOCOL 0x0b + +#define USB_HID_KEYBOARD 1 +#define USB_HID_MOUSE 2 + +rt_err_t rt_usbh_hid_set_idle(struct uhintf* intf, int duration, int report_id); +rt_err_t rt_usbh_hid_get_report(struct uhintf* intf, rt_uint8_t type, rt_uint8_t id, rt_uint8_t *buffer, rt_size_t size); +rt_err_t rt_usbh_hid_set_report(struct uhintf* intf, rt_uint8_t *buffer, rt_size_t size); +rt_err_t rt_usbh_hid_set_protocal(struct uhintf* intf, int protocol); +rt_err_t rt_usbh_hid_get_report_descriptor(struct uhintf* intf, rt_uint8_t *buffer, rt_size_t size); +rt_err_t rt_usbh_hid_protocal_register(uprotocal_t protocal); + +#endif diff --git a/components/legacy/usb/usbhost/class/mass.c b/components/legacy/usb/usbhost/class/mass.c new file mode 100644 index 0000000000..c29cd30ee4 --- /dev/null +++ b/components/legacy/usb/usbhost/class/mass.c @@ -0,0 +1,647 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2011-12-12 Yi Qiu first version + */ + +#include <rtthread.h> +#include <drivers/usb_host.h> +#include "mass.h" + +#ifdef RT_USBH_MSTORAGE + +#define DBG_TAG "usbhost.mass" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + +extern rt_err_t rt_udisk_run(struct uhintf* intf); +extern rt_err_t rt_udisk_stop(struct uhintf* intf); + +static struct uclass_driver storage_driver; + +/** + * This function will do USBREQ_GET_MAX_LUN request for the usb interface instance. + * + * @param intf the interface instance. + * @param max_lun the buffer to save max_lun. + * + * @return the error code, RT_EOK on successfully. + */ +static rt_err_t _pipe_check(struct uhintf* intf, upipe_t pipe) +{ + struct uinstance* device; + rt_err_t ret; + ustor_t stor; + int size = 0; + struct ustorage_csw csw; + + if(intf == RT_NULL || pipe == RT_NULL) + { + rt_kprintf("the interface is not available\n"); + return -RT_EIO; + } + + /* get usb device instance from the interface instance */ + device = intf->device; + + /* get storage instance from the interface instance */ + stor = (ustor_t)intf->user_data; + + /* check pipe status */ + if(pipe->status == UPIPE_STATUS_OK) return RT_EOK; + + if(pipe->status == UPIPE_STATUS_ERROR) + { + rt_kprintf("pipe status error\n"); + return -RT_EIO; + } + if(pipe->status == UPIPE_STATUS_STALL) + { + /* clear the pipe stall status */ + ret = rt_usbh_clear_feature(device, pipe->ep.bEndpointAddress, + USB_FEATURE_ENDPOINT_HALT); + if(ret != RT_EOK) return ret; + } + + + rt_thread_delay(50); + + rt_kprintf("pipes1 0x%x, 0x%x\n", stor->pipe_in, stor->pipe_out); + + stor->pipe_in->status = UPIPE_STATUS_OK; + + LOG_D("clean storage in pipe stall"); + + /* it should receive csw after clear the stall feature */ + size = rt_usb_hcd_pipe_xfer(stor->pipe_in->inst->hcd, + stor->pipe_in, &csw, SIZEOF_CSW, 100); + if(size != SIZEOF_CSW) + { + rt_kprintf("receive the csw after stall failed\n"); + return -RT_EIO; + } + + return -RT_ERROR; +} + +/** + * This function will do USBREQ_GET_MAX_LUN request for the usb interface instance. + * + * @param intf the interface instance. + * @param max_lun the buffer to save max_lun. + * + * @return the error code, RT_EOK on successfully. + */ +static rt_err_t rt_usb_bulk_only_xfer(struct uhintf* intf, + ustorage_cbw_t cmd, rt_uint8_t* buffer, int timeout) +{ + rt_size_t size; + rt_err_t ret; + upipe_t pipe; + struct ustorage_csw csw; + ustor_t stor; + + RT_ASSERT(cmd != RT_NULL); + + if(intf == RT_NULL) + { + rt_kprintf("the interface is not available\n"); + return -RT_EIO; + } + + /* get storage instance from the interface instance */ + stor = (ustor_t)intf->user_data; + + do + { + /* send the cbw */ + size = rt_usb_hcd_pipe_xfer(stor->pipe_out->inst->hcd, stor->pipe_out, + cmd, SIZEOF_CBW, timeout); + if(size != SIZEOF_CBW) + { + rt_kprintf("CBW size error\n"); + return -RT_EIO; + } + if(cmd->xfer_len != 0) + { + pipe = (cmd->dflags == CBWFLAGS_DIR_IN) ? stor->pipe_in : + stor->pipe_out; + size = rt_usb_hcd_pipe_xfer(pipe->inst->hcd, pipe, (void*)buffer, + cmd->xfer_len, timeout); + if(size != cmd->xfer_len) + { + rt_kprintf("request size %d, transfer size %d\n", + cmd->xfer_len, size); + break; + } + } + + /* receive the csw */ + size = rt_usb_hcd_pipe_xfer(stor->pipe_in->inst->hcd, stor->pipe_in, + &csw, SIZEOF_CSW, timeout); + if(size != SIZEOF_CSW) + { + rt_kprintf("csw size error\n"); + return -RT_EIO; + } + }while(0); + + /* check in pipes status */ + ret = _pipe_check(intf, stor->pipe_in); + if(ret != RT_EOK) + { + rt_kprintf("in pipe error\n"); + return ret; + } + + /* check out pipes status */ + ret = _pipe_check(intf, stor->pipe_out); + if(ret != RT_EOK) + { + rt_kprintf("out pipe error\n"); + return ret; + } + + /* check csw status */ + if(csw.signature != CSW_SIGNATURE || csw.tag != CBW_TAG_VALUE) + { + rt_kprintf("csw signature error\n"); + return -RT_EIO; + } + + if(csw.status != 0) + { + //rt_kprintf("csw status error:%d\n",csw.status); + return -RT_ERROR; + } + + return RT_EOK; +} + +/** + * This function will do USBREQ_GET_MAX_LUN request for the usb interface instance. + * + * @param intf the interface instance. + * @param max_lun the buffer to save max_lun. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_storage_get_max_lun(struct uhintf* intf, rt_uint8_t* max_lun) +{ + struct uinstance* device; + struct urequest setup; + int timeout = USB_TIMEOUT_BASIC; + + if(intf == RT_NULL) + { + rt_kprintf("the interface is not available\n"); + return -RT_EIO; + } + + /* parameter check */ + RT_ASSERT(intf->device != RT_NULL); + LOG_D("rt_usbh_storage_get_max_lun"); + + /* get usb device instance from the interface instance */ + device = intf->device; + + /* construct the request */ + setup.request_type = USB_REQ_TYPE_DIR_IN | USB_REQ_TYPE_CLASS | + USB_REQ_TYPE_INTERFACE; + setup.bRequest = USBREQ_GET_MAX_LUN; + setup.wValue = intf->intf_desc->bInterfaceNumber; + setup.wIndex = 0; + setup.wLength = 1; + + /* do control transfer request */ + if(rt_usb_hcd_setup_xfer(device->hcd, device->pipe_ep0_out, &setup, timeout) != 8) + { + return -RT_EIO; + } + if(rt_usb_hcd_pipe_xfer(device->hcd, device->pipe_ep0_in, max_lun, 1, timeout) != 1) + { + return -RT_EIO; + } + if(rt_usb_hcd_pipe_xfer(device->hcd, device->pipe_ep0_out, RT_NULL, 0, timeout) != 0) + { + return -RT_EIO; + } + return RT_EOK; +} + +/** + * This function will do USBREQ_MASS_STORAGE_RESET request for the usb interface instance. + * + * @param intf the interface instance. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_storage_reset(struct uhintf* intf) +{ + struct urequest setup; + struct uinstance* device; + int timeout = USB_TIMEOUT_BASIC; + + /* parameter check */ + if(intf == RT_NULL) + { + rt_kprintf("the interface is not available\n"); + return -RT_EIO; + } + + RT_ASSERT(intf->device != RT_NULL); + LOG_D("rt_usbh_storage_reset"); + + /* get usb device instance from the interface instance */ + device = intf->device; + + /* construct the request */ + setup.request_type = USB_REQ_TYPE_DIR_OUT | USB_REQ_TYPE_CLASS | + USB_REQ_TYPE_INTERFACE; + setup.bRequest = USBREQ_MASS_STORAGE_RESET; + setup.wIndex = intf->intf_desc->bInterfaceNumber; + setup.wLength = 0; + setup.wValue = 0; + + if(rt_usb_hcd_setup_xfer(device->hcd, device->pipe_ep0_out, &setup, timeout) != 8) + { + return -RT_EIO; + } + if(rt_usb_hcd_pipe_xfer(device->hcd, device->pipe_ep0_in, RT_NULL, 0, timeout) != 0) + { + return -RT_EIO; + } + return RT_EOK; +} + +/** + * This function will execute SCSI_READ_10 command to read data from the usb device. + * + * @param intf the interface instance. + * @param buffer the data buffer to save read data + * @param sector the start sector address to read. + * @param sector the sector count to read. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_storage_read10(struct uhintf* intf, rt_uint8_t *buffer, + rt_uint32_t sector, rt_size_t count, int timeout) +{ + struct ustorage_cbw cmd; + + /* parameter check */ + if(intf == RT_NULL) + { + rt_kprintf("interface is not available\n"); + return -RT_EIO; + } + + RT_ASSERT(intf->device != RT_NULL); + LOG_D("rt_usbh_storage_read10"); + + /* construct the command block wrapper */ + rt_memset(&cmd, 0, sizeof(struct ustorage_cbw)); + cmd.signature = CBW_SIGNATURE; + cmd.tag = CBW_TAG_VALUE; + cmd.xfer_len = SECTOR_SIZE * count; + cmd.dflags = CBWFLAGS_DIR_IN; + cmd.lun = 0; + cmd.cb_len = 10; + cmd.cb[0] = SCSI_READ_10; + cmd.cb[1] = 0; + cmd.cb[2] = (rt_uint8_t)(sector >> 24); + cmd.cb[3] = (rt_uint8_t)(sector >> 16); + cmd.cb[4] = (rt_uint8_t)(sector >> 8); + cmd.cb[5] = (rt_uint8_t)sector; + cmd.cb[6] = 0; + cmd.cb[7] = (count & 0xff00) >> 8; + cmd.cb[8] = (rt_uint8_t) count & 0xff; + + return rt_usb_bulk_only_xfer(intf, &cmd, buffer, timeout); +} + +/** + * This function will execute SCSI_WRITE_10 command to write data to the usb device. + * + * @param intf the interface instance. + * @param buffer the data buffer to save write data + * @param sector the start sector address to write. + * @param sector the sector count to write. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_storage_write10(struct uhintf* intf, rt_uint8_t *buffer, + rt_uint32_t sector, rt_size_t count, int timeout) +{ + struct ustorage_cbw cmd; + + /* parameter check */ + if(intf == RT_NULL) + { + rt_kprintf("the interface is not available\n"); + return -RT_EIO; + } + + RT_ASSERT(intf->device != RT_NULL); + LOG_D("rt_usbh_storage_write10"); + + /* construct the command block wrapper */ + rt_memset(&cmd, 0, sizeof(struct ustorage_cbw)); + cmd.signature = CBW_SIGNATURE; + cmd.tag = CBW_TAG_VALUE; + cmd.xfer_len = SECTOR_SIZE * count; + cmd.dflags = CBWFLAGS_DIR_OUT; + cmd.lun = 0; + cmd.cb_len = 10; + cmd.cb[0] = SCSI_WRITE_10; + cmd.cb[1] = 0; + cmd.cb[2] = (rt_uint8_t)(sector >> 24); + cmd.cb[3] = (rt_uint8_t)(sector >> 16); + cmd.cb[4] = (rt_uint8_t)(sector >> 8); + cmd.cb[5] = (rt_uint8_t)sector; + cmd.cb[6] = 0; + cmd.cb[7] = (count & 0xff00) >> 8; + cmd.cb[8] = (rt_uint8_t) count & 0xff; + + return rt_usb_bulk_only_xfer(intf, &cmd, buffer, timeout); +} + +/** + * This function will execute SCSI_REQUEST_SENSE command to get sense data. + * + * @param intf the interface instance. + * @param buffer the data buffer to save sense data + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_storage_request_sense(struct uhintf* intf, rt_uint8_t* buffer) +{ + struct ustorage_cbw cmd; + int timeout = USB_TIMEOUT_LONG; + + /* parameter check */ + if(intf == RT_NULL) + { + rt_kprintf("the interface is not available\n"); + return -RT_EIO; + } + + RT_ASSERT(intf->device != RT_NULL); + LOG_D("rt_usbh_storage_request_sense"); + + /* construct the command block wrapper */ + rt_memset(&cmd, 0, sizeof(struct ustorage_cbw)); + cmd.signature = CBW_SIGNATURE; + cmd.tag = CBW_TAG_VALUE; + cmd.xfer_len = 18; + cmd.dflags = CBWFLAGS_DIR_IN; + cmd.lun = 0; + cmd.cb_len = 6; + cmd.cb[0] = SCSI_REQUEST_SENSE; + cmd.cb[4] = 18; + + return rt_usb_bulk_only_xfer(intf, &cmd, buffer, timeout); +} + +/** + * This function will execute SCSI_TEST_UNIT_READY command to get unit ready status. + * + * @param intf the interface instance. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_storage_test_unit_ready(struct uhintf* intf) +{ + struct ustorage_cbw cmd; + int timeout = USB_TIMEOUT_LONG; + + /* parameter check */ + if(intf == RT_NULL) + { + rt_kprintf("the interface is not available\n"); + return -RT_EIO; + } + + RT_ASSERT(intf->device != RT_NULL); + LOG_D("rt_usbh_storage_test_unit_ready"); + + /* construct the command block wrapper */ + rt_memset(&cmd, 0, sizeof(struct ustorage_cbw)); + cmd.signature = CBW_SIGNATURE; + cmd.tag = CBW_TAG_VALUE; + cmd.xfer_len = 0; + cmd.dflags = CBWFLAGS_DIR_OUT; + cmd.lun = 0; + cmd.cb_len = 12; + cmd.cb[0] = SCSI_TEST_UNIT_READY; + + return rt_usb_bulk_only_xfer(intf, &cmd, RT_NULL, timeout); +} + +/** + * This function will execute SCSI_INQUIRY_CMD command to get inquiry data. + * + * @param intf the interface instance. + * @param buffer the data buffer to save inquiry data + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_storage_inquiry(struct uhintf* intf, rt_uint8_t* buffer) +{ + struct ustorage_cbw cmd; + int timeout = USB_TIMEOUT_LONG; + + /* parameter check */ + if(intf == RT_NULL) + { + rt_kprintf("the interface is not available\n"); + return -RT_EIO; + } + + RT_ASSERT(intf->device != RT_NULL); + LOG_D("rt_usbh_storage_inquiry"); + + /* construct the command block wrapper */ + rt_memset(&cmd, 0, sizeof(struct ustorage_cbw)); + cmd.signature = CBW_SIGNATURE; + cmd.tag = CBW_TAG_VALUE; + cmd.xfer_len = 36; + cmd.dflags = CBWFLAGS_DIR_IN; + cmd.lun = 0; + cmd.cb_len = 6;//12 + cmd.cb[0] = SCSI_INQUIRY_CMD; + cmd.cb[4] = 36; + + return rt_usb_bulk_only_xfer(intf, &cmd, buffer, timeout); +} + +/** + * This function will execute SCSI_READ_CAPACITY command to get capacity data. + * + * @param intf the interface instance. + * @param buffer the data buffer to save capacity data + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_storage_get_capacity(struct uhintf* intf, rt_uint8_t* buffer) +{ + struct ustorage_cbw cmd; + int timeout = USB_TIMEOUT_LONG; + + /* parameter check */ + if(intf == RT_NULL) + { + rt_kprintf("the interface is not available\n"); + return -RT_EIO; + } + + RT_ASSERT(intf->device != RT_NULL); + LOG_D("rt_usbh_storage_get_capacity"); + + /* construct the command block wrapper */ + rt_memset(&cmd, 0, sizeof(struct ustorage_cbw)); + cmd.signature = CBW_SIGNATURE; + cmd.tag = CBW_TAG_VALUE; + cmd.xfer_len = 8; + cmd.dflags = CBWFLAGS_DIR_IN; + cmd.lun = 0; + cmd.cb_len = 12; + cmd.cb[0] = SCSI_READ_CAPACITY; + + return rt_usb_bulk_only_xfer(intf, &cmd, buffer, timeout); +} + +/** + * This function will run mass storage class driver when usb device is detected + * and identified as a mass storage class device, it will continue to do the enumulate + * process. + * + * @param arg the argument. + * + * @return the error code, RT_EOK on successfully. + */ +static rt_err_t rt_usbh_storage_enable(void* arg) +{ + int i = 0; + rt_err_t ret; + ustor_t stor; + struct uhintf* intf = (struct uhintf*)arg; + + /* parameter check */ + if(intf == RT_NULL) + { + rt_kprintf("the interface is not available\n"); + return -RT_EIO; + } + + LOG_D("subclass %d, protocal %d", + intf->intf_desc->bInterfaceSubClass, + intf->intf_desc->bInterfaceProtocol); + + LOG_D("rt_usbh_storage_run"); + + /* only support SCSI subclass and bulk only protocal */ + + stor = rt_malloc(sizeof(struct ustor)); + RT_ASSERT(stor != RT_NULL); + + /* initilize the data structure */ + rt_memset(stor, 0, sizeof(struct ustor)); + intf->user_data = (void*)stor; + + for(i=0; i<intf->intf_desc->bNumEndpoints; i++) + { + uep_desc_t ep_desc; + + /* get endpoint descriptor from interface descriptor */ + rt_usbh_get_endpoint_descriptor(intf->intf_desc, i, &ep_desc); + if(ep_desc == RT_NULL) + { + rt_kprintf("rt_usb_get_endpoint_descriptor error\n"); + return -RT_ERROR; + } + + /* the endpoint type of mass storage class should be BULK */ + if((ep_desc->bmAttributes & USB_EP_ATTR_TYPE_MASK) != USB_EP_ATTR_BULK) + continue; + + /* allocate pipes according to the endpoint type */ + if(ep_desc->bEndpointAddress & USB_DIR_IN) + { + /* alloc an in pipe for the storage instance */ + stor->pipe_in = rt_usb_instance_find_pipe(intf->device,ep_desc->bEndpointAddress); + } + else + { + /* alloc an output pipe for the storage instance */ + stor->pipe_out = rt_usb_instance_find_pipe(intf->device,ep_desc->bEndpointAddress); + } + } + + /* check pipes infomation */ + if(stor->pipe_in == RT_NULL || stor->pipe_out == RT_NULL) + { + rt_kprintf("pipe error, unsupported device\n"); + return -RT_ERROR; + } + + /* should implement as callback */ + ret = rt_udisk_run(intf); + if(ret != RT_EOK) return ret; + + return RT_EOK; +} + +/** + * This function will be invoked when usb device plug out is detected and it would clean + * and release all mass storage class related resources. + * + * @param arg the argument. + * + * @return the error code, RT_EOK on successfully. + */ +static rt_err_t rt_usbh_storage_disable(void* arg) +{ + ustor_t stor; + struct uhintf* intf = (struct uhintf*)arg; + + /* parameter check */ + RT_ASSERT(intf != RT_NULL); + RT_ASSERT(intf->user_data != RT_NULL); + RT_ASSERT(intf->device != RT_NULL); + + LOG_D("rt_usbh_storage_stop"); + + /* get storage instance from interface instance */ + stor = (ustor_t)intf->user_data; + + rt_udisk_stop(intf); + + + /* free storage instance */ + if(stor != RT_NULL) rt_free(stor); + return RT_EOK; +} + +/** + * This function will register mass storage class driver to the usb class driver manager. + * and it should be invoked in the usb system initialization. + * + * @return the error code, RT_EOK on successfully. + */ +ucd_t rt_usbh_class_driver_storage(void) +{ + storage_driver.class_code = USB_CLASS_MASS_STORAGE; + + storage_driver.enable = rt_usbh_storage_enable; + storage_driver.disable = rt_usbh_storage_disable; + + return &storage_driver; +} + +#endif + diff --git a/components/legacy/usb/usbhost/class/mass.h b/components/legacy/usb/usbhost/class/mass.h new file mode 100644 index 0000000000..6d54dd66a7 --- /dev/null +++ b/components/legacy/usb/usbhost/class/mass.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2011-12-12 Yi Qiu first version + */ + +#ifndef __MASS_H__ +#define __MASS_H__ + +#include <rtthread.h> +#include "dfs_fs.h" + +#define MAX_PARTITION_COUNT 4 +#define SECTOR_SIZE 512 + +struct ustor_data +{ + struct dfs_partition part; + struct uhintf* intf; + int udisk_id; + const char path; +}; + +struct ustor +{ + upipe_t pipe_in; + upipe_t pipe_out; + rt_uint32_t capicity[2]; + + struct rt_device dev[MAX_PARTITION_COUNT]; + rt_uint8_t dev_cnt; +}; +typedef struct ustor* ustor_t; + +rt_err_t rt_usbh_storage_get_max_lun(struct uhintf* intf, rt_uint8_t* max_lun); +rt_err_t rt_usbh_storage_reset(struct uhintf* intf); +rt_err_t rt_usbh_storage_read10(struct uhintf* intf, rt_uint8_t *buffer, + rt_uint32_t sector, rt_size_t count, int timeout); +rt_err_t rt_usbh_storage_write10(struct uhintf* intf, rt_uint8_t *buffer, + rt_uint32_t sector, rt_size_t count, int timeout); +rt_err_t rt_usbh_storage_request_sense(struct uhintf* intf, rt_uint8_t* buffer); +rt_err_t rt_usbh_storage_test_unit_ready(struct uhintf* intf); +rt_err_t rt_usbh_storage_inquiry(struct uhintf* intf, rt_uint8_t* buffer); +rt_err_t rt_usbh_storage_get_capacity(struct uhintf* intf, rt_uint8_t* buffer); + +#endif diff --git a/components/legacy/usb/usbhost/class/udisk.c b/components/legacy/usb/usbhost/class/udisk.c new file mode 100644 index 0000000000..07815e5996 --- /dev/null +++ b/components/legacy/usb/usbhost/class/udisk.c @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2011-12-12 Yi Qiu first version + */ + +#include <rtthread.h> +#include <dfs_fs.h> +#include <drivers/usb_host.h> +#include "mass.h" + +#define DBG_TAG "usbhost.udisk" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + +#ifdef RT_USBH_MSTORAGE + +#define UDISK_MAX_COUNT 8 +static rt_uint8_t _udisk_idset = 0; + +static int udisk_get_id(void) +{ + int i; + + for(i=0; i< UDISK_MAX_COUNT; i++) + { + if((_udisk_idset & (1 << i)) != 0) continue; + else break; + } + + /* it should not happen */ + if(i == UDISK_MAX_COUNT) RT_ASSERT(0); + + _udisk_idset |= (1 << i); + return i; +} + +static void udisk_free_id(int id) +{ + RT_ASSERT(id < UDISK_MAX_COUNT) + + _udisk_idset &= ~(1 << id); +} + +/** + * This function will initialize the udisk device + * + * @param dev the pointer of device driver structure + * + * @return RT_EOK + */ +static rt_err_t rt_udisk_init(rt_device_t dev) +{ + return RT_EOK; +} + +/** + * This function will read some data from a device. + * + * @param dev the pointer of device driver structure + * @param pos the position of reading + * @param buffer the data buffer to save read data + * @param size the size of buffer + * + * @return the actually read size on successful, otherwise negative returned. + */ +static rt_ssize_t rt_udisk_read(rt_device_t dev, rt_off_t pos, void* buffer, + rt_size_t size) +{ + rt_err_t ret; + struct uhintf* intf; + struct ustor_data* data; + int timeout = USB_TIMEOUT_LONG; + + /* check parameter */ + RT_ASSERT(dev != RT_NULL); + RT_ASSERT(buffer != RT_NULL); + + if(size > 4096) timeout *= 2; + + data = (struct ustor_data*)dev->user_data; + intf = data->intf; + + ret = rt_usbh_storage_read10(intf, (rt_uint8_t*)buffer, pos, size, timeout); + + if (ret != RT_EOK) + { + rt_kprintf("usb mass_storage read failed\n"); + return 0; + } + + return size; +} + +/** + * This function will write some data to a device. + * + * @param dev the pointer of device driver structure + * @param pos the position of written + * @param buffer the data buffer to be written to device + * @param size the size of buffer + * + * @return the actually written size on successful, otherwise negative returned. + */ +static rt_ssize_t rt_udisk_write (rt_device_t dev, rt_off_t pos, const void* buffer, + rt_size_t size) +{ + rt_err_t ret; + struct uhintf* intf; + struct ustor_data* data; + int timeout = USB_TIMEOUT_LONG; + + /* check parameter */ + RT_ASSERT(dev != RT_NULL); + RT_ASSERT(buffer != RT_NULL); + + if(size * SECTOR_SIZE > 4096) timeout *= 2; + + data = (struct ustor_data*)dev->user_data; + intf = data->intf; + + ret = rt_usbh_storage_write10(intf, (rt_uint8_t*)buffer, pos, size, timeout); + if (ret != RT_EOK) + { + rt_kprintf("usb mass_storage write %d sector failed\n", size); + return 0; + } + + return size; +} + +/** + * This function will execute SCSI_INQUIRY_CMD command to get inquiry data. + * + * @param intf the interface instance. + * @param buffer the data buffer to save inquiry data + * + * @return the error code, RT_EOK on successfully. + */ +static rt_err_t rt_udisk_control(rt_device_t dev, int cmd, void *args) +{ + ustor_t stor; + struct ustor_data* data; + + /* check parameter */ + RT_ASSERT(dev != RT_NULL); + + data = (struct ustor_data*)dev->user_data; + stor = (ustor_t)data->intf->user_data; + + if (cmd == RT_DEVICE_CTRL_BLK_GETGEOME) + { + struct rt_device_blk_geometry *geometry; + + geometry = (struct rt_device_blk_geometry *)args; + if (geometry == RT_NULL) return -RT_ERROR; + + geometry->bytes_per_sector = SECTOR_SIZE; + geometry->block_size = stor->capicity[1]; + geometry->sector_count = stor->capicity[0]; + } + + return RT_EOK; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops udisk_device_ops = +{ + rt_udisk_init, + RT_NULL, + RT_NULL, + rt_udisk_read, + rt_udisk_write, + rt_udisk_control +}; +#endif + +/** + * This function will run udisk driver when usb disk is detected. + * + * @param intf the usb interface instance. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_udisk_run(struct uhintf* intf) +{ + int i = 0; + rt_err_t ret; + char dname[8]; + char sname[8]; + rt_uint8_t max_lun, *sector, sense[18], inquiry[36]; + struct dfs_partition part[MAX_PARTITION_COUNT]; + ustor_t stor; + + /* check parameter */ + RT_ASSERT(intf != RT_NULL); + + /* set interface */ +// ret = rt_usbh_set_interface(intf->device, intf->intf_desc->bInterfaceNumber); +// if(ret != RT_EOK) +// rt_usbh_clear_feature(intf->device, 0, USB_FEATURE_ENDPOINT_HALT); + /* reset mass storage class device */ + ret = rt_usbh_storage_reset(intf); + if(ret != RT_EOK) return ret; + + stor = (ustor_t)intf->user_data; + + /* get max logic unit number */ + ret = rt_usbh_storage_get_max_lun(intf, &max_lun); + if(ret != RT_EOK) + rt_usbh_clear_feature(intf->device, 0, USB_FEATURE_ENDPOINT_HALT); + + /* reset pipe in endpoint */ + if(stor->pipe_in->status == UPIPE_STATUS_STALL) + { + ret = rt_usbh_clear_feature(intf->device, + stor->pipe_in->ep.bEndpointAddress, USB_FEATURE_ENDPOINT_HALT); + if(ret != RT_EOK) return ret; + } + + + /* reset pipe out endpoint */ + if(stor->pipe_out->status == UPIPE_STATUS_STALL) + { + ret = rt_usbh_clear_feature(intf->device, + stor->pipe_out->ep.bEndpointAddress, USB_FEATURE_ENDPOINT_HALT); + if(ret != RT_EOK) return ret; + } + + while((ret = rt_usbh_storage_inquiry(intf, inquiry)) != RT_EOK) + { + if(ret == -RT_EIO) return ret; + + rt_thread_delay(5); + if(i++ < 10) continue; + rt_kprintf("rt_usbh_storage_inquiry error\n"); + return -RT_ERROR; + } + + i = 0; + + /* wait device ready */ + while((ret = rt_usbh_storage_test_unit_ready(intf)) != RT_EOK) + { + if(ret == -RT_EIO) return ret; + + ret = rt_usbh_storage_request_sense(intf, sense); + if(ret == -RT_EIO) return ret; + + rt_thread_delay(10); + if(i++ < 10) continue; + + rt_kprintf("rt_usbh_storage_test_unit_ready error\n"); + return -RT_ERROR; + } + + i = 0; + rt_memset(stor->capicity, 0, sizeof(stor->capicity)); + + /* get storage capacity */ + while((ret = rt_usbh_storage_get_capacity(intf, + (rt_uint8_t*)stor->capicity)) != RT_EOK) + { + if(ret == -RT_EIO) return ret; + + rt_thread_delay(50); + if(i++ < 10) continue; + + stor->capicity[0] = 2880; + stor->capicity[1] = 0x200; + + rt_kprintf("rt_usbh_storage_get_capacity error\n"); + break; + } + + stor->capicity[0] = uswap_32(stor->capicity[0]); + stor->capicity[1] = uswap_32(stor->capicity[1]); + stor->capicity[0] += 1; + + LOG_D("capicity %d, block size %d", + stor->capicity[0], stor->capicity[1]); + + /* get the first sector to read partition table */ + sector = (rt_uint8_t*) rt_malloc (SECTOR_SIZE); + if (sector == RT_NULL) + { + rt_kprintf("allocate partition sector buffer failed\n"); + return -RT_ERROR; + } + + rt_memset(sector, 0, SECTOR_SIZE); + + LOG_D("read partition table"); + + /* get the partition table */ + ret = rt_usbh_storage_read10(intf, sector, 0, 1, USB_TIMEOUT_LONG); + if(ret != RT_EOK) + { + rt_kprintf("read parition table error\n"); + + rt_free(sector); + return -RT_ERROR; + } + + LOG_D("finished reading partition"); + + for(i=0; i<MAX_PARTITION_COUNT; i++) + { + /* get the first partition */ + ret = dfs_filesystem_get_partition(&part[i], sector, i); + if (ret == RT_EOK) + { + struct ustor_data* data = rt_malloc(sizeof(struct ustor_data)); + if (data == RT_NULL) + { + LOG_E("Allocate partition data buffer failed."); + continue; + } + rt_memset(data, 0, sizeof(struct ustor_data)); + data->intf = intf; + data->udisk_id = udisk_get_id(); + rt_snprintf(dname, 6, "ud%d-%d", data->udisk_id, i); + rt_snprintf(sname, 8, "sem_ud%d", i); + data->part.lock = rt_sem_create(sname, 1, RT_IPC_FLAG_FIFO); + + /* register sdcard device */ + stor->dev[i].type = RT_Device_Class_Block; +#ifdef RT_USING_DEVICE_OPS + stor->dev[i].ops = &udisk_device_ops; +#else + stor->dev[i].init = rt_udisk_init; + stor->dev[i].read = rt_udisk_read; + stor->dev[i].write = rt_udisk_write; + stor->dev[i].control = rt_udisk_control; +#endif + stor->dev[i].user_data = (void*)data; + + rt_device_register(&stor->dev[i], dname, RT_DEVICE_FLAG_RDWR | + RT_DEVICE_FLAG_REMOVABLE | RT_DEVICE_FLAG_STANDALONE); + + stor->dev_cnt++; + if (dfs_mount(stor->dev[i].parent.name, UDISK_MOUNTPOINT, "elm", + 0, 0) == 0) + { + LOG_D("udisk part %d mount successfully", i); + } + else + { + LOG_D("udisk part %d mount failed", i); + } + } + else + { + if(i == 0) + { + struct ustor_data* data = rt_malloc(sizeof(struct ustor_data)); + if (data == RT_NULL) + { + LOG_E("Allocate partition data buffer failed."); + break; + } + rt_memset(data, 0, sizeof(struct ustor_data)); + data->udisk_id = udisk_get_id(); + + /* there is no partition table */ + data->part.offset = 0; + data->part.size = 0; + data->intf = intf; + data->part.lock = rt_sem_create("sem_ud", 1, RT_IPC_FLAG_FIFO); + + rt_snprintf(dname, 7, "udisk%d", data->udisk_id); + + /* register sdcard device */ + stor->dev[0].type = RT_Device_Class_Block; +#ifdef RT_USING_DEVICE_OPS + stor->dev[i].ops = &udisk_device_ops; +#else + stor->dev[0].init = rt_udisk_init; + stor->dev[0].read = rt_udisk_read; + stor->dev[0].write = rt_udisk_write; + stor->dev[0].control = rt_udisk_control; +#endif + stor->dev[0].user_data = (void*)data; + + rt_device_register(&stor->dev[0], dname, + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE + | RT_DEVICE_FLAG_STANDALONE); + + stor->dev_cnt++; + if (dfs_mount(stor->dev[0].parent.name, UDISK_MOUNTPOINT, + "elm", 0, 0) == 0) + { + rt_kprintf("Mount FAT on Udisk successful.\n"); + } + else + { + rt_kprintf("Mount FAT on Udisk failed.\n"); + } + } + + break; + } + } + + rt_free(sector); + + return RT_EOK; +} + +/** + * This function will be invoked when usb disk plug out is detected and it would clean + * and release all udisk related resources. + * + * @param intf the usb interface instance. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_udisk_stop(struct uhintf* intf) +{ + int i; + ustor_t stor; + struct ustor_data* data; + + /* check parameter */ + RT_ASSERT(intf != RT_NULL); + RT_ASSERT(intf->device != RT_NULL); + + stor = (ustor_t)intf->user_data; + RT_ASSERT(stor != RT_NULL); + + for(i=0; i<stor->dev_cnt; i++) + { + rt_device_t dev = &stor->dev[i]; + data = (struct ustor_data*)dev->user_data; + + /* unmount filesystem */ + dfs_unmount(UDISK_MOUNTPOINT); + + /* delete semaphore */ + rt_sem_delete(data->part.lock); + udisk_free_id(data->udisk_id); + rt_free(data); + + /* unregister device */ + rt_device_unregister(&stor->dev[i]); + } + + return RT_EOK; +} + +#endif + diff --git a/components/legacy/usb/usbhost/class/ukbd.c b/components/legacy/usb/usbhost/class/ukbd.c new file mode 100644 index 0000000000..db080ed406 --- /dev/null +++ b/components/legacy/usb/usbhost/class/ukbd.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2012-01-03 Yi Qiu first version + */ + +#include <rtthread.h> +#include <drivers/usb_host.h> +#include "hid.h" + +#if defined(RT_USBH_HID) && defined(RT_USBH_HID_KEYBOARD) + +#define DBG_TAG "usbhost.ukbd" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + +static struct uprotocal kbd_protocal; + +static rt_err_t rt_usbh_hid_kbd_callback(void* arg) +{ + int int1, int2; + struct uhid* hid; + + hid = (struct uhid*)arg; + + int1 = *(rt_uint32_t*)hid->buffer; + int2 = *(rt_uint32_t*)(&hid->buffer[4]); + + if(int1 != 0 || int2 != 0) + { + LOG_D("key down 0x%x, 0x%x", int1, int2); + } + + return RT_EOK; +} + +static rt_thread_t kbd_thread; +static void kbd_task(void* param) +{ + struct uhintf* intf = (struct uhintf*)param; + while (1) + { + if (rt_usb_hcd_pipe_xfer(intf->device->hcd, ((struct uhid*)intf->user_data)->pipe_in, + ((struct uhid*)intf->user_data)->buffer, ((struct uhid*)intf->user_data)->pipe_in->ep.wMaxPacketSize, + USB_TIMEOUT_BASIC) == 0) + { + break; + } + + rt_usbh_hid_kbd_callback(intf->user_data); + } +} + + +static rt_err_t rt_usbh_hid_kbd_init(void* arg) +{ + struct uhintf* intf = (struct uhintf*)arg; + + RT_ASSERT(intf != RT_NULL); + + rt_usbh_hid_set_protocal(intf, 0); + + rt_usbh_hid_set_idle(intf, 10, 0); + + LOG_D("start usb keyboard"); + + kbd_thread = rt_thread_create("kbd0", kbd_task, intf, 1024, 8, 100); + rt_thread_startup(kbd_thread); + + return RT_EOK; +} + +/** + * This function will define the hid keyboard protocal, it will be register to the protocal list. + * + * @return the keyboard protocal structure. + */ +uprotocal_t rt_usbh_hid_protocal_kbd(void) +{ + kbd_protocal.pro_id = USB_HID_KEYBOARD; + + kbd_protocal.init = rt_usbh_hid_kbd_init; + kbd_protocal.callback = rt_usbh_hid_kbd_callback; + + return &kbd_protocal; +} + +#endif + diff --git a/components/legacy/usb/usbhost/class/umouse.c b/components/legacy/usb/usbhost/class/umouse.c new file mode 100644 index 0000000000..dfd6e9081c --- /dev/null +++ b/components/legacy/usb/usbhost/class/umouse.c @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2012-01-03 Yi Qiu first version + */ + +#include <rtthread.h> +#include <drivers/usb_host.h> +#include "hid.h" + +#ifdef RT_USING_RTGUI +#include <rtgui/event.h> +#include <rtgui/rtgui_server.h> +#include "drv_lcd.h" +#endif + +#if defined(RT_USBH_HID) && defined(RT_USBH_HID_MOUSE) + +#define DBG_TAG "usbhost.umouse" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + +static struct uprotocal mouse_protocal; + +#ifdef RT_USING_RTGUI +#define LKEY_PRESS 0x01 +#define RKEY_PRESS 0x02 +#define MKEY_PRESS 0x04 +#define MOUSE_SCALING 0x02 + +static rt_bool_t lkey_down=RT_FALSE; +//static rt_bool_t rkey_down=RT_FALSE; +//static rt_bool_t mkey_down=RT_FALSE; +static struct rtgui_event_mouse emouse; +#endif + +static rt_err_t rt_usbh_hid_mouse_callback(void* arg) +{ + struct uhid* hid; +#ifdef RT_USING_RTGUI + rt_uint16_t xoffset=0; + rt_uint16_t yoffset=0; +#endif + hid = (struct uhid*)arg; + + LOG_D("hid 0x%x 0x%x", + *(rt_uint32_t*)hid->buffer, + *(rt_uint32_t*)(&hid->buffer[4])); +#ifdef RT_USING_RTGUI + if(hid->buffer[1]!=0) + { + if(hid->buffer[1]>127) + { + xoffset=(256-hid->buffer[1])*MOUSE_SCALING; + if(emouse.x>xoffset) + { + emouse.x-=xoffset; + } + else + { + emouse.x=0; + } + } + else + { + xoffset=(hid->buffer[1])*MOUSE_SCALING; + if((emouse.x+xoffset)<480) + { + emouse.x+=xoffset; + } + else + { + emouse.x=480; + } + } + } + if(hid->buffer[2]!=0) + { + + if(hid->buffer[2]>127) + { + yoffset=(256-hid->buffer[2])*MOUSE_SCALING; + if(emouse.y>yoffset) + { + emouse.y-=yoffset; + } + else + { + emouse.y=0; + } + } + else + { + yoffset=hid->buffer[2]*MOUSE_SCALING; + if(emouse.y+yoffset<272) + { + emouse.y+=yoffset; + } + else + { + emouse.y=272; + } + } + } + if(xoffset!=0||yoffset!=0) + { + cursor_set_position(emouse.x,emouse.y); + } + if(hid->buffer[0]&LKEY_PRESS) + { + if(lkey_down==RT_FALSE) + { + // rt_kprintf("mouse left key press down\n"); + emouse.button = (RTGUI_MOUSE_BUTTON_LEFT | RTGUI_MOUSE_BUTTON_DOWN); + rtgui_server_post_event(&emouse.parent, sizeof(struct rtgui_event_mouse)); + lkey_down=RT_TRUE; + } + } + else if(lkey_down==RT_TRUE) + { + // rt_kprintf("mouse left key press up\n"); + emouse.button = (RTGUI_MOUSE_BUTTON_LEFT | RTGUI_MOUSE_BUTTON_UP); + rtgui_server_post_event(&emouse.parent, sizeof(struct rtgui_event_mouse)); + lkey_down=RT_FALSE; + } +#endif + return RT_EOK; +} + +static rt_thread_t mouse_thread; +static void mouse_task(void* param) +{ + struct uhintf* intf = (struct uhintf*)param; + while (1) + { + if (rt_usb_hcd_pipe_xfer(intf->device->hcd, ((struct uhid*)intf->user_data)->pipe_in, + ((struct uhid*)intf->user_data)->buffer, ((struct uhid*)intf->user_data)->pipe_in->ep.wMaxPacketSize, + USB_TIMEOUT_BASIC) == 0) + { + break; + } + + rt_usbh_hid_mouse_callback(intf->user_data); + } +} + + +static rt_err_t rt_usbh_hid_mouse_init(void* arg) +{ + struct uhintf* intf = (struct uhintf*)arg; + + RT_ASSERT(intf != RT_NULL); + + rt_usbh_hid_set_protocal(intf, 0); + + rt_usbh_hid_set_idle(intf, 0, 0); + + mouse_thread = rt_thread_create("mouse0", mouse_task, intf, 1024, 8, 100); + rt_thread_startup(mouse_thread); + + LOG_D("start usb mouse"); +#ifdef RT_USING_RTGUI + RTGUI_EVENT_MOUSE_BUTTON_INIT(&emouse); + emouse.wid = RT_NULL; + cursor_display(RT_TRUE); +#endif + return RT_EOK; +} + +/** + * This function will define the hid mouse protocal, it will be register to the protocal list. + * + * @return the keyboard protocal structure. + */ +uprotocal_t rt_usbh_hid_protocal_mouse(void) +{ + mouse_protocal.pro_id = USB_HID_MOUSE; + + mouse_protocal.init = rt_usbh_hid_mouse_init; + mouse_protocal.callback = rt_usbh_hid_mouse_callback; + + return &mouse_protocal; +} + +#endif + diff --git a/components/legacy/usb/usbhost/core/driver.c b/components/legacy/usb/usbhost/core/driver.c new file mode 100644 index 0000000000..28a79dfa7f --- /dev/null +++ b/components/legacy/usb/usbhost/core/driver.c @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2011-03-12 Yi Qiu first version + * 2021-02-23 Leslie Lee provide possibility for multi usb host + */ + +#include <rtthread.h> +#include <rtservice.h> +#include <drivers/usb_host.h> + +static rt_list_t _driver_list; +static rt_bool_t _driver_list_created = RT_FALSE; + +/** + * This function will initilize the usb class driver related data structure, + * and it should be invoked in the usb system initialization. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_class_driver_init(void) +{ + if (_driver_list_created == RT_FALSE) + { + rt_list_init(&_driver_list); + _driver_list_created = RT_TRUE; + } + return RT_EOK; +} + +/** + * This function will register an usb class driver to the class driver manager. + * + * @param drv the pointer of the usb class driver. + * + * @return the error code, RT_EOK on successfully. + */ + +rt_err_t rt_usbh_class_driver_register(ucd_t drv) +{ + if (drv == RT_NULL) return -RT_ERROR; + + if (rt_usbh_class_driver_find(drv->class_code, drv->subclass_code) == RT_NULL) + { + /* insert class driver into driver list */ + rt_list_insert_after(&_driver_list, &(drv->list)); + } + + return RT_EOK; +} + +/** + * This function will removes a previously registed usb class driver. + * + * @param drv the pointer of the usb class driver structure. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_class_driver_unregister(ucd_t drv) +{ + RT_ASSERT(drv != RT_NULL); + + /* remove class driver from driver list */ + rt_list_remove(&(drv->list)); + + return RT_EOK; +} + +/** + * This function will run an usb class driver. + * + * @param drv the pointer of usb class driver. + * @param args the parameter of run function. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_class_driver_enable(ucd_t drv, void* args) +{ + RT_ASSERT(drv != RT_NULL); + + if(drv->enable != RT_NULL) + drv->enable(args); + + return RT_EOK; +} + +/** + * This function will stop a usb class driver. + * + * @param drv the pointer of usb class driver structure. + * @param args the argument of the stop function. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_class_driver_disable(ucd_t drv, void* args) +{ + RT_ASSERT(drv != RT_NULL); + + if(drv->disable != RT_NULL) + drv->disable(args); + + return RT_EOK; +} + + +/** + * This function finds a usb class driver by specified class code and subclass code. + * + * @param class_code the usb class driver's class code. + * @param subclass_code the usb class driver's sub class code. + * + * @return the registered usb class driver on successful, or RT_NULL on failure. + */ +ucd_t rt_usbh_class_driver_find(int class_code, int subclass_code) +{ + struct rt_list_node *node; + + /* enter critical */ + if (rt_thread_self() != RT_NULL) + rt_enter_critical(); + + /* try to find driver object */ + for (node = _driver_list.next; node != &_driver_list; node = node->next) + { + ucd_t drv = + (ucd_t)rt_list_entry(node, struct uclass_driver, list); + if (drv->class_code == class_code) + { + /* leave critical */ + if (rt_thread_self() != RT_NULL) + rt_exit_critical(); + + return drv; + } + } + + /* leave critical */ + if (rt_thread_self() != RT_NULL) + rt_exit_critical(); + + /* not found */ + return RT_NULL; +} diff --git a/components/legacy/usb/usbhost/core/hub.c b/components/legacy/usb/usbhost/core/hub.c new file mode 100644 index 0000000000..d8e6716099 --- /dev/null +++ b/components/legacy/usb/usbhost/core/hub.c @@ -0,0 +1,727 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2011-12-12 Yi Qiu first version + * 2021-02-23 Leslie Lee provide possibility for multi usb host + */ + +#include <rtthread.h> +#include <drivers/usb_host.h> + +#define USB_THREAD_STACK_SIZE 4096 + +#define DBG_TAG "usb.host.hub" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + + +// static struct rt_messagequeue *usb_mq; +static struct uclass_driver hub_driver; +// static struct uhub root_hub; + +static rt_err_t root_hub_ctrl(struct uhcd *hcd, rt_uint16_t port, rt_uint8_t cmd, void *args) +{ + switch(cmd) + { + case RH_GET_PORT_STATUS: + (*(rt_uint32_t *)args) = hcd->roothub->port_status[port-1]; + break; + case RH_SET_PORT_STATUS: + hcd->roothub->port_status[port-1] = (*(rt_uint32_t *)args); + break; + case RH_CLEAR_PORT_FEATURE: + switch(((rt_uint32_t)args)) + { + case PORT_FEAT_C_CONNECTION: + hcd->roothub->port_status[port-1] &= ~PORT_CCSC; + break; + case PORT_FEAT_C_ENABLE: + hcd->roothub->port_status[port-1] &= ~PORT_PESC; + break; + case PORT_FEAT_C_SUSPEND: + hcd->roothub->port_status[port-1] &= ~PORT_PSSC; + break; + case PORT_FEAT_C_OVER_CURRENT: + hcd->roothub->port_status[port-1] &= ~PORT_POCIC; + break; + case PORT_FEAT_C_RESET: + hcd->roothub->port_status[port-1] &= ~PORT_PRSC; + break; + } + break; + case RH_SET_PORT_FEATURE: + switch((rt_uint32_t)args) + { + case PORT_FEAT_CONNECTION: + hcd->roothub->port_status[port-1] |= PORT_CCSC; + break; + case PORT_FEAT_ENABLE: + hcd->roothub->port_status[port-1] |= PORT_PESC; + break; + case PORT_FEAT_SUSPEND: + hcd->roothub->port_status[port-1] |= PORT_PSSC; + break; + case PORT_FEAT_OVER_CURRENT: + hcd->roothub->port_status[port-1] |= PORT_POCIC; + break; + case PORT_FEAT_RESET: + hcd->ops->reset_port(port); + break; + case PORT_FEAT_POWER: + break; + case PORT_FEAT_LOWSPEED: + break; + case PORT_FEAT_HIGHSPEED: + break; + } + break; + default: + return -RT_ERROR; + } + return RT_EOK; +} +void rt_usbh_root_hub_connect_handler(struct uhcd *hcd, rt_uint8_t port, rt_bool_t isHS) +{ + struct uhost_msg msg; + msg.type = USB_MSG_CONNECT_CHANGE; + msg.content.hub = hcd->roothub; + hcd->roothub->port_status[port - 1] |= PORT_CCS | PORT_CCSC; + if(isHS) + { + hcd->roothub->port_status[port - 1] &= ~PORT_LSDA; + } + else + { + hcd->roothub->port_status[port - 1] |= PORT_LSDA; + } + rt_usbh_event_signal(hcd, &msg); +} + +void rt_usbh_root_hub_disconnect_handler(struct uhcd *hcd, rt_uint8_t port) +{ + struct uhost_msg msg; + msg.type = USB_MSG_CONNECT_CHANGE; + msg.content.hub = hcd->roothub; + hcd->roothub->port_status[port - 1] |= PORT_CCSC; + hcd->roothub->port_status[port - 1] &= ~PORT_CCS; + rt_usbh_event_signal(hcd, &msg); +} + +/** + * This function will do USB_REQ_GET_DESCRIPTOR bRequest for the device instance + * to get usb hub descriptor. + * + * @param intf the interface instance. + * @buffer the data buffer to save usb hub descriptor. + * @param nbytes the size of buffer + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_hub_get_descriptor(struct uinstance* device, rt_uint8_t *buffer, rt_size_t nbytes) +{ + struct urequest setup; + int timeout = USB_TIMEOUT_BASIC; + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + + setup.request_type = USB_REQ_TYPE_DIR_IN | USB_REQ_TYPE_CLASS | USB_REQ_TYPE_DEVICE; + setup.bRequest = USB_REQ_GET_DESCRIPTOR; + setup.wIndex = 0; + setup.wLength = nbytes; + setup.wValue = USB_DESC_TYPE_HUB << 8; + + if(rt_usb_hcd_setup_xfer(device->hcd, device->pipe_ep0_out, &setup, timeout) == 8) + { + if(rt_usb_hcd_pipe_xfer(device->hcd, device->pipe_ep0_in, buffer, nbytes, timeout) == nbytes) + { + return RT_EOK; + } + } + return -RT_FALSE; +} + +/** + * This function will do USB_REQ_GET_STATUS bRequest for the device instance + * to get usb hub status. + * + * @param intf the interface instance. + * @buffer the data buffer to save usb hub status. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_hub_get_status(struct uinstance* device, rt_uint32_t* buffer) +{ + struct urequest setup; + int timeout = USB_TIMEOUT_BASIC; + + /* parameter check */ + RT_ASSERT(device != RT_NULL); + + setup.request_type = USB_REQ_TYPE_DIR_IN | USB_REQ_TYPE_CLASS | USB_REQ_TYPE_DEVICE; + setup.bRequest = USB_REQ_GET_STATUS; + setup.wIndex = 0; + setup.wLength = 4; + setup.wValue = 0; + if(rt_usb_hcd_setup_xfer(device->hcd, device->pipe_ep0_out, &setup, timeout) == 8) + { + if(rt_usb_hcd_pipe_xfer(device->hcd, device->pipe_ep0_in, buffer, 4, timeout) == 4) + { + return RT_EOK; + } + } + return -RT_FALSE; +} + +/** + * This function will do USB_REQ_GET_STATUS bRequest for the device instance + * to get hub port status. + * + * @param intf the interface instance. + * @port the hub port to get status. + * @buffer the data buffer to save usb hub status. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_hub_get_port_status(uhub_t hub, rt_uint16_t port, rt_uint32_t* buffer) +{ + struct urequest setup; + int timeout = USB_TIMEOUT_BASIC; + + /* parameter check */ + RT_ASSERT(hub != RT_NULL); + + /* get roothub port status */ + if(hub->is_roothub) + { + root_hub_ctrl(hub->hcd, port, RH_GET_PORT_STATUS, + (void*)buffer); + return RT_EOK; + } + + setup.request_type = USB_REQ_TYPE_DIR_IN | USB_REQ_TYPE_CLASS | USB_REQ_TYPE_OTHER; + setup.bRequest = USB_REQ_GET_STATUS; + setup.wIndex = port; + setup.wLength = 4; + setup.wValue = 0; + + if(rt_usb_hcd_setup_xfer(hub->hcd, hub->self->pipe_ep0_out, &setup, timeout) == 8) + { + if(rt_usb_hcd_pipe_xfer(hub->hcd, hub->self->pipe_ep0_in, buffer, 4, timeout) == 4) + { + return RT_EOK; + } + } + return -RT_FALSE; +} + +/** + * This function will do USB_REQ_CLEAR_FEATURE bRequest for the device instance + * to clear feature of the hub port. + * + * @param intf the interface instance. + * @port the hub port. + * @feature feature to be cleared. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_hub_clear_port_feature(uhub_t hub, rt_uint16_t port, rt_uint16_t feature) +{ + struct urequest setup; + int timeout = USB_TIMEOUT_BASIC; + + /* parameter check */ + RT_ASSERT(hub != RT_NULL); + + /* clear roothub feature */ + if(hub->is_roothub) + { + root_hub_ctrl(hub->hcd, port, RH_CLEAR_PORT_FEATURE, + (void*)(rt_uint32_t)feature); + return RT_EOK; + } + + setup.request_type = USB_REQ_TYPE_DIR_OUT | USB_REQ_TYPE_CLASS | + USB_REQ_TYPE_OTHER; + setup.bRequest = USB_REQ_CLEAR_FEATURE; + setup.wIndex = port; + setup.wLength = 0; + setup.wValue = feature; + + if(rt_usb_hcd_setup_xfer(hub->hcd, hub->self->pipe_ep0_out, &setup, timeout) == 8) + { + return RT_EOK; + } + return -RT_FALSE; +} + +/** + * This function will do USB_REQ_SET_FEATURE bRequest for the device instance + * to set feature of the hub port. + * + * @param intf the interface instance. + * @port the hub port. + * @feature feature to be set. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_hub_set_port_feature(uhub_t hub, rt_uint16_t port, + rt_uint16_t feature) +{ + struct urequest setup; + int timeout = USB_TIMEOUT_BASIC; + + /* parameter check */ + RT_ASSERT(hub != RT_NULL); + + /* clear roothub feature */ + if(hub->is_roothub) + { + root_hub_ctrl(hub->hcd, port, RH_SET_PORT_FEATURE, + (void*)(rt_uint32_t)feature); + return RT_EOK; + } + + setup.request_type = USB_REQ_TYPE_DIR_OUT | USB_REQ_TYPE_CLASS | + USB_REQ_TYPE_OTHER; + setup.bRequest = USB_REQ_SET_FEATURE; + setup.wIndex = port; + setup.wLength = 0; + setup.wValue = feature; + + if(rt_usb_hcd_setup_xfer(hub->hcd, hub->self->pipe_ep0_out, &setup, timeout) == 8) + { + return RT_EOK; + } + else return -RT_FALSE; +} + +/** + * This function will rest hub port, it is invoked when sub device attached to the hub port. + * + * @param intf the interface instance. + * @param port the hub port. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_hub_reset_port(uhub_t hub, rt_uint16_t port) +{ + rt_err_t ret; + rt_uint32_t pstatus; + + /* parameter check */ + RT_ASSERT(hub != RT_NULL); + + rt_thread_delay(50); + + /* reset hub port */ + ret = rt_usbh_hub_set_port_feature(hub, port, PORT_FEAT_RESET); + if(ret != RT_EOK) return ret; + + while(1) + { + ret = rt_usbh_hub_get_port_status(hub, port, &pstatus); + if(!(pstatus & PORT_PRS)) break; + } + + /* clear port reset feature */ + ret = rt_usbh_hub_clear_port_feature(hub, port, PORT_FEAT_C_RESET); + if(ret != RT_EOK) return ret; + + rt_thread_delay(50); + + return RT_EOK; +} + +/** + * This function will do debouce, it is invoked when sub device attached to the hub port. + * + * @param device the usb instance. + * @param port the hub port. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_hub_port_debounce(uhub_t hub, rt_uint16_t port) +{ + rt_err_t ret; + int i = 0, times = 20; + rt_uint32_t pstatus; + rt_bool_t connect = RT_TRUE; + int delayticks = USB_DEBOUNCE_TIME / times; + if (delayticks < 1) + delayticks = 1; + + /* parameter check */ + RT_ASSERT(hub != RT_NULL); + + for(i=0; i<times; i++) + { + ret = rt_usbh_hub_get_port_status(hub, port, &pstatus); + if(ret != RT_EOK) return ret; + + if(!(pstatus & PORT_CCS)) + { + connect = RT_FALSE; + break; + } + + rt_thread_delay(delayticks); + } + + if(connect) return RT_EOK; + else return -RT_ERROR; +} + +/** + * This function will poll all the hub ports to detect port status, especially connect and + * disconnect events. + * + * @param intf the interface instance. + * + * @return the error code, RT_EOK on successfully. + */ +static rt_err_t rt_usbh_hub_port_change(uhub_t hub) +{ + int i; + rt_bool_t reconnect; + + /* parameter check */ + RT_ASSERT(hub != RT_NULL); + + /* get usb device instance */ + for (i = 0; i < hub->num_ports; i++) + { + rt_err_t ret; + struct uinstance* device; + rt_uint32_t pstatus = 0; + + reconnect = RT_FALSE; + + /* get hub port status */ + ret = rt_usbh_hub_get_port_status(hub, i + 1, &pstatus); + if(ret != RT_EOK) continue; + + LOG_D("port %d status 0x%x", i + 1, pstatus); + + /* check port status change */ + if (pstatus & PORT_CCSC) + { + /* clear port status change feature */ + rt_usbh_hub_clear_port_feature(hub, i + 1, PORT_FEAT_C_CONNECTION); + reconnect = RT_TRUE; + } + + if(pstatus & PORT_PESC) + { + rt_usbh_hub_clear_port_feature(hub, i + 1, PORT_FEAT_C_ENABLE); + reconnect = RT_TRUE; + } + + if(reconnect) + { + if(hub->child[i] != RT_NULL && hub->child[i]->status != DEV_STATUS_IDLE) + { + rt_usbh_detach_instance(hub->child[i]); + + /* Child device have been detach. Set hub->child[i] to NULL. */ + hub->child[i] = RT_NULL; + } + + ret = rt_usbh_hub_port_debounce(hub, i + 1); + if(ret != RT_EOK) continue; + + /* allocate an usb instance for new connected device */ + device = rt_usbh_alloc_instance(hub->hcd); + if(device == RT_NULL) break; + + /* set usb device speed */ + device->speed = (pstatus & PORT_LSDA) ? 1 : 0; + device->parent_hub = hub; + device->hcd = hub->hcd; + device->port = i + 1; + hub->child[i] = device; + + /* reset usb roothub port */ + rt_usbh_hub_reset_port(hub, i + 1); + + /* attatch the usb instance to the hcd */ + rt_usbh_attatch_instance(device); + } + } + + return RT_EOK; +} + +/** + * This function is the callback function of hub's int endpoint, it is invoked when data comes. + * + * @param context the context of the callback function. + * + * @return none. + */ +static void rt_usbh_hub_irq(void* context) +{ + upipe_t pipe; + uhub_t hub; + int timeout = USB_TIMEOUT_BASIC; + + RT_ASSERT(context != RT_NULL); + + pipe = (upipe_t)context; + hub = (uhub_t)pipe->user_data; + + if(pipe->status != UPIPE_STATUS_OK) + { + LOG_D("hub irq error"); + return; + } + + rt_usbh_hub_port_change(hub); + + LOG_D("hub int xfer..."); + + /* parameter check */ + RT_ASSERT(pipe->inst->hcd != RT_NULL); + + rt_usb_hcd_pipe_xfer(hub->self->hcd, pipe, hub->buffer, pipe->ep.wMaxPacketSize, timeout); +} + +/** + * This function will run usb hub class driver when usb hub is detected and identified + * as a hub class device, it will continue to do the enumulate process. + * + * @param arg the argument. + * + * @return the error code, RT_EOK on successfully. + */ + +static rt_err_t rt_usbh_hub_enable(void *arg) +{ + int i = 0; + rt_err_t ret = RT_EOK; + uep_desc_t ep_desc = RT_NULL; + uhub_t hub; + struct uinstance* device; + struct uhintf* intf = (struct uhintf*)arg; + upipe_t pipe_in = RT_NULL; + int timeout = USB_TIMEOUT_LONG; + /* paremeter check */ + RT_ASSERT(intf != RT_NULL); + + /* get usb device instance */ + device = intf->device; + + /* create a hub instance */ + hub = rt_malloc(sizeof(struct uhub)); + RT_ASSERT(hub != RT_NULL); + rt_memset(hub, 0, sizeof(struct uhub)); + + /* make interface instance's user data point to hub instance */ + intf->user_data = (void*)hub; + + /* get hub descriptor head */ + ret = rt_usbh_hub_get_descriptor(device, (rt_uint8_t*)&hub->hub_desc, 8); + if(ret != RT_EOK) + { + rt_kprintf("get hub descriptor failed\n"); + return -RT_ERROR; + } + + /* get full hub descriptor */ + ret = rt_usbh_hub_get_descriptor(device, (rt_uint8_t*)&hub->hub_desc, + hub->hub_desc.length); + if(ret != RT_EOK) + { + rt_kprintf("get hub descriptor again failed\n"); + return -RT_ERROR; + } + + /* get hub ports number */ + /* If hub device supported ports over USB_HUB_PORT_NUM(Ex: 8 port hub). Set hub->num_ports to USB_HUB_PORT_NUM */ + if(hub->hub_desc.num_ports > USB_HUB_PORT_NUM) + hub->num_ports = USB_HUB_PORT_NUM; + else + hub->num_ports = hub->hub_desc.num_ports; + + hub->hcd = device->hcd; + hub->self = device; + + /* reset all hub ports */ + for (i = 0; i < hub->num_ports; i++) + { + rt_usbh_hub_set_port_feature(hub, i + 1, PORT_FEAT_POWER); + rt_thread_delay(hub->hub_desc.pwron_to_good + * 2 * RT_TICK_PER_SECOND / 1000 ); + } + + if(intf->intf_desc->bNumEndpoints != 1) + return -RT_ERROR; + + /* get endpoint descriptor from interface descriptor */ + rt_usbh_get_endpoint_descriptor(intf->intf_desc, 0, &ep_desc); + if(ep_desc == RT_NULL) + { + rt_kprintf("rt_usb_get_endpoint_descriptor error\n"); + return -RT_ERROR; + } + + /* the endpoint type of hub class should be interrupt */ + if( USB_EP_ATTR(ep_desc->bmAttributes) == USB_EP_ATTR_INT) + { + /* the endpoint direction of hub class should be in */ + if(ep_desc->bEndpointAddress & USB_DIR_IN) + { + /* allocate a pipe according to the endpoint type */ + pipe_in = rt_usb_instance_find_pipe(device,ep_desc->bEndpointAddress); + if(pipe_in == RT_NULL) + { + return -RT_ERROR; + } + rt_usb_pipe_add_callback(pipe_in,rt_usbh_hub_irq); + } + else return -RT_ERROR; + } + + /* parameter check */ + RT_ASSERT(device->hcd != RT_NULL); + pipe_in->user_data = hub; + rt_usb_hcd_pipe_xfer(hub->hcd, pipe_in, hub->buffer, + pipe_in->ep.wMaxPacketSize, timeout); + return RT_EOK; +} + +/** + * This function will be invoked when usb hub plug out is detected and it would clean + * and release all hub class related resources. + * + * @param arg the argument. + * + * @return the error code, RT_EOK on successfully. + */ +static rt_err_t rt_usbh_hub_disable(void* arg) +{ + int i; + uhub_t hub; + struct uhintf* intf = (struct uhintf*)arg; + + /* paremeter check */ + RT_ASSERT(intf != RT_NULL); + + LOG_D("rt_usbh_hub_stop"); + hub = (uhub_t)intf->user_data; + + for(i=0; i<hub->num_ports; i++) + { + if(hub->child[i] != RT_NULL) + rt_usbh_detach_instance(hub->child[i]); + } + + if(hub != RT_NULL) rt_free(hub); + + return RT_EOK; +} + +/** + * This function will register hub class driver to the usb class driver manager. + * and it should be invoked in the usb system initialization. + * + * @return the error code, RT_EOK on successfully. + */ +ucd_t rt_usbh_class_driver_hub(void) +{ + hub_driver.class_code = USB_CLASS_HUB; + + hub_driver.enable = rt_usbh_hub_enable; + hub_driver.disable = rt_usbh_hub_disable; + + return &hub_driver; +} + +/** + * This function is the main entry of usb hub thread, it is in charge of + * processing all messages received from the usb message buffer. + * + * @param parameter the parameter of the usb host thread. + * + * @return none. + */ +static void rt_usbh_hub_thread_entry(void* parameter) +{ + uhcd_t hcd = (uhcd_t)parameter; + while(1) + { + struct uhost_msg msg; + + /* receive message */ + if (rt_mq_recv(hcd->usb_mq, &msg, sizeof(struct uhost_msg), RT_WAITING_FOREVER) < 0) + continue; + + switch (msg.type) + { + case USB_MSG_CONNECT_CHANGE: + rt_usbh_hub_port_change(msg.content.hub); + break; + case USB_MSG_CALLBACK: + /* invoke callback */ + msg.content.cb.function(msg.content.cb.context); + break; + default: + break; + } + } +} + +/** + * This function will post an message to the usb message queue, + * + * @param msg the message to be posted + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_event_signal(uhcd_t hcd, struct uhost_msg* msg) +{ + RT_ASSERT(msg != RT_NULL); + + /* send message to usb message queue */ + rt_mq_send(hcd->usb_mq, (void*)msg, sizeof(struct uhost_msg)); + + return RT_EOK; +} + +/** + * This function will initialize usb hub thread. + * + * @return none. + * + */ +void rt_usbh_hub_init(uhcd_t hcd) +{ + rt_thread_t thread; + /* create root hub for hcd */ + hcd->roothub = rt_malloc(sizeof(struct uhub)); + if(hcd->roothub == RT_NULL) + { + LOG_E("hcd->roothub: allocate buffer failed."); + return; + } + rt_memset(hcd->roothub, 0, sizeof(struct uhub)); + hcd->roothub->is_roothub = RT_TRUE; + hcd->roothub->hcd = hcd; + hcd->roothub->num_ports = hcd->num_ports; + /* create usb message queue */ + + hcd->usb_mq = rt_mq_create(hcd->parent.parent.name, 32, 16, RT_IPC_FLAG_FIFO); + + /* create usb hub thread */ + thread = rt_thread_create(hcd->parent.parent.name, rt_usbh_hub_thread_entry, hcd, + USB_THREAD_STACK_SIZE, 8, 20); + if(thread != RT_NULL) + { + /* startup usb host thread */ + rt_thread_startup(thread); + } +} diff --git a/components/legacy/usb/usbhost/core/usbhost.c b/components/legacy/usb/usbhost/core/usbhost.c new file mode 100644 index 0000000000..a4cdc12e5e --- /dev/null +++ b/components/legacy/usb/usbhost/core/usbhost.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2011-12-12 Yi Qiu first version + * 2021-02-23 Leslie Lee provide possibility for multi usb host + */ +#include <rtthread.h> +#include <drivers/usb_host.h> + +#define USB_HOST_CONTROLLER_NAME "usbh" + +#if defined(RT_USBH_HID_KEYBOARD) || defined(RT_USBH_HID_MOUSE) +#include <hid.h> +#endif + +/** + * This function will initialize the usb host stack, all the usb class driver and + * host controller driver are also be initialized here. + * + * @return none. + */ +rt_err_t rt_usb_host_init(const char *name) +{ + ucd_t drv; + rt_device_t uhc; + + uhc = rt_device_find(name); + if(uhc == RT_NULL) + { + rt_kprintf("can't find usb host controller %s\n", name); + return -RT_ERROR; + } + + /* initialize usb hub */ + rt_usbh_hub_init((uhcd_t)uhc); + + /* initialize class driver */ + rt_usbh_class_driver_init(); + +#ifdef RT_USBH_MSTORAGE + /* register mass storage class driver */ + drv = rt_usbh_class_driver_storage(); + rt_usbh_class_driver_register(drv); +#endif +#ifdef RT_USBH_HID + extern ucd_t rt_usbh_class_driver_hid(void); + /* register mass storage class driver */ + drv = rt_usbh_class_driver_hid(); + rt_usbh_class_driver_register(drv); +#ifdef RT_USBH_HID_MOUSE + { + extern uprotocal_t rt_usbh_hid_protocal_mouse(void); + rt_usbh_hid_protocal_register(rt_usbh_hid_protocal_mouse()); + } +#endif +#ifdef RT_USBH_HID_KEYBOARD + { + extern uprotocal_t rt_usbh_hid_protocal_kbd(void); + rt_usbh_hid_protocal_register(rt_usbh_hid_protocal_kbd()); + } +#endif +#endif + + /* register hub class driver */ + drv = rt_usbh_class_driver_hub(); + rt_usbh_class_driver_register(drv); + + /* initialize usb host controller */ + rt_device_init(uhc); + + return RT_EOK; +} diff --git a/components/legacy/usb/usbhost/core/usbhost_core.c b/components/legacy/usb/usbhost/core/usbhost_core.c new file mode 100644 index 0000000000..fae5ba75fb --- /dev/null +++ b/components/legacy/usb/usbhost/core/usbhost_core.c @@ -0,0 +1,586 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2011-12-12 Yi Qiu first version + */ + +#include <rtthread.h> +#include <drivers/usb_host.h> + +#define DBG_TAG "usbhost.core" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + +static struct uinstance dev[USB_MAX_DEVICE]; + +/** + * This function will allocate an usb device instance from system. + * + * @param parent the hub instance to which the new allocated device attached. + * @param port the hub port. + * + * @return the allocate instance on successful, or RT_NULL on failure. + */ +uinst_t rt_usbh_alloc_instance(uhcd_t uhcd) +{ + int i; + + /* lock scheduler */ + rt_enter_critical(); + + for(i=0; i<USB_MAX_DEVICE; i++) + { + /* to find an idle instance handle */ + if(dev[i].status != DEV_STATUS_IDLE) continue; + + /* initialize the usb device instance */ + rt_memset(&dev[i], 0, sizeof(struct uinstance)); + + dev[i].status = DEV_STATUS_BUSY; + dev[i].index = i + 1; + dev[i].address = 0; + dev[i].max_packet_size = 0x8; + rt_list_init(&dev[i].pipe); + dev[i].hcd = uhcd; + /* unlock scheduler */ + rt_exit_critical(); + return &dev[i]; + } + + /* unlock scheduler */ + rt_exit_critical(); + + return RT_NULL; +} + +/** + * This function will attatch an usb device instance to a host controller, + * and do device enumunation process. + * + * @param hcd the host controller driver. + * @param device the usb device instance. + * + * @return the error code, RT_EOK on successfully. + */ +static struct uendpoint_descriptor ep0_out_desc = +{ + /*endpoint descriptor*/ + USB_DESC_LENGTH_ENDPOINT, + USB_DESC_TYPE_ENDPOINT, + 0x00 | USB_DIR_OUT, + USB_EP_ATTR_CONTROL, + 0x00, + 0x00, +}; +static struct uendpoint_descriptor ep0_in_desc = +{ + /*endpoint descriptor*/ + USB_DESC_LENGTH_ENDPOINT, + USB_DESC_TYPE_ENDPOINT, + 0x00 | USB_DIR_IN, + USB_EP_ATTR_CONTROL, + 0x00, + 0x00, +}; +rt_err_t rt_usbh_attatch_instance(uinst_t device) +{ + int i = 0; + rt_err_t ret = RT_EOK; + struct uconfig_descriptor cfg_desc; + udev_desc_t dev_desc; + uintf_desc_t intf_desc; + uep_desc_t ep_desc; + rt_uint8_t ep_index; + upipe_t pipe; + ucd_t drv; + + RT_ASSERT(device != RT_NULL); + + rt_memset(&cfg_desc, 0, sizeof(struct uconfig_descriptor)); + dev_desc = &device->dev_desc; + + /* alloc address 0 ep0 pipe*/ + ep0_out_desc.wMaxPacketSize = 8; + ep0_in_desc.wMaxPacketSize = 8; + rt_usb_hcd_alloc_pipe(device->hcd, &device->pipe_ep0_out, device, &ep0_out_desc); + rt_usb_hcd_alloc_pipe(device->hcd, &device->pipe_ep0_in, device, &ep0_in_desc); + + LOG_D("start enumnation"); + + /* get device descriptor head */ + ret = rt_usbh_get_descriptor(device, USB_DESC_TYPE_DEVICE, (void*)dev_desc, 8); + if(ret != RT_EOK) + { + rt_kprintf("get device descriptor head failed\n"); + return ret; + } + + /* reset bus */ + rt_usbh_hub_reset_port(device->parent_hub, device->port); + rt_thread_delay(2); + rt_usbh_hub_clear_port_feature(device->parent_hub, i + 1, PORT_FEAT_C_CONNECTION); + /* set device address */ + ret = rt_usbh_set_address(device); + if(ret != RT_EOK) + { + rt_kprintf("set device address failed\n"); + return ret; + } + /* free address 0 ep0 pipe*/ + + rt_usb_hcd_free_pipe(device->hcd,device->pipe_ep0_out); + rt_usb_hcd_free_pipe(device->hcd,device->pipe_ep0_in); + + /* set device max packet size */ + ep0_out_desc.wMaxPacketSize = device->dev_desc.bMaxPacketSize0; + ep0_in_desc.wMaxPacketSize = device->dev_desc.bMaxPacketSize0; + + /* alloc true address ep0 pipe*/ + rt_usb_hcd_alloc_pipe(device->hcd, &device->pipe_ep0_out, device, &ep0_out_desc); + rt_usb_hcd_alloc_pipe(device->hcd, &device->pipe_ep0_in, device, &ep0_in_desc); + LOG_D("get device descriptor length %d", + dev_desc->bLength); + + /* get full device descriptor again */ + ret = rt_usbh_get_descriptor(device, USB_DESC_TYPE_DEVICE, (void*)dev_desc, dev_desc->bLength); + if(ret != RT_EOK) + { + rt_kprintf("get full device descriptor failed\n"); + return ret; + } + + LOG_D("Vendor ID 0x%x", dev_desc->idVendor); + LOG_D("Product ID 0x%x", dev_desc->idProduct); + + /* get configuration descriptor head */ + ret = rt_usbh_get_descriptor(device, USB_DESC_TYPE_CONFIGURATION, &cfg_desc, 18); + if(ret != RT_EOK) + { + rt_kprintf("get configuration descriptor head failed\n"); + return ret; + } + + /* alloc memory for configuration descriptor */ + device->cfg_desc = (ucfg_desc_t)rt_malloc(cfg_desc.wTotalLength); + if(device->cfg_desc == RT_NULL) + { + return -RT_ENOMEM; + } + rt_memset(device->cfg_desc, 0, cfg_desc.wTotalLength); + + /* get full configuration descriptor */ + ret = rt_usbh_get_descriptor(device, USB_DESC_TYPE_CONFIGURATION, + device->cfg_desc, cfg_desc.wTotalLength); + if(ret != RT_EOK) + { + rt_kprintf("get full configuration descriptor failed\n"); + return ret; + } + + /* set configuration */ + ret = rt_usbh_set_configure(device, 1); + if(ret != RT_EOK) + { + return ret; + } + for(i=0; i<device->cfg_desc->bNumInterfaces; i++) + { + /* get interface descriptor through configuration descriptor */ + ret = rt_usbh_get_interface_descriptor(device->cfg_desc, i, &intf_desc); + if(ret != RT_EOK) + { + rt_kprintf("rt_usb_get_interface_descriptor error\n"); + return -RT_ERROR; + } + + LOG_D("interface class 0x%x, subclass 0x%x", + intf_desc->bInterfaceClass, + intf_desc->bInterfaceSubClass); + /* alloc pipe*/ + for(ep_index = 0; ep_index < intf_desc->bNumEndpoints; ep_index++) + { + rt_usbh_get_endpoint_descriptor(intf_desc, ep_index, &ep_desc); + if(ep_desc != RT_NULL) + { + if(rt_usb_hcd_alloc_pipe(device->hcd, &pipe, device, ep_desc) != RT_EOK) + { + rt_kprintf("alloc pipe failed\n"); + return -RT_ERROR; + } + rt_usb_instance_add_pipe(device,pipe); + } + else + { + rt_kprintf("get endpoint desc failed\n"); + return -RT_ERROR; + } + } + /* find driver by class code found in interface descriptor */ + drv = rt_usbh_class_driver_find(intf_desc->bInterfaceClass, + intf_desc->bInterfaceSubClass); + + if(drv != RT_NULL) + { + /* allocate memory for interface device */ + device->intf[i] = (struct uhintf*)rt_malloc(sizeof(struct uhintf)); + if(device->intf[i] == RT_NULL) + { + return -RT_ENOMEM; + } + device->intf[i]->drv = drv; + device->intf[i]->device = device; + device->intf[i]->intf_desc = intf_desc; + device->intf[i]->user_data = RT_NULL; + + /* open usb class driver */ + ret = rt_usbh_class_driver_enable(drv, (void*)device->intf[i]); + if(ret != RT_EOK) + { + rt_kprintf("interface %d run class driver error\n", i); + } + } + else + { + rt_kprintf("find usb device driver failed\n"); + continue; + } + } + + return RT_EOK; +} + +/** + * This function will detach an usb device instance from its host controller, + * and release all resource. + * + * @param device the usb device instance. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_detach_instance(uinst_t device) +{ + int i = 0; + rt_list_t * l; + if(device == RT_NULL) + { + rt_kprintf("no usb instance to detach\n"); + return -RT_ERROR; + } + + /* free configration descriptor */ + if (device->cfg_desc) { + for (i = 0; i < device->cfg_desc->bNumInterfaces; i++) + { + if (device->intf[i] == RT_NULL) continue; + if (device->intf[i]->drv == RT_NULL) continue; + + RT_ASSERT(device->intf[i]->device == device); + + LOG_D("free interface instance %d", i); + rt_usbh_class_driver_disable(device->intf[i]->drv, (void*)device->intf[i]); + rt_free(device->intf[i]); + } + rt_free(device->cfg_desc); + } + + rt_usb_hcd_free_pipe(device->hcd,device->pipe_ep0_out); + rt_usb_hcd_free_pipe(device->hcd,device->pipe_ep0_in); + + while(device->pipe.next!= &device->pipe) + { + l = device->pipe.next; + rt_list_remove(l); + rt_usb_hcd_free_pipe(device->hcd,rt_list_entry(l,struct upipe,list)); + } + rt_memset(device, 0, sizeof(struct uinstance)); + + return RT_EOK; +} + +/** + * This function will do USB_REQ_GET_DESCRIPTO' bRequest for the usb device instance, + * + * @param device the usb device instance. + * @param type the type of descriptor bRequest. + * @param buffer the data buffer to save requested data + * @param nbytes the size of buffer + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_get_descriptor(uinst_t device, rt_uint8_t type, void* buffer, + int nbytes) +{ + struct urequest setup; + int timeout = USB_TIMEOUT_BASIC; + + RT_ASSERT(device != RT_NULL); + + setup.request_type = USB_REQ_TYPE_DIR_IN | USB_REQ_TYPE_STANDARD | + USB_REQ_TYPE_DEVICE; + setup.bRequest = USB_REQ_GET_DESCRIPTOR; + setup.wIndex = 0; + setup.wLength = nbytes; + setup.wValue = type << 8; + + if(rt_usb_hcd_setup_xfer(device->hcd, device->pipe_ep0_out, &setup, timeout) == 8) + { + if(rt_usb_hcd_pipe_xfer(device->hcd, device->pipe_ep0_in, buffer, nbytes, timeout) == nbytes) + { + if(rt_usb_hcd_pipe_xfer(device->hcd, device->pipe_ep0_out, RT_NULL, 0, timeout) == 0) + { + return RT_EOK; + } + } + } + return -RT_ERROR; +} + +/** + * This function will set an address to the usb device. + * + * @param device the usb device instance. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_set_address(uinst_t device) +{ + struct urequest setup; + int timeout = USB_TIMEOUT_BASIC; + + RT_ASSERT(device != RT_NULL); + + LOG_D("rt_usb_set_address"); + + setup.request_type = USB_REQ_TYPE_DIR_OUT | USB_REQ_TYPE_STANDARD | + USB_REQ_TYPE_DEVICE; + setup.bRequest = USB_REQ_SET_ADDRESS; + setup.wIndex = 0; + setup.wLength = 0; + setup.wValue = device->index; + + if(rt_usb_hcd_setup_xfer(device->hcd, device->pipe_ep0_out, &setup, timeout) != 8) + { + return -RT_ERROR; + } + if(rt_usb_hcd_pipe_xfer(device->hcd, device->pipe_ep0_in, RT_NULL, 0, timeout) == 0) + { + device->address = device->index; + } + + return RT_EOK; +} + +/** + * This function will set a configuration to the usb device. + * + * @param device the usb device instance. + * @param config the configuration number. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_set_configure(uinst_t device, int config) +{ + struct urequest setup; + int timeout = USB_TIMEOUT_BASIC; + + /* check parameter */ + RT_ASSERT(device != RT_NULL); + + setup.request_type = USB_REQ_TYPE_DIR_OUT | USB_REQ_TYPE_STANDARD | + USB_REQ_TYPE_DEVICE; + setup.bRequest = USB_REQ_SET_CONFIGURATION; + setup.wIndex = 0; + setup.wLength = 0; + setup.wValue = config; + + if(rt_usb_hcd_setup_xfer(device->hcd, device->pipe_ep0_out, &setup, timeout) != 8) + { + return -RT_ERROR; + } + if(rt_usb_hcd_pipe_xfer(device->hcd, device->pipe_ep0_in, RT_NULL, 0, timeout) != 0) + { + return -RT_ERROR; + } + return RT_EOK; +} + +/** + * This function will set an interface to the usb device. + * + * @param device the usb device instance. + * @param intf the interface number. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_set_interface(uinst_t device, int intf) +{ + struct urequest setup; + int timeout = USB_TIMEOUT_BASIC; + + /* check parameter */ + RT_ASSERT(device != RT_NULL); + + setup.request_type = USB_REQ_TYPE_DIR_OUT | USB_REQ_TYPE_STANDARD | + USB_REQ_TYPE_INTERFACE; + setup.bRequest = USB_REQ_SET_INTERFACE; + setup.wIndex = 0; + setup.wLength = 0; + setup.wValue = intf; + + if(rt_usb_hcd_setup_xfer(device->hcd, device->pipe_ep0_out, &setup, timeout) != 8) + { + return -RT_ERROR; + } + + return RT_EOK; +} + +/** + * This function will clear feature for the endpoint of the usb device. + * + * @param device the usb device instance. + * @param endpoint the endpoint number of the usb device. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_clear_feature(uinst_t device, int endpoint, int feature) +{ + struct urequest setup; + int timeout = USB_TIMEOUT_BASIC; + + /* check parameter */ + RT_ASSERT(device != RT_NULL); + + setup.request_type = USB_REQ_TYPE_DIR_OUT | USB_REQ_TYPE_STANDARD | + USB_REQ_TYPE_ENDPOINT; + setup.bRequest = USB_REQ_CLEAR_FEATURE; + setup.wIndex = endpoint; + setup.wLength = 0; + setup.wValue = feature; + + if(rt_usb_hcd_setup_xfer(device->hcd, device->pipe_ep0_out, &setup, timeout) != 8) + { + return -RT_ERROR; + } + + return RT_EOK; +} + +/** + * This function will get an interface descriptor from the configuration descriptor. + * + * @param cfg_desc the point of configuration descriptor structure. + * @param num the number of interface descriptor. + * @intf_desc the point of interface descriptor point. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_get_interface_descriptor(ucfg_desc_t cfg_desc, int num, + uintf_desc_t* intf_desc) +{ + rt_uint32_t ptr, depth = 0; + udesc_t desc; + + /* check parameter */ + RT_ASSERT(cfg_desc != RT_NULL); + + ptr = (rt_uint32_t)cfg_desc + cfg_desc->bLength; + while(ptr < (rt_uint32_t)cfg_desc + cfg_desc->wTotalLength) + { + if(depth++ > 0x20) + { + *intf_desc = RT_NULL; + return -RT_EIO; + } + desc = (udesc_t)ptr; + if(desc->type == USB_DESC_TYPE_INTERFACE) + { + if(((uintf_desc_t)desc)->bInterfaceNumber == num) + { + *intf_desc = (uintf_desc_t)desc; + + LOG_D("rt_usb_get_interface_descriptor: %d", num); + return RT_EOK; + } + } + ptr = (rt_uint32_t)desc + desc->bLength; + } + + rt_kprintf("rt_usb_get_interface_descriptor %d failed\n", num); + return -RT_EIO; +} + +/** + * This function will get an endpoint descriptor from the interface descriptor. + * + * @param intf_desc the point of interface descriptor structure. + * @param num the number of endpoint descriptor. + * @param ep_desc the point of endpoint descriptor point. + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_usbh_get_endpoint_descriptor(uintf_desc_t intf_desc, int num, + uep_desc_t* ep_desc) +{ + int count = 0, depth = 0; + rt_uint32_t ptr; + udesc_t desc; + + /* check parameter */ + RT_ASSERT(intf_desc != RT_NULL); + RT_ASSERT(num < intf_desc->bNumEndpoints); + *ep_desc = RT_NULL; + + ptr = (rt_uint32_t)intf_desc + intf_desc->bLength; + while(count < intf_desc->bNumEndpoints) + { + if(depth++ > 0x20) + { + *ep_desc = RT_NULL; + return -RT_EIO; + } + desc = (udesc_t)ptr; + if(desc->type == USB_DESC_TYPE_ENDPOINT) + { + if(num == count) + { + *ep_desc = (uep_desc_t)desc; + + LOG_D("rt_usb_get_endpoint_descriptor: %d", num); + return RT_EOK; + } + else count++; + } + ptr = (rt_uint32_t)desc + desc->bLength; + } + + rt_kprintf("rt_usb_get_endpoint_descriptor %d failed\n", num); + return -RT_EIO; +} + +int rt_usb_hcd_pipe_xfer(uhcd_t hcd, upipe_t pipe, void* buffer, int nbytes, int timeout) +{ + rt_size_t remain_size; + rt_size_t send_size; + remain_size = nbytes; + rt_uint8_t * pbuffer = (rt_uint8_t *)buffer; + do + { + LOG_D("pipe transform remain size,: %d", remain_size); + send_size = (remain_size > pipe->ep.wMaxPacketSize) ? pipe->ep.wMaxPacketSize : remain_size; + if(hcd->ops->pipe_xfer(pipe, USBH_PID_DATA, pbuffer, send_size, timeout) == send_size) + { + remain_size -= send_size; + pbuffer += send_size; + } + else + { + return 0; + } + }while(remain_size > 0); + return nbytes; +} |
