From 25c1bea782e69e8d365e8b61e12d888611d2f6f7 Mon Sep 17 00:00:00 2001 From: Nathan Conrad Date: Tue, 14 Jan 2020 23:30:39 -0500 Subject: Normalize line endings --- src/device/dcd.h | 302 ++++---- src/device/usbd.c | 2002 ++++++++++++++++++++++++++--------------------------- src/device/usbd.h | 670 +++++++++--------- 3 files changed, 1487 insertions(+), 1487 deletions(-) (limited to 'src/device') diff --git a/src/device/dcd.h b/src/device/dcd.h index 8ae232fa6..143a2de34 100644 --- a/src/device/dcd.h +++ b/src/device/dcd.h @@ -1,151 +1,151 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2019 Ha Thach (tinyusb.org) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * This file is part of the TinyUSB stack. - */ - -/** \ingroup group_usbd - * \defgroup group_dcd Device Controller Driver (DCD) - * @{ */ - -#ifndef _TUSB_DCD_H_ -#define _TUSB_DCD_H_ - -#include "common/tusb_common.h" - -#ifdef __cplusplus - extern "C" { -#endif - -typedef enum -{ - DCD_EVENT_INVALID = 0, - DCD_EVENT_BUS_RESET, - DCD_EVENT_UNPLUGGED, - DCD_EVENT_SOF, - DCD_EVENT_SUSPEND, // TODO LPM Sleep L1 support - DCD_EVENT_RESUME, - - DCD_EVENT_SETUP_RECEIVED, - DCD_EVENT_XFER_COMPLETE, - - // Not an DCD event, just a convenient way to defer ISR function - USBD_EVENT_FUNC_CALL, - - DCD_EVENT_COUNT -} dcd_eventid_t; - -typedef struct TU_ATTR_ALIGNED(4) -{ - uint8_t rhport; - uint8_t event_id; - - union { - // USBD_EVT_SETUP_RECEIVED - tusb_control_request_t setup_received; - - // USBD_EVT_XFER_COMPLETE - struct { - uint8_t ep_addr; - uint8_t result; - uint32_t len; - }xfer_complete; - - // USBD_EVENT_FUNC_CALL - struct { - void (*func) (void*); - void* param; - }func_call; - }; -} dcd_event_t; - -//TU_VERIFY_STATIC(sizeof(dcd_event_t) <= 12, "size is not correct"); - -/*------------------------------------------------------------------*/ -/* Device API - *------------------------------------------------------------------*/ - -// Initialize controller to device mode -void dcd_init (uint8_t rhport); - -// Interrupt Handler -void dcd_isr (uint8_t rhport); - -// Enable device interrupt -void dcd_int_enable (uint8_t rhport); - -// Disable device interrupt -void dcd_int_disable(uint8_t rhport); - -// Receive Set Address request, mcu port must also include status IN response -void dcd_set_address(uint8_t rhport, uint8_t dev_addr); - -// Receive Set Configure request -void dcd_set_config (uint8_t rhport, uint8_t config_num); - -// Wake up host -void dcd_remote_wakeup(uint8_t rhport); - -//--------------------------------------------------------------------+ -// Endpoint API -//--------------------------------------------------------------------+ - -// Invoked when a control transfer's status stage is complete. -// May help DCD to prepare for next control transfer, this API is optional. -void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request) TU_ATTR_WEAK; - -// Configure endpoint's registers according to descriptor -bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc); - -// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack -bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes); - -// Stall endpoint -void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr); - -// clear stall, data toggle is also reset to DATA0 -void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr); - -//--------------------------------------------------------------------+ -// Event API (Implemented by device stack) -//--------------------------------------------------------------------+ - -// Called by DCD to notify device stack -extern void dcd_event_handler(dcd_event_t const * event, bool in_isr); - -// helper to send bus signal event -extern void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr); - -// helper to send setup received -extern void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr); - -// helper to send transfer complete event -extern void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr); - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_DCD_H_ */ - -/// @} +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +/** \ingroup group_usbd + * \defgroup group_dcd Device Controller Driver (DCD) + * @{ */ + +#ifndef _TUSB_DCD_H_ +#define _TUSB_DCD_H_ + +#include "common/tusb_common.h" + +#ifdef __cplusplus + extern "C" { +#endif + +typedef enum +{ + DCD_EVENT_INVALID = 0, + DCD_EVENT_BUS_RESET, + DCD_EVENT_UNPLUGGED, + DCD_EVENT_SOF, + DCD_EVENT_SUSPEND, // TODO LPM Sleep L1 support + DCD_EVENT_RESUME, + + DCD_EVENT_SETUP_RECEIVED, + DCD_EVENT_XFER_COMPLETE, + + // Not an DCD event, just a convenient way to defer ISR function + USBD_EVENT_FUNC_CALL, + + DCD_EVENT_COUNT +} dcd_eventid_t; + +typedef struct TU_ATTR_ALIGNED(4) +{ + uint8_t rhport; + uint8_t event_id; + + union { + // USBD_EVT_SETUP_RECEIVED + tusb_control_request_t setup_received; + + // USBD_EVT_XFER_COMPLETE + struct { + uint8_t ep_addr; + uint8_t result; + uint32_t len; + }xfer_complete; + + // USBD_EVENT_FUNC_CALL + struct { + void (*func) (void*); + void* param; + }func_call; + }; +} dcd_event_t; + +//TU_VERIFY_STATIC(sizeof(dcd_event_t) <= 12, "size is not correct"); + +/*------------------------------------------------------------------*/ +/* Device API + *------------------------------------------------------------------*/ + +// Initialize controller to device mode +void dcd_init (uint8_t rhport); + +// Interrupt Handler +void dcd_isr (uint8_t rhport); + +// Enable device interrupt +void dcd_int_enable (uint8_t rhport); + +// Disable device interrupt +void dcd_int_disable(uint8_t rhport); + +// Receive Set Address request, mcu port must also include status IN response +void dcd_set_address(uint8_t rhport, uint8_t dev_addr); + +// Receive Set Configure request +void dcd_set_config (uint8_t rhport, uint8_t config_num); + +// Wake up host +void dcd_remote_wakeup(uint8_t rhport); + +//--------------------------------------------------------------------+ +// Endpoint API +//--------------------------------------------------------------------+ + +// Invoked when a control transfer's status stage is complete. +// May help DCD to prepare for next control transfer, this API is optional. +void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request) TU_ATTR_WEAK; + +// Configure endpoint's registers according to descriptor +bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc); + +// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack +bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes); + +// Stall endpoint +void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr); + +// clear stall, data toggle is also reset to DATA0 +void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr); + +//--------------------------------------------------------------------+ +// Event API (Implemented by device stack) +//--------------------------------------------------------------------+ + +// Called by DCD to notify device stack +extern void dcd_event_handler(dcd_event_t const * event, bool in_isr); + +// helper to send bus signal event +extern void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr); + +// helper to send setup received +extern void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr); + +// helper to send transfer complete event +extern void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_DCD_H_ */ + +/// @} diff --git a/src/device/usbd.c b/src/device/usbd.c index fcc623d33..16670b944 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -1,1001 +1,1001 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2019 Ha Thach (tinyusb.org) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * This file is part of the TinyUSB stack. - */ - -#include "tusb_option.h" - -#if TUSB_OPT_DEVICE_ENABLED - -#include "tusb.h" -#include "usbd.h" -#include "device/usbd_pvt.h" -#include "dcd.h" - -#ifndef CFG_TUD_TASK_QUEUE_SZ -#define CFG_TUD_TASK_QUEUE_SZ 16 -#endif - -//--------------------------------------------------------------------+ -// Device Data -//--------------------------------------------------------------------+ -typedef struct { - struct TU_ATTR_PACKED - { - volatile uint8_t connected : 1; - volatile uint8_t configured : 1; - volatile uint8_t suspended : 1; - - uint8_t remote_wakeup_en : 1; // enable/disable by host - uint8_t remote_wakeup_support : 1; // configuration descriptor's attribute - uint8_t self_powered : 1; // configuration descriptor's attribute - }; - - uint8_t itf2drv[16]; // map interface number to driver (0xff is invalid) - uint8_t ep2drv[8][2]; // map endpoint to driver ( 0xff is invalid ) - - struct TU_ATTR_PACKED - { - volatile bool busy : 1; - volatile bool stalled : 1; - - // TODO merge ep2drv here, 4-bit should be sufficient - }ep_status[8][2]; -}usbd_device_t; - -static usbd_device_t _usbd_dev; - -// Invalid driver ID in itf2drv[] ep2drv[][] mapping -enum { DRVID_INVALID = 0xFFu }; - -//--------------------------------------------------------------------+ -// Class Driver -//--------------------------------------------------------------------+ -typedef struct { - uint8_t class_code; - - void (* init ) (void); - void (* reset ) (uint8_t rhport); - bool (* open ) (uint8_t rhport, tusb_desc_interface_t const * desc_intf, uint16_t* p_length); - bool (* control_request ) (uint8_t rhport, tusb_control_request_t const * request); - bool (* control_complete ) (uint8_t rhport, tusb_control_request_t const * request); - bool (* xfer_cb ) (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); - void (* sof ) (uint8_t rhport); -} usbd_class_driver_t; - -static usbd_class_driver_t const _usbd_driver[] = -{ - #if CFG_TUD_CDC - { - .class_code = TUSB_CLASS_CDC, - .init = cdcd_init, - .reset = cdcd_reset, - .open = cdcd_open, - .control_request = cdcd_control_request, - .control_complete = cdcd_control_complete, - .xfer_cb = cdcd_xfer_cb, - .sof = NULL - }, - #endif - - #if CFG_TUD_MSC - { - .class_code = TUSB_CLASS_MSC, - .init = mscd_init, - .reset = mscd_reset, - .open = mscd_open, - .control_request = mscd_control_request, - .control_complete = mscd_control_complete, - .xfer_cb = mscd_xfer_cb, - .sof = NULL - }, - #endif - - #if CFG_TUD_HID - { - .class_code = TUSB_CLASS_HID, - .init = hidd_init, - .reset = hidd_reset, - .open = hidd_open, - .control_request = hidd_control_request, - .control_complete = hidd_control_complete, - .xfer_cb = hidd_xfer_cb, - .sof = NULL - }, - #endif - - #if CFG_TUD_MIDI - { - .class_code = TUSB_CLASS_AUDIO, - .init = midid_init, - .open = midid_open, - .reset = midid_reset, - .control_request = midid_control_request, - .control_complete = midid_control_complete, - .xfer_cb = midid_xfer_cb, - .sof = NULL - }, - #endif - - #if CFG_TUD_VENDOR - { - .class_code = TUSB_CLASS_VENDOR_SPECIFIC, - .init = vendord_init, - .reset = vendord_reset, - .open = vendord_open, - .control_request = tud_vendor_control_request_cb, - .control_complete = tud_vendor_control_complete_cb, - .xfer_cb = vendord_xfer_cb, - .sof = NULL - }, - #endif - - #if CFG_TUD_USBTMC - // Presently USBTMC is the only defined class with the APP_SPECIFIC class code. - // We maybe need to add subclass codes here, or a callback to ask if a driver can - // handle a particular interface. - { - .class_code = TUD_USBTMC_APP_CLASS, - //.subclass_code = TUD_USBTMC_APP_SUBCLASS - .init = usbtmcd_init_cb, - .reset = usbtmcd_reset_cb, - .open = usbtmcd_open_cb, - .control_request = usbtmcd_control_request_cb, - .control_complete = usbtmcd_control_complete_cb, - .xfer_cb = usbtmcd_xfer_cb, - .sof = NULL - }, - #endif - - #if CFG_TUD_DFU_RT - { - .class_code = TUD_DFU_APP_CLASS, - //.subclass_code = TUD_DFU_APP_SUBCLASS - .init = dfu_rtd_init, - .reset = dfu_rtd_reset, - .open = dfu_rtd_open, - .control_request = dfu_rtd_control_request, - .control_complete = dfu_rtd_control_complete, - .xfer_cb = dfu_rtd_xfer_cb, - .sof = NULL - }, - #endif -}; - -enum { USBD_CLASS_DRIVER_COUNT = TU_ARRAY_SIZE(_usbd_driver) }; - -//--------------------------------------------------------------------+ -// DCD Event -//--------------------------------------------------------------------+ - -// Event queue -// OPT_MODE_DEVICE is used by OS NONE for mutex (disable usb isr) -OSAL_QUEUE_DEF(OPT_MODE_DEVICE, _usbd_qdef, CFG_TUD_TASK_QUEUE_SZ, dcd_event_t); -static osal_queue_t _usbd_q; - -//--------------------------------------------------------------------+ -// Prototypes -//--------------------------------------------------------------------+ -static void mark_interface_endpoint(uint8_t ep2drv[8][2], uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id); -static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request); -static bool process_set_config(uint8_t rhport, uint8_t cfg_num); -static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request); - -void usbd_control_reset(void); -void usbd_control_set_request(tusb_control_request_t const *request); -void usbd_control_set_complete_callback( bool (*fp) (uint8_t, tusb_control_request_t const * ) ); -bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); - - -//--------------------------------------------------------------------+ -// Debugging -//--------------------------------------------------------------------+ -#if CFG_TUSB_DEBUG >= 2 -static char const* const _usbd_event_str[DCD_EVENT_COUNT] = -{ - "INVALID" , - "BUS_RESET" , - "UNPLUGGED" , - "SOF" , - "SUSPEND" , - "RESUME" , - "SETUP_RECEIVED" , - "XFER_COMPLETE" , - "FUNC_CALL" -}; - -// must be same driver order as usbd_class_drivers[] -static char const* const _usbd_driver_str[USBD_CLASS_DRIVER_COUNT] = -{ - #if CFG_TUD_CDC - "CDC", - #endif - #if CFG_TUD_MSC - "MSC", - #endif - #if CFG_TUD_HID - "HID", - #endif - #if CFG_TUD_MIDI - "MIDI", - #endif - #if CFG_TUD_VENDOR - "Vendor", - #endif - #if CFG_TUD_USBTMC - "USBTMC" - #endif -}; - -static char const* const _tusb_std_request_str[] = -{ - "Get Status" , - "Clear Feature" , - "Reserved" , - "Set Feature" , - "Reserved" , - "Set Address" , - "Get Descriptor" , - "Set Descriptor" , - "Get Configuration" , - "Set Configuration" , - "Get Interface" , - "Set Interface" , - "Synch Frame" -}; - -#endif - -//--------------------------------------------------------------------+ -// Application API -//--------------------------------------------------------------------+ -bool tud_mounted(void) -{ - return _usbd_dev.configured; -} - -bool tud_suspended(void) -{ - return _usbd_dev.suspended; -} - -bool tud_remote_wakeup(void) -{ - // only wake up host if this feature is supported and enabled and we are suspended - TU_VERIFY (_usbd_dev.suspended && _usbd_dev.remote_wakeup_support && _usbd_dev.remote_wakeup_en ); - dcd_remote_wakeup(TUD_OPT_RHPORT); - return true; -} - -//--------------------------------------------------------------------+ -// USBD Task -//--------------------------------------------------------------------+ -bool tud_init (void) -{ - TU_LOG2("USBD init\r\n"); - - tu_varclr(&_usbd_dev); - - // Init device queue & task - _usbd_q = osal_queue_create(&_usbd_qdef); - TU_ASSERT(_usbd_q != NULL); - - // Init class drivers - for (uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++) - { - TU_LOG2("%s init\r\n", _usbd_driver_str[i]); - _usbd_driver[i].init(); - } - - // Init device controller driver - dcd_init(TUD_OPT_RHPORT); - dcd_int_enable(TUD_OPT_RHPORT); - - return true; -} - -static void usbd_reset(uint8_t rhport) -{ - tu_varclr(&_usbd_dev); - - memset(_usbd_dev.itf2drv, DRVID_INVALID, sizeof(_usbd_dev.itf2drv)); // invalid mapping - memset(_usbd_dev.ep2drv , DRVID_INVALID, sizeof(_usbd_dev.ep2drv )); // invalid mapping - - usbd_control_reset(); - - for (uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++) - { - if ( _usbd_driver[i].reset ) _usbd_driver[i].reset( rhport ); - } -} - -/* USB Device Driver task - * This top level thread manages all device controller event and delegates events to class-specific drivers. - * This should be called periodically within the mainloop or rtos thread. - * - @code - int main(void) - { - application_init(); - tusb_init(); - - while(1) // the mainloop - { - application_code(); - tud_task(); // tinyusb device task - } - } - @endcode - */ -void tud_task (void) -{ - // Skip if stack is not initialized - if ( !tusb_inited() ) return; - - // Loop until there is no more events in the queue - while (1) - { - dcd_event_t event; - - if ( !osal_queue_receive(_usbd_q, &event) ) return; - - TU_LOG2("USBD: event %s\r\n", event.event_id < DCD_EVENT_COUNT ? _usbd_event_str[event.event_id] : "CORRUPTED"); - - switch ( event.event_id ) - { - case DCD_EVENT_BUS_RESET: - usbd_reset(event.rhport); - break; - - case DCD_EVENT_UNPLUGGED: - usbd_reset(event.rhport); - - // invoke callback - if (tud_umount_cb) tud_umount_cb(); - break; - - case DCD_EVENT_SETUP_RECEIVED: - TU_LOG2_MEM(&event.setup_received, 8, 2); - - // Mark as connected after receiving 1st setup packet. - // But it is easier to set it every time instead of wasting time to check then set - _usbd_dev.connected = 1; - - // Process control request - if ( !process_control_request(event.rhport, &event.setup_received) ) - { - TU_LOG2(" Stall EP0\r\n"); - // Failed -> stall both control endpoint IN and OUT - dcd_edpt_stall(event.rhport, 0); - dcd_edpt_stall(event.rhport, 0 | TUSB_DIR_IN_MASK); - } - break; - - case DCD_EVENT_XFER_COMPLETE: - { - // Invoke the class callback associated with the endpoint address - uint8_t const ep_addr = event.xfer_complete.ep_addr; - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const ep_dir = tu_edpt_dir(ep_addr); - - TU_LOG2(" Endpoint: 0x%02X, Bytes: %ld\r\n", ep_addr, event.xfer_complete.len); - - _usbd_dev.ep_status[epnum][ep_dir].busy = false; - - if ( 0 == epnum ) - { - usbd_control_xfer_cb(event.rhport, ep_addr, event.xfer_complete.result, event.xfer_complete.len); - } - else - { - uint8_t const drv_id = _usbd_dev.ep2drv[epnum][ep_dir]; - TU_ASSERT(drv_id < USBD_CLASS_DRIVER_COUNT,); - - TU_LOG2(" %s xfer callback\r\n", _usbd_driver_str[drv_id]); - _usbd_driver[drv_id].xfer_cb(event.rhport, ep_addr, event.xfer_complete.result, event.xfer_complete.len); - } - } - break; - - case DCD_EVENT_SUSPEND: - if (tud_suspend_cb) tud_suspend_cb(_usbd_dev.remote_wakeup_en); - break; - - case DCD_EVENT_RESUME: - if (tud_resume_cb) tud_resume_cb(); - break; - - case DCD_EVENT_SOF: - for ( uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++ ) - { - if ( _usbd_driver[i].sof ) - { - _usbd_driver[i].sof(event.rhport); - } - } - break; - - case USBD_EVENT_FUNC_CALL: - if ( event.func_call.func ) event.func_call.func(event.func_call.param); - break; - - default: - TU_BREAKPOINT(); - break; - } - } -} - -//--------------------------------------------------------------------+ -// Control Request Parser & Handling -//--------------------------------------------------------------------+ - -// Helper to invoke class driver control request handler -static bool invoke_class_control(uint8_t rhport, uint8_t drvid, tusb_control_request_t const * request) -{ - TU_ASSERT(_usbd_driver[drvid].control_request); - - usbd_control_set_complete_callback(_usbd_driver[drvid].control_complete); - TU_LOG2(" %s control request\r\n", _usbd_driver_str[drvid]); - return _usbd_driver[drvid].control_request(rhport, request); -} - -// This handles the actual request and its response. -// return false will cause its caller to stall control endpoint -static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request) -{ - usbd_control_set_complete_callback(NULL); - - TU_ASSERT(p_request->bmRequestType_bit.type < TUSB_REQ_TYPE_INVALID); - - // Vendor request - if ( p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR ) - { - TU_VERIFY(tud_vendor_control_request_cb); - - if (tud_vendor_control_complete_cb) usbd_control_set_complete_callback(tud_vendor_control_complete_cb); - return tud_vendor_control_request_cb(rhport, p_request); - } - -#if CFG_TUSB_DEBUG > 1 - if (TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type && p_request->bRequest <= TUSB_REQ_SYNCH_FRAME) - { - TU_LOG2(" %s\r\n", _tusb_std_request_str[p_request->bRequest]); - } -#endif - - switch ( p_request->bmRequestType_bit.recipient ) - { - //------------- Device Requests e.g in enumeration -------------// - case TUSB_REQ_RCPT_DEVICE: - if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type ) - { - // Non standard request is not supported - TU_BREAKPOINT(); - return false; - } - - switch ( p_request->bRequest ) - { - case TUSB_REQ_SET_ADDRESS: - // Depending on mcu, status phase could be sent either before or after changing device address, - // or even require stack to not response with status at all - // Therefore DCD must take full responsibility to response and include zlp status packet if needed. - usbd_control_set_request(p_request); // set request since DCD has no access to tud_control_status() API - dcd_set_address(rhport, (uint8_t) p_request->wValue); - // skip tud_control_status() - break; - - case TUSB_REQ_GET_CONFIGURATION: - { - uint8_t cfgnum = _usbd_dev.configured ? 1 : 0; - tud_control_xfer(rhport, p_request, &cfgnum, 1); - } - break; - - case TUSB_REQ_SET_CONFIGURATION: - { - uint8_t const cfg_num = (uint8_t) p_request->wValue; - - dcd_set_config(rhport, cfg_num); - - if ( !_usbd_dev.configured && cfg_num ) TU_ASSERT( process_set_config(rhport, cfg_num) ); - - _usbd_dev.configured = cfg_num ? 1 : 0; - - tud_control_status(rhport, p_request); - } - break; - - case TUSB_REQ_GET_DESCRIPTOR: - TU_VERIFY( process_get_descriptor(rhport, p_request) ); - break; - - case TUSB_REQ_SET_FEATURE: - // Only support remote wakeup for device feature - TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue); - - // Host may enable remote wake up before suspending especially HID device - _usbd_dev.remote_wakeup_en = true; - tud_control_status(rhport, p_request); - break; - - case TUSB_REQ_CLEAR_FEATURE: - // Only support remote wakeup for device feature - TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue); - - // Host may disable remote wake up after resuming - _usbd_dev.remote_wakeup_en = false; - tud_control_status(rhport, p_request); - break; - - case TUSB_REQ_GET_STATUS: - { - // Device status bit mask - // - Bit 0: Self Powered - // - Bit 1: Remote Wakeup enabled - uint16_t status = (_usbd_dev.self_powered ? 1 : 0) | (_usbd_dev.remote_wakeup_en ? 2 : 0); - tud_control_xfer(rhport, p_request, &status, 2); - } - break; - - // Unknown/Unsupported request - default: TU_BREAKPOINT(); return false; - } - break; - - //------------- Class/Interface Specific Request -------------// - case TUSB_REQ_RCPT_INTERFACE: - { - uint8_t const itf = tu_u16_low(p_request->wIndex); - TU_VERIFY(itf < TU_ARRAY_SIZE(_usbd_dev.itf2drv)); - - uint8_t const drvid = _usbd_dev.itf2drv[itf]; - TU_VERIFY(drvid < USBD_CLASS_DRIVER_COUNT); - - if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) - { - switch ( p_request->bRequest ) - { - case TUSB_REQ_GET_INTERFACE: - { - // TODO not support alternate interface yet - uint8_t alternate = 0; - tud_control_xfer(rhport, p_request, &alternate, 1); - } - break; - - case TUSB_REQ_SET_INTERFACE: - { - uint8_t const alternate = (uint8_t) p_request->wValue; - - // TODO not support alternate interface yet - TU_ASSERT(alternate == 0); - tud_control_status(rhport, p_request); - } - break; - - default: - // forward to class driver: "STD request to Interface" - // GET HID REPORT DESCRIPTOR falls into this case - TU_VERIFY(invoke_class_control(rhport, drvid, p_request)); - break; - } - }else - { - // forward to class driver: "non-STD request to Interface" - TU_VERIFY(invoke_class_control(rhport, drvid, p_request)); - } - } - break; - - //------------- Endpoint Request -------------// - case TUSB_REQ_RCPT_ENDPOINT: - { - uint8_t const ep_addr = tu_u16_low(p_request->wIndex); - uint8_t const ep_num = tu_edpt_number(ep_addr); - uint8_t const ep_dir = tu_edpt_dir(ep_addr); - - TU_ASSERT(ep_num < TU_ARRAY_SIZE(_usbd_dev.ep2drv) ); - - uint8_t const drvid = _usbd_dev.ep2drv[ep_num][ep_dir]; - - bool ret = false; - - // Handle STD request to endpoint - if ( TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type ) - { - // force return true for standard request - ret = true; - - switch ( p_request->bRequest ) - { - case TUSB_REQ_GET_STATUS: - { - uint16_t status = usbd_edpt_stalled(rhport, ep_addr) ? 0x0001 : 0x0000; - tud_control_xfer(rhport, p_request, &status, 2); - } - break; - - case TUSB_REQ_CLEAR_FEATURE: - if ( TUSB_REQ_FEATURE_EDPT_HALT == p_request->wValue ) - { - usbd_edpt_clear_stall(rhport, ep_addr); - } - tud_control_status(rhport, p_request); - break; - - case TUSB_REQ_SET_FEATURE: - if ( TUSB_REQ_FEATURE_EDPT_HALT == p_request->wValue ) - { - usbd_edpt_stall(rhport, ep_addr); - } - tud_control_status(rhport, p_request); - break; - - // Unknown/Unsupported request - default: TU_BREAKPOINT(); return false; - } - } - - if (drvid < 0xFF) { - TU_ASSERT(drvid < USBD_CLASS_DRIVER_COUNT); - - // Some classes such as USBTMC needs to clear/re-init its buffer when receiving CLEAR_FEATURE request - // We will forward all request targeted endpoint to class drivers after - // - For class-type requests: driver is fully responsible to reply to host - // - For std-type requests : driver init/re-init internal variable/buffer only, and - // must not call tud_control_status(), driver's return value will have no effect. - // EP state has already affected (stalled/cleared) - if ( invoke_class_control(rhport, drvid, p_request) ) ret = true; - } - - if ( TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type ) - { - // Set complete callback = NULL since it can also stall the request. - usbd_control_set_complete_callback(NULL); - } - - return ret; - } - break; - - // Unknown recipient - default: TU_BREAKPOINT(); return false; - } - - return true; -} - -// Process Set Configure Request -// This function parse configuration descriptor & open drivers accordingly -static bool process_set_config(uint8_t rhport, uint8_t cfg_num) -{ - tusb_desc_configuration_t const * desc_cfg = (tusb_desc_configuration_t const *) tud_descriptor_configuration_cb(cfg_num-1); // index is cfg_num-1 - TU_ASSERT(desc_cfg != NULL && desc_cfg->bDescriptorType == TUSB_DESC_CONFIGURATION); - - // Parse configuration descriptor - _usbd_dev.remote_wakeup_support = (desc_cfg->bmAttributes & TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP) ? 1 : 0; - _usbd_dev.self_powered = (desc_cfg->bmAttributes & TUSB_DESC_CONFIG_ATT_SELF_POWERED) ? 1 : 0; - - // Parse interface descriptor - uint8_t const * p_desc = ((uint8_t const*) desc_cfg) + sizeof(tusb_desc_configuration_t); - uint8_t const * desc_end = ((uint8_t const*) desc_cfg) + desc_cfg->wTotalLength; - - while( p_desc < desc_end ) - { - // Each interface always starts with Interface or Association descriptor - if ( TUSB_DESC_INTERFACE_ASSOCIATION == tu_desc_type(p_desc) ) - { - p_desc = tu_desc_next(p_desc); // ignore Interface Association - }else - { - TU_ASSERT( TUSB_DESC_INTERFACE == tu_desc_type(p_desc) ); - - tusb_desc_interface_t* desc_itf = (tusb_desc_interface_t*) p_desc; - - // Check if class is supported - uint8_t drv_id; - for (drv_id = 0; drv_id < USBD_CLASS_DRIVER_COUNT; drv_id++) - { - if ( _usbd_driver[drv_id].class_code == desc_itf->bInterfaceClass ) break; - } - TU_ASSERT( drv_id < USBD_CLASS_DRIVER_COUNT ); - - // Interface number must not be used already TODO alternate interface - TU_ASSERT( DRVID_INVALID == _usbd_dev.itf2drv[desc_itf->bInterfaceNumber] ); - _usbd_dev.itf2drv[desc_itf->bInterfaceNumber] = drv_id; - - uint16_t itf_len=0; - TU_LOG2(" %s open\r\n", _usbd_driver_str[drv_id]); - TU_ASSERT( _usbd_driver[drv_id].open(rhport, desc_itf, &itf_len) ); - TU_ASSERT( itf_len >= sizeof(tusb_desc_interface_t) ); - - mark_interface_endpoint(_usbd_dev.ep2drv, p_desc, itf_len, drv_id); - - p_desc += itf_len; // next interface - } - } - - // invoke callback - if (tud_mount_cb) tud_mount_cb(); - - return true; -} - -// Helper marking endpoint of interface belongs to class driver -static void mark_interface_endpoint(uint8_t ep2drv[8][2], uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id) -{ - uint16_t len = 0; - - while( len < desc_len ) - { - if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) ) - { - uint8_t const ep_addr = ((tusb_desc_endpoint_t const*) p_desc)->bEndpointAddress; - - ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)] = driver_id; - } - - len = (uint16_t)(len + tu_desc_len(p_desc)); - p_desc = tu_desc_next(p_desc); - } -} - -// return descriptor's buffer and update desc_len -static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request) -{ - tusb_desc_type_t const desc_type = (tusb_desc_type_t) tu_u16_high(p_request->wValue); - uint8_t const desc_index = tu_u16_low( p_request->wValue ); - - switch(desc_type) - { - case TUSB_DESC_DEVICE: - return tud_control_xfer(rhport, p_request, (void*) tud_descriptor_device_cb(), sizeof(tusb_desc_device_t)); - break; - - case TUSB_DESC_BOS: - { - // requested by host if USB > 2.0 ( i.e 2.1 or 3.x ) - if (!tud_descriptor_bos_cb) return false; - - tusb_desc_bos_t const* desc_bos = (tusb_desc_bos_t const*) tud_descriptor_bos_cb(); - uint16_t total_len; - memcpy(&total_len, &desc_bos->wTotalLength, 2); // possibly mis-aligned memory - - return tud_control_xfer(rhport, p_request, (void*) desc_bos, total_len); - } - break; - - case TUSB_DESC_CONFIGURATION: - { - tusb_desc_configuration_t const* desc_config = (tusb_desc_configuration_t const*) tud_descriptor_configuration_cb(desc_index); - TU_ASSERT(desc_config); - - uint16_t total_len; - memcpy(&total_len, &desc_config->wTotalLength, 2); // possibly mis-aligned memory - - return tud_control_xfer(rhport, p_request, (void*) desc_config, total_len); - } - break; - - case TUSB_DESC_STRING: - // String Descriptor always uses the desc set from user - if ( desc_index == 0xEE ) - { - // The 0xEE index string is a Microsoft OS Descriptors. - // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors - return false; - }else - { - uint8_t const* desc_str = (uint8_t const*) tud_descriptor_string_cb(desc_index); - TU_ASSERT(desc_str); - - // first byte of descriptor is its size - return tud_control_xfer(rhport, p_request, (void*) desc_str, desc_str[0]); - } - break; - - case TUSB_DESC_DEVICE_QUALIFIER: - // TODO If not highspeed capable stall this request otherwise - // return the descriptor that could work in highspeed - return false; - break; - - default: return false; - } - - return true; -} - -//--------------------------------------------------------------------+ -// DCD Event Handler -//--------------------------------------------------------------------+ -void dcd_event_handler(dcd_event_t const * event, bool in_isr) -{ - switch (event->event_id) - { - case DCD_EVENT_UNPLUGGED: - _usbd_dev.connected = 0; - _usbd_dev.configured = 0; - _usbd_dev.suspended = 0; - osal_queue_send(_usbd_q, event, in_isr); - break; - - case DCD_EVENT_SOF: - return; // skip SOF event for now - break; - - case DCD_EVENT_SUSPEND: - // NOTE: When plugging/unplugging device, the D+/D- state are unstable and can accidentally meet the - // SUSPEND condition ( Idle for 3ms ). Some MCUs such as SAMD doesn't distinguish suspend vs disconnect as well. - // We will skip handling SUSPEND/RESUME event if not currently connected - if ( _usbd_dev.connected ) - { - _usbd_dev.suspended = 1; - osal_queue_send(_usbd_q, event, in_isr); - } - break; - - case DCD_EVENT_RESUME: - // skip event if not connected (especially required for SAMD) - if ( _usbd_dev.connected ) - { - _usbd_dev.suspended = 0; - osal_queue_send(_usbd_q, event, in_isr); - } - break; - - default: - osal_queue_send(_usbd_q, event, in_isr); - break; - } -} - -void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr) -{ - dcd_event_t event = { .rhport = rhport, .event_id = eid, }; - dcd_event_handler(&event, in_isr); -} - -void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr) -{ - dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SETUP_RECEIVED }; - memcpy(&event.setup_received, setup, 8); - - dcd_event_handler(&event, in_isr); -} - -void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr) -{ - dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_XFER_COMPLETE }; - - event.xfer_complete.ep_addr = ep_addr; - event.xfer_complete.len = xferred_bytes; - event.xfer_complete.result = result; - - dcd_event_handler(&event, in_isr); -} - -//--------------------------------------------------------------------+ -// Helper -//--------------------------------------------------------------------+ - -// Parse consecutive endpoint descriptors (IN & OUT) -bool usbd_open_edpt_pair(uint8_t rhport, uint8_t const* p_desc, uint8_t ep_count, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in) -{ - for(int i=0; ibDescriptorType && xfer_type == desc_ep->bmAttributes.xfer); - TU_ASSERT(dcd_edpt_open(rhport, desc_ep)); - - if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN ) - { - (*ep_in) = desc_ep->bEndpointAddress; - }else - { - (*ep_out) = desc_ep->bEndpointAddress; - } - - p_desc = tu_desc_next(p_desc); - } - - return true; -} - -// Helper to defer an isr function -void usbd_defer_func(osal_task_func_t func, void* param, bool in_isr) -{ - dcd_event_t event = - { - .rhport = 0, - .event_id = USBD_EVENT_FUNC_CALL, - }; - - event.func_call.func = func; - event.func_call.param = param; - - dcd_event_handler(&event, in_isr); -} - -//--------------------------------------------------------------------+ -// USBD Endpoint API -//--------------------------------------------------------------------+ - -bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) -{ - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - TU_VERIFY( dcd_edpt_xfer(rhport, ep_addr, buffer, total_bytes) ); - _usbd_dev.ep_status[epnum][dir].busy = true; - - TU_LOG2(" XFER Endpoint: 0x%02X, Bytes: %d\r\n", ep_addr, total_bytes); - - return true; -} - -bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr) -{ - (void) rhport; - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - return _usbd_dev.ep_status[epnum][dir].busy; -} - -void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr) -{ - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - dcd_edpt_stall(rhport, ep_addr); - _usbd_dev.ep_status[epnum][dir].stalled = true; - _usbd_dev.ep_status[epnum][dir].busy = true; -} - -void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) -{ - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - dcd_edpt_clear_stall(rhport, ep_addr); - _usbd_dev.ep_status[epnum][dir].stalled = false; - _usbd_dev.ep_status[epnum][dir].busy = false; -} - -bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr) -{ - (void) rhport; - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - return _usbd_dev.ep_status[epnum][dir].stalled; -} - -#endif +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if TUSB_OPT_DEVICE_ENABLED + +#include "tusb.h" +#include "usbd.h" +#include "device/usbd_pvt.h" +#include "dcd.h" + +#ifndef CFG_TUD_TASK_QUEUE_SZ +#define CFG_TUD_TASK_QUEUE_SZ 16 +#endif + +//--------------------------------------------------------------------+ +// Device Data +//--------------------------------------------------------------------+ +typedef struct { + struct TU_ATTR_PACKED + { + volatile uint8_t connected : 1; + volatile uint8_t configured : 1; + volatile uint8_t suspended : 1; + + uint8_t remote_wakeup_en : 1; // enable/disable by host + uint8_t remote_wakeup_support : 1; // configuration descriptor's attribute + uint8_t self_powered : 1; // configuration descriptor's attribute + }; + + uint8_t itf2drv[16]; // map interface number to driver (0xff is invalid) + uint8_t ep2drv[8][2]; // map endpoint to driver ( 0xff is invalid ) + + struct TU_ATTR_PACKED + { + volatile bool busy : 1; + volatile bool stalled : 1; + + // TODO merge ep2drv here, 4-bit should be sufficient + }ep_status[8][2]; +}usbd_device_t; + +static usbd_device_t _usbd_dev; + +// Invalid driver ID in itf2drv[] ep2drv[][] mapping +enum { DRVID_INVALID = 0xFFu }; + +//--------------------------------------------------------------------+ +// Class Driver +//--------------------------------------------------------------------+ +typedef struct { + uint8_t class_code; + + void (* init ) (void); + void (* reset ) (uint8_t rhport); + bool (* open ) (uint8_t rhport, tusb_desc_interface_t const * desc_intf, uint16_t* p_length); + bool (* control_request ) (uint8_t rhport, tusb_control_request_t const * request); + bool (* control_complete ) (uint8_t rhport, tusb_control_request_t const * request); + bool (* xfer_cb ) (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); + void (* sof ) (uint8_t rhport); +} usbd_class_driver_t; + +static usbd_class_driver_t const _usbd_driver[] = +{ + #if CFG_TUD_CDC + { + .class_code = TUSB_CLASS_CDC, + .init = cdcd_init, + .reset = cdcd_reset, + .open = cdcd_open, + .control_request = cdcd_control_request, + .control_complete = cdcd_control_complete, + .xfer_cb = cdcd_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_MSC + { + .class_code = TUSB_CLASS_MSC, + .init = mscd_init, + .reset = mscd_reset, + .open = mscd_open, + .control_request = mscd_control_request, + .control_complete = mscd_control_complete, + .xfer_cb = mscd_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_HID + { + .class_code = TUSB_CLASS_HID, + .init = hidd_init, + .reset = hidd_reset, + .open = hidd_open, + .control_request = hidd_control_request, + .control_complete = hidd_control_complete, + .xfer_cb = hidd_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_MIDI + { + .class_code = TUSB_CLASS_AUDIO, + .init = midid_init, + .open = midid_open, + .reset = midid_reset, + .control_request = midid_control_request, + .control_complete = midid_control_complete, + .xfer_cb = midid_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_VENDOR + { + .class_code = TUSB_CLASS_VENDOR_SPECIFIC, + .init = vendord_init, + .reset = vendord_reset, + .open = vendord_open, + .control_request = tud_vendor_control_request_cb, + .control_complete = tud_vendor_control_complete_cb, + .xfer_cb = vendord_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_USBTMC + // Presently USBTMC is the only defined class with the APP_SPECIFIC class code. + // We maybe need to add subclass codes here, or a callback to ask if a driver can + // handle a particular interface. + { + .class_code = TUD_USBTMC_APP_CLASS, + //.subclass_code = TUD_USBTMC_APP_SUBCLASS + .init = usbtmcd_init_cb, + .reset = usbtmcd_reset_cb, + .open = usbtmcd_open_cb, + .control_request = usbtmcd_control_request_cb, + .control_complete = usbtmcd_control_complete_cb, + .xfer_cb = usbtmcd_xfer_cb, + .sof = NULL + }, + #endif + + #if CFG_TUD_DFU_RT + { + .class_code = TUD_DFU_APP_CLASS, + //.subclass_code = TUD_DFU_APP_SUBCLASS + .init = dfu_rtd_init, + .reset = dfu_rtd_reset, + .open = dfu_rtd_open, + .control_request = dfu_rtd_control_request, + .control_complete = dfu_rtd_control_complete, + .xfer_cb = dfu_rtd_xfer_cb, + .sof = NULL + }, + #endif +}; + +enum { USBD_CLASS_DRIVER_COUNT = TU_ARRAY_SIZE(_usbd_driver) }; + +//--------------------------------------------------------------------+ +// DCD Event +//--------------------------------------------------------------------+ + +// Event queue +// OPT_MODE_DEVICE is used by OS NONE for mutex (disable usb isr) +OSAL_QUEUE_DEF(OPT_MODE_DEVICE, _usbd_qdef, CFG_TUD_TASK_QUEUE_SZ, dcd_event_t); +static osal_queue_t _usbd_q; + +//--------------------------------------------------------------------+ +// Prototypes +//--------------------------------------------------------------------+ +static void mark_interface_endpoint(uint8_t ep2drv[8][2], uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id); +static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request); +static bool process_set_config(uint8_t rhport, uint8_t cfg_num); +static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request); + +void usbd_control_reset(void); +void usbd_control_set_request(tusb_control_request_t const *request); +void usbd_control_set_complete_callback( bool (*fp) (uint8_t, tusb_control_request_t const * ) ); +bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); + + +//--------------------------------------------------------------------+ +// Debugging +//--------------------------------------------------------------------+ +#if CFG_TUSB_DEBUG >= 2 +static char const* const _usbd_event_str[DCD_EVENT_COUNT] = +{ + "INVALID" , + "BUS_RESET" , + "UNPLUGGED" , + "SOF" , + "SUSPEND" , + "RESUME" , + "SETUP_RECEIVED" , + "XFER_COMPLETE" , + "FUNC_CALL" +}; + +// must be same driver order as usbd_class_drivers[] +static char const* const _usbd_driver_str[USBD_CLASS_DRIVER_COUNT] = +{ + #if CFG_TUD_CDC + "CDC", + #endif + #if CFG_TUD_MSC + "MSC", + #endif + #if CFG_TUD_HID + "HID", + #endif + #if CFG_TUD_MIDI + "MIDI", + #endif + #if CFG_TUD_VENDOR + "Vendor", + #endif + #if CFG_TUD_USBTMC + "USBTMC" + #endif +}; + +static char const* const _tusb_std_request_str[] = +{ + "Get Status" , + "Clear Feature" , + "Reserved" , + "Set Feature" , + "Reserved" , + "Set Address" , + "Get Descriptor" , + "Set Descriptor" , + "Get Configuration" , + "Set Configuration" , + "Get Interface" , + "Set Interface" , + "Synch Frame" +}; + +#endif + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ +bool tud_mounted(void) +{ + return _usbd_dev.configured; +} + +bool tud_suspended(void) +{ + return _usbd_dev.suspended; +} + +bool tud_remote_wakeup(void) +{ + // only wake up host if this feature is supported and enabled and we are suspended + TU_VERIFY (_usbd_dev.suspended && _usbd_dev.remote_wakeup_support && _usbd_dev.remote_wakeup_en ); + dcd_remote_wakeup(TUD_OPT_RHPORT); + return true; +} + +//--------------------------------------------------------------------+ +// USBD Task +//--------------------------------------------------------------------+ +bool tud_init (void) +{ + TU_LOG2("USBD init\r\n"); + + tu_varclr(&_usbd_dev); + + // Init device queue & task + _usbd_q = osal_queue_create(&_usbd_qdef); + TU_ASSERT(_usbd_q != NULL); + + // Init class drivers + for (uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++) + { + TU_LOG2("%s init\r\n", _usbd_driver_str[i]); + _usbd_driver[i].init(); + } + + // Init device controller driver + dcd_init(TUD_OPT_RHPORT); + dcd_int_enable(TUD_OPT_RHPORT); + + return true; +} + +static void usbd_reset(uint8_t rhport) +{ + tu_varclr(&_usbd_dev); + + memset(_usbd_dev.itf2drv, DRVID_INVALID, sizeof(_usbd_dev.itf2drv)); // invalid mapping + memset(_usbd_dev.ep2drv , DRVID_INVALID, sizeof(_usbd_dev.ep2drv )); // invalid mapping + + usbd_control_reset(); + + for (uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++) + { + if ( _usbd_driver[i].reset ) _usbd_driver[i].reset( rhport ); + } +} + +/* USB Device Driver task + * This top level thread manages all device controller event and delegates events to class-specific drivers. + * This should be called periodically within the mainloop or rtos thread. + * + @code + int main(void) + { + application_init(); + tusb_init(); + + while(1) // the mainloop + { + application_code(); + tud_task(); // tinyusb device task + } + } + @endcode + */ +void tud_task (void) +{ + // Skip if stack is not initialized + if ( !tusb_inited() ) return; + + // Loop until there is no more events in the queue + while (1) + { + dcd_event_t event; + + if ( !osal_queue_receive(_usbd_q, &event) ) return; + + TU_LOG2("USBD: event %s\r\n", event.event_id < DCD_EVENT_COUNT ? _usbd_event_str[event.event_id] : "CORRUPTED"); + + switch ( event.event_id ) + { + case DCD_EVENT_BUS_RESET: + usbd_reset(event.rhport); + break; + + case DCD_EVENT_UNPLUGGED: + usbd_reset(event.rhport); + + // invoke callback + if (tud_umount_cb) tud_umount_cb(); + break; + + case DCD_EVENT_SETUP_RECEIVED: + TU_LOG2_MEM(&event.setup_received, 8, 2); + + // Mark as connected after receiving 1st setup packet. + // But it is easier to set it every time instead of wasting time to check then set + _usbd_dev.connected = 1; + + // Process control request + if ( !process_control_request(event.rhport, &event.setup_received) ) + { + TU_LOG2(" Stall EP0\r\n"); + // Failed -> stall both control endpoint IN and OUT + dcd_edpt_stall(event.rhport, 0); + dcd_edpt_stall(event.rhport, 0 | TUSB_DIR_IN_MASK); + } + break; + + case DCD_EVENT_XFER_COMPLETE: + { + // Invoke the class callback associated with the endpoint address + uint8_t const ep_addr = event.xfer_complete.ep_addr; + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const ep_dir = tu_edpt_dir(ep_addr); + + TU_LOG2(" Endpoint: 0x%02X, Bytes: %ld\r\n", ep_addr, event.xfer_complete.len); + + _usbd_dev.ep_status[epnum][ep_dir].busy = false; + + if ( 0 == epnum ) + { + usbd_control_xfer_cb(event.rhport, ep_addr, event.xfer_complete.result, event.xfer_complete.len); + } + else + { + uint8_t const drv_id = _usbd_dev.ep2drv[epnum][ep_dir]; + TU_ASSERT(drv_id < USBD_CLASS_DRIVER_COUNT,); + + TU_LOG2(" %s xfer callback\r\n", _usbd_driver_str[drv_id]); + _usbd_driver[drv_id].xfer_cb(event.rhport, ep_addr, event.xfer_complete.result, event.xfer_complete.len); + } + } + break; + + case DCD_EVENT_SUSPEND: + if (tud_suspend_cb) tud_suspend_cb(_usbd_dev.remote_wakeup_en); + break; + + case DCD_EVENT_RESUME: + if (tud_resume_cb) tud_resume_cb(); + break; + + case DCD_EVENT_SOF: + for ( uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++ ) + { + if ( _usbd_driver[i].sof ) + { + _usbd_driver[i].sof(event.rhport); + } + } + break; + + case USBD_EVENT_FUNC_CALL: + if ( event.func_call.func ) event.func_call.func(event.func_call.param); + break; + + default: + TU_BREAKPOINT(); + break; + } + } +} + +//--------------------------------------------------------------------+ +// Control Request Parser & Handling +//--------------------------------------------------------------------+ + +// Helper to invoke class driver control request handler +static bool invoke_class_control(uint8_t rhport, uint8_t drvid, tusb_control_request_t const * request) +{ + TU_ASSERT(_usbd_driver[drvid].control_request); + + usbd_control_set_complete_callback(_usbd_driver[drvid].control_complete); + TU_LOG2(" %s control request\r\n", _usbd_driver_str[drvid]); + return _usbd_driver[drvid].control_request(rhport, request); +} + +// This handles the actual request and its response. +// return false will cause its caller to stall control endpoint +static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request) +{ + usbd_control_set_complete_callback(NULL); + + TU_ASSERT(p_request->bmRequestType_bit.type < TUSB_REQ_TYPE_INVALID); + + // Vendor request + if ( p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR ) + { + TU_VERIFY(tud_vendor_control_request_cb); + + if (tud_vendor_control_complete_cb) usbd_control_set_complete_callback(tud_vendor_control_complete_cb); + return tud_vendor_control_request_cb(rhport, p_request); + } + +#if CFG_TUSB_DEBUG > 1 + if (TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type && p_request->bRequest <= TUSB_REQ_SYNCH_FRAME) + { + TU_LOG2(" %s\r\n", _tusb_std_request_str[p_request->bRequest]); + } +#endif + + switch ( p_request->bmRequestType_bit.recipient ) + { + //------------- Device Requests e.g in enumeration -------------// + case TUSB_REQ_RCPT_DEVICE: + if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type ) + { + // Non standard request is not supported + TU_BREAKPOINT(); + return false; + } + + switch ( p_request->bRequest ) + { + case TUSB_REQ_SET_ADDRESS: + // Depending on mcu, status phase could be sent either before or after changing device address, + // or even require stack to not response with status at all + // Therefore DCD must take full responsibility to response and include zlp status packet if needed. + usbd_control_set_request(p_request); // set request since DCD has no access to tud_control_status() API + dcd_set_address(rhport, (uint8_t) p_request->wValue); + // skip tud_control_status() + break; + + case TUSB_REQ_GET_CONFIGURATION: + { + uint8_t cfgnum = _usbd_dev.configured ? 1 : 0; + tud_control_xfer(rhport, p_request, &cfgnum, 1); + } + break; + + case TUSB_REQ_SET_CONFIGURATION: + { + uint8_t const cfg_num = (uint8_t) p_request->wValue; + + dcd_set_config(rhport, cfg_num); + + if ( !_usbd_dev.configured && cfg_num ) TU_ASSERT( process_set_config(rhport, cfg_num) ); + + _usbd_dev.configured = cfg_num ? 1 : 0; + + tud_control_status(rhport, p_request); + } + break; + + case TUSB_REQ_GET_DESCRIPTOR: + TU_VERIFY( process_get_descriptor(rhport, p_request) ); + break; + + case TUSB_REQ_SET_FEATURE: + // Only support remote wakeup for device feature + TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue); + + // Host may enable remote wake up before suspending especially HID device + _usbd_dev.remote_wakeup_en = true; + tud_control_status(rhport, p_request); + break; + + case TUSB_REQ_CLEAR_FEATURE: + // Only support remote wakeup for device feature + TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue); + + // Host may disable remote wake up after resuming + _usbd_dev.remote_wakeup_en = false; + tud_control_status(rhport, p_request); + break; + + case TUSB_REQ_GET_STATUS: + { + // Device status bit mask + // - Bit 0: Self Powered + // - Bit 1: Remote Wakeup enabled + uint16_t status = (_usbd_dev.self_powered ? 1 : 0) | (_usbd_dev.remote_wakeup_en ? 2 : 0); + tud_control_xfer(rhport, p_request, &status, 2); + } + break; + + // Unknown/Unsupported request + default: TU_BREAKPOINT(); return false; + } + break; + + //------------- Class/Interface Specific Request -------------// + case TUSB_REQ_RCPT_INTERFACE: + { + uint8_t const itf = tu_u16_low(p_request->wIndex); + TU_VERIFY(itf < TU_ARRAY_SIZE(_usbd_dev.itf2drv)); + + uint8_t const drvid = _usbd_dev.itf2drv[itf]; + TU_VERIFY(drvid < USBD_CLASS_DRIVER_COUNT); + + if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) + { + switch ( p_request->bRequest ) + { + case TUSB_REQ_GET_INTERFACE: + { + // TODO not support alternate interface yet + uint8_t alternate = 0; + tud_control_xfer(rhport, p_request, &alternate, 1); + } + break; + + case TUSB_REQ_SET_INTERFACE: + { + uint8_t const alternate = (uint8_t) p_request->wValue; + + // TODO not support alternate interface yet + TU_ASSERT(alternate == 0); + tud_control_status(rhport, p_request); + } + break; + + default: + // forward to class driver: "STD request to Interface" + // GET HID REPORT DESCRIPTOR falls into this case + TU_VERIFY(invoke_class_control(rhport, drvid, p_request)); + break; + } + }else + { + // forward to class driver: "non-STD request to Interface" + TU_VERIFY(invoke_class_control(rhport, drvid, p_request)); + } + } + break; + + //------------- Endpoint Request -------------// + case TUSB_REQ_RCPT_ENDPOINT: + { + uint8_t const ep_addr = tu_u16_low(p_request->wIndex); + uint8_t const ep_num = tu_edpt_number(ep_addr); + uint8_t const ep_dir = tu_edpt_dir(ep_addr); + + TU_ASSERT(ep_num < TU_ARRAY_SIZE(_usbd_dev.ep2drv) ); + + uint8_t const drvid = _usbd_dev.ep2drv[ep_num][ep_dir]; + + bool ret = false; + + // Handle STD request to endpoint + if ( TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type ) + { + // force return true for standard request + ret = true; + + switch ( p_request->bRequest ) + { + case TUSB_REQ_GET_STATUS: + { + uint16_t status = usbd_edpt_stalled(rhport, ep_addr) ? 0x0001 : 0x0000; + tud_control_xfer(rhport, p_request, &status, 2); + } + break; + + case TUSB_REQ_CLEAR_FEATURE: + if ( TUSB_REQ_FEATURE_EDPT_HALT == p_request->wValue ) + { + usbd_edpt_clear_stall(rhport, ep_addr); + } + tud_control_status(rhport, p_request); + break; + + case TUSB_REQ_SET_FEATURE: + if ( TUSB_REQ_FEATURE_EDPT_HALT == p_request->wValue ) + { + usbd_edpt_stall(rhport, ep_addr); + } + tud_control_status(rhport, p_request); + break; + + // Unknown/Unsupported request + default: TU_BREAKPOINT(); return false; + } + } + + if (drvid < 0xFF) { + TU_ASSERT(drvid < USBD_CLASS_DRIVER_COUNT); + + // Some classes such as USBTMC needs to clear/re-init its buffer when receiving CLEAR_FEATURE request + // We will forward all request targeted endpoint to class drivers after + // - For class-type requests: driver is fully responsible to reply to host + // - For std-type requests : driver init/re-init internal variable/buffer only, and + // must not call tud_control_status(), driver's return value will have no effect. + // EP state has already affected (stalled/cleared) + if ( invoke_class_control(rhport, drvid, p_request) ) ret = true; + } + + if ( TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type ) + { + // Set complete callback = NULL since it can also stall the request. + usbd_control_set_complete_callback(NULL); + } + + return ret; + } + break; + + // Unknown recipient + default: TU_BREAKPOINT(); return false; + } + + return true; +} + +// Process Set Configure Request +// This function parse configuration descriptor & open drivers accordingly +static bool process_set_config(uint8_t rhport, uint8_t cfg_num) +{ + tusb_desc_configuration_t const * desc_cfg = (tusb_desc_configuration_t const *) tud_descriptor_configuration_cb(cfg_num-1); // index is cfg_num-1 + TU_ASSERT(desc_cfg != NULL && desc_cfg->bDescriptorType == TUSB_DESC_CONFIGURATION); + + // Parse configuration descriptor + _usbd_dev.remote_wakeup_support = (desc_cfg->bmAttributes & TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP) ? 1 : 0; + _usbd_dev.self_powered = (desc_cfg->bmAttributes & TUSB_DESC_CONFIG_ATT_SELF_POWERED) ? 1 : 0; + + // Parse interface descriptor + uint8_t const * p_desc = ((uint8_t const*) desc_cfg) + sizeof(tusb_desc_configuration_t); + uint8_t const * desc_end = ((uint8_t const*) desc_cfg) + desc_cfg->wTotalLength; + + while( p_desc < desc_end ) + { + // Each interface always starts with Interface or Association descriptor + if ( TUSB_DESC_INTERFACE_ASSOCIATION == tu_desc_type(p_desc) ) + { + p_desc = tu_desc_next(p_desc); // ignore Interface Association + }else + { + TU_ASSERT( TUSB_DESC_INTERFACE == tu_desc_type(p_desc) ); + + tusb_desc_interface_t* desc_itf = (tusb_desc_interface_t*) p_desc; + + // Check if class is supported + uint8_t drv_id; + for (drv_id = 0; drv_id < USBD_CLASS_DRIVER_COUNT; drv_id++) + { + if ( _usbd_driver[drv_id].class_code == desc_itf->bInterfaceClass ) break; + } + TU_ASSERT( drv_id < USBD_CLASS_DRIVER_COUNT ); + + // Interface number must not be used already TODO alternate interface + TU_ASSERT( DRVID_INVALID == _usbd_dev.itf2drv[desc_itf->bInterfaceNumber] ); + _usbd_dev.itf2drv[desc_itf->bInterfaceNumber] = drv_id; + + uint16_t itf_len=0; + TU_LOG2(" %s open\r\n", _usbd_driver_str[drv_id]); + TU_ASSERT( _usbd_driver[drv_id].open(rhport, desc_itf, &itf_len) ); + TU_ASSERT( itf_len >= sizeof(tusb_desc_interface_t) ); + + mark_interface_endpoint(_usbd_dev.ep2drv, p_desc, itf_len, drv_id); + + p_desc += itf_len; // next interface + } + } + + // invoke callback + if (tud_mount_cb) tud_mount_cb(); + + return true; +} + +// Helper marking endpoint of interface belongs to class driver +static void mark_interface_endpoint(uint8_t ep2drv[8][2], uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id) +{ + uint16_t len = 0; + + while( len < desc_len ) + { + if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) ) + { + uint8_t const ep_addr = ((tusb_desc_endpoint_t const*) p_desc)->bEndpointAddress; + + ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)] = driver_id; + } + + len = (uint16_t)(len + tu_desc_len(p_desc)); + p_desc = tu_desc_next(p_desc); + } +} + +// return descriptor's buffer and update desc_len +static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request) +{ + tusb_desc_type_t const desc_type = (tusb_desc_type_t) tu_u16_high(p_request->wValue); + uint8_t const desc_index = tu_u16_low( p_request->wValue ); + + switch(desc_type) + { + case TUSB_DESC_DEVICE: + return tud_control_xfer(rhport, p_request, (void*) tud_descriptor_device_cb(), sizeof(tusb_desc_device_t)); + break; + + case TUSB_DESC_BOS: + { + // requested by host if USB > 2.0 ( i.e 2.1 or 3.x ) + if (!tud_descriptor_bos_cb) return false; + + tusb_desc_bos_t const* desc_bos = (tusb_desc_bos_t const*) tud_descriptor_bos_cb(); + uint16_t total_len; + memcpy(&total_len, &desc_bos->wTotalLength, 2); // possibly mis-aligned memory + + return tud_control_xfer(rhport, p_request, (void*) desc_bos, total_len); + } + break; + + case TUSB_DESC_CONFIGURATION: + { + tusb_desc_configuration_t const* desc_config = (tusb_desc_configuration_t const*) tud_descriptor_configuration_cb(desc_index); + TU_ASSERT(desc_config); + + uint16_t total_len; + memcpy(&total_len, &desc_config->wTotalLength, 2); // possibly mis-aligned memory + + return tud_control_xfer(rhport, p_request, (void*) desc_config, total_len); + } + break; + + case TUSB_DESC_STRING: + // String Descriptor always uses the desc set from user + if ( desc_index == 0xEE ) + { + // The 0xEE index string is a Microsoft OS Descriptors. + // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors + return false; + }else + { + uint8_t const* desc_str = (uint8_t const*) tud_descriptor_string_cb(desc_index); + TU_ASSERT(desc_str); + + // first byte of descriptor is its size + return tud_control_xfer(rhport, p_request, (void*) desc_str, desc_str[0]); + } + break; + + case TUSB_DESC_DEVICE_QUALIFIER: + // TODO If not highspeed capable stall this request otherwise + // return the descriptor that could work in highspeed + return false; + break; + + default: return false; + } + + return true; +} + +//--------------------------------------------------------------------+ +// DCD Event Handler +//--------------------------------------------------------------------+ +void dcd_event_handler(dcd_event_t const * event, bool in_isr) +{ + switch (event->event_id) + { + case DCD_EVENT_UNPLUGGED: + _usbd_dev.connected = 0; + _usbd_dev.configured = 0; + _usbd_dev.suspended = 0; + osal_queue_send(_usbd_q, event, in_isr); + break; + + case DCD_EVENT_SOF: + return; // skip SOF event for now + break; + + case DCD_EVENT_SUSPEND: + // NOTE: When plugging/unplugging device, the D+/D- state are unstable and can accidentally meet the + // SUSPEND condition ( Idle for 3ms ). Some MCUs such as SAMD doesn't distinguish suspend vs disconnect as well. + // We will skip handling SUSPEND/RESUME event if not currently connected + if ( _usbd_dev.connected ) + { + _usbd_dev.suspended = 1; + osal_queue_send(_usbd_q, event, in_isr); + } + break; + + case DCD_EVENT_RESUME: + // skip event if not connected (especially required for SAMD) + if ( _usbd_dev.connected ) + { + _usbd_dev.suspended = 0; + osal_queue_send(_usbd_q, event, in_isr); + } + break; + + default: + osal_queue_send(_usbd_q, event, in_isr); + break; + } +} + +void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr) +{ + dcd_event_t event = { .rhport = rhport, .event_id = eid, }; + dcd_event_handler(&event, in_isr); +} + +void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr) +{ + dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SETUP_RECEIVED }; + memcpy(&event.setup_received, setup, 8); + + dcd_event_handler(&event, in_isr); +} + +void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr) +{ + dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_XFER_COMPLETE }; + + event.xfer_complete.ep_addr = ep_addr; + event.xfer_complete.len = xferred_bytes; + event.xfer_complete.result = result; + + dcd_event_handler(&event, in_isr); +} + +//--------------------------------------------------------------------+ +// Helper +//--------------------------------------------------------------------+ + +// Parse consecutive endpoint descriptors (IN & OUT) +bool usbd_open_edpt_pair(uint8_t rhport, uint8_t const* p_desc, uint8_t ep_count, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in) +{ + for(int i=0; ibDescriptorType && xfer_type == desc_ep->bmAttributes.xfer); + TU_ASSERT(dcd_edpt_open(rhport, desc_ep)); + + if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN ) + { + (*ep_in) = desc_ep->bEndpointAddress; + }else + { + (*ep_out) = desc_ep->bEndpointAddress; + } + + p_desc = tu_desc_next(p_desc); + } + + return true; +} + +// Helper to defer an isr function +void usbd_defer_func(osal_task_func_t func, void* param, bool in_isr) +{ + dcd_event_t event = + { + .rhport = 0, + .event_id = USBD_EVENT_FUNC_CALL, + }; + + event.func_call.func = func; + event.func_call.param = param; + + dcd_event_handler(&event, in_isr); +} + +//--------------------------------------------------------------------+ +// USBD Endpoint API +//--------------------------------------------------------------------+ + +bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) +{ + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + TU_VERIFY( dcd_edpt_xfer(rhport, ep_addr, buffer, total_bytes) ); + _usbd_dev.ep_status[epnum][dir].busy = true; + + TU_LOG2(" XFER Endpoint: 0x%02X, Bytes: %d\r\n", ep_addr, total_bytes); + + return true; +} + +bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + return _usbd_dev.ep_status[epnum][dir].busy; +} + +void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr) +{ + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + dcd_edpt_stall(rhport, ep_addr); + _usbd_dev.ep_status[epnum][dir].stalled = true; + _usbd_dev.ep_status[epnum][dir].busy = true; +} + +void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) +{ + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + dcd_edpt_clear_stall(rhport, ep_addr); + _usbd_dev.ep_status[epnum][dir].stalled = false; + _usbd_dev.ep_status[epnum][dir].busy = false; +} + +bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + return _usbd_dev.ep_status[epnum][dir].stalled; +} + +#endif diff --git a/src/device/usbd.h b/src/device/usbd.h index e685686ad..847251caa 100644 --- a/src/device/usbd.h +++ b/src/device/usbd.h @@ -1,335 +1,335 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2019 Ha Thach (tinyusb.org) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * This file is part of the TinyUSB stack. - */ - -/** \ingroup group_usbd - * @{ */ - -#ifndef _TUSB_USBD_H_ -#define _TUSB_USBD_H_ - -#ifdef __cplusplus - extern "C" { -#endif - -#include "common/tusb_common.h" -#include "dcd.h" - -//--------------------------------------------------------------------+ -// Application API -//--------------------------------------------------------------------+ - -// Init device stack -bool tud_init (void); - -// Task function should be called in main/rtos loop -void tud_task (void); - -// Interrupt handler, name alias to DCD -#define tud_isr dcd_isr - -// Check if device is connected and configured -bool tud_mounted(void); - -// Check if device is suspended -bool tud_suspended(void); - -// Check if device is ready to transfer -static inline bool tud_ready(void) -{ - return tud_mounted() && !tud_suspended(); -} - -// Remote wake up host, only if suspended and enabled by host -bool tud_remote_wakeup(void); - -// Carry out Data and Status stage of control transfer -// - If len = 0, it is equivalent to sending status only -// - If len > wLength : it will be truncated -bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, void* buffer, uint16_t len); - -// Send STATUS (zero length) packet -bool tud_control_status(uint8_t rhport, tusb_control_request_t const * request); - -//--------------------------------------------------------------------+ -// Application Callbacks (WEAK is optional) -//--------------------------------------------------------------------+ - -// Invoked when received GET DEVICE DESCRIPTOR request -// Application return pointer to descriptor -uint8_t const * tud_descriptor_device_cb(void); - -// Invoked when received GET BOS DESCRIPTOR request -// Application return pointer to descriptor -TU_ATTR_WEAK uint8_t const * tud_descriptor_bos_cb(void); - -// Invoked when received GET CONFIGURATION DESCRIPTOR request -// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete -uint8_t const * tud_descriptor_configuration_cb(uint8_t index); - -// Invoked when received GET STRING DESCRIPTOR request -// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete -uint16_t const* tud_descriptor_string_cb(uint8_t index); - -// Invoked when device is mounted (configured) -TU_ATTR_WEAK void tud_mount_cb(void); - -// Invoked when device is unmounted -TU_ATTR_WEAK void tud_umount_cb(void); - -// Invoked when usb bus is suspended -// Within 7ms, device must draw an average of current less than 2.5 mA from bus -TU_ATTR_WEAK void tud_suspend_cb(bool remote_wakeup_en); - -// Invoked when usb bus is resumed -TU_ATTR_WEAK void tud_resume_cb(void); - -// Invoked when received control request with VENDOR TYPE -TU_ATTR_WEAK bool tud_vendor_control_request_cb(uint8_t rhport, tusb_control_request_t const * request); -TU_ATTR_WEAK bool tud_vendor_control_complete_cb(uint8_t rhport, tusb_control_request_t const * request); - - -//--------------------------------------------------------------------+ -// Binary Device Object Store (BOS) Descriptor Templates -//--------------------------------------------------------------------+ - -#define TUD_BOS_DESC_LEN 5 - -// total length, number of device caps -#define TUD_BOS_DESCRIPTOR(_total_len, _caps_num) \ - 5, TUSB_DESC_BOS, U16_TO_U8S_LE(_total_len), _caps_num - -// Device Capability Platform 128-bit UUID + Data -#define TUD_BOS_PLATFORM_DESCRIPTOR(...) \ - 4+TU_ARGS_NUM(__VA_ARGS__), TUSB_DESC_DEVICE_CAPABILITY, DEVICE_CAPABILITY_PLATFORM, 0x00, __VA_ARGS__ - -//------------- WebUSB BOS Platform -------------// - -// Descriptor Length -#define TUD_BOS_WEBUSB_DESC_LEN 24 - -// Vendor Code, iLandingPage -#define TUD_BOS_WEBUSB_DESCRIPTOR(_vendor_code, _ipage) \ - TUD_BOS_PLATFORM_DESCRIPTOR(TUD_BOS_WEBUSB_UUID, U16_TO_U8S_LE(0x0100), _vendor_code, _ipage) - -#define TUD_BOS_WEBUSB_UUID \ - 0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47, \ - 0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65 - -//------------- Microsoft OS 2.0 Platform -------------// -#define TUD_BOS_MICROSOFT_OS_DESC_LEN 28 - -// Total Length of descriptor set, vendor code -#define TUD_BOS_MS_OS_20_DESCRIPTOR(_desc_set_len, _vendor_code) \ - TUD_BOS_PLATFORM_DESCRIPTOR(TUD_BOS_MS_OS_20_UUID, U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(_desc_set_len), _vendor_code, 0) - -#define TUD_BOS_MS_OS_20_UUID \ - 0xDF, 0x60, 0xDD, 0xD8, 0x89, 0x45, 0xC7, 0x4C, \ - 0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F - -//--------------------------------------------------------------------+ -// Configuration & Interface Descriptor Templates -//--------------------------------------------------------------------+ - -//------------- Configuration -------------// -#define TUD_CONFIG_DESC_LEN (9) - -// Interface count, string index, total length, attribute, power in mA -#define TUD_CONFIG_DESCRIPTOR(_itfcount, _stridx, _total_len, _attribute, _power_ma) \ - 9, TUSB_DESC_CONFIGURATION, U16_TO_U8S_LE(_total_len), _itfcount, 1, _stridx, TU_BIT(7) | _attribute, (_power_ma)/2 - -//------------- CDC -------------// - -// Length of template descriptor: 66 bytes -#define TUD_CDC_DESC_LEN (8+9+5+5+4+5+7+9+7+7) - -// CDC Descriptor Template -// Interface number, string index, EP notification address and size, EP data address (out, in) and size. -#define TUD_CDC_DESCRIPTOR(_itfnum, _stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize) \ - /* Interface Associate */\ - 8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL, CDC_COMM_PROTOCOL_ATCOMMAND, 0,\ - /* CDC Control Interface */\ - 9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL, CDC_COMM_PROTOCOL_ATCOMMAND, _stridx,\ - /* CDC Header */\ - 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0120),\ - /* CDC Call */\ - 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_CALL_MANAGEMENT, 0, (uint8_t)((_itfnum) + 1),\ - /* CDC ACM: support line request */\ - 4, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, 2,\ - /* CDC Union */\ - 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\ - /* Endpoint Notification */\ - 7, TUSB_DESC_ENDPOINT, _ep_notif, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_notif_size), 16,\ - /* CDC Data Interface */\ - 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 2, TUSB_CLASS_CDC_DATA, 0, 0, 0,\ - /* Endpoint Out */\ - 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ - /* Endpoint In */\ - 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 - -//------------- MSC -------------// - -// Length of template descriptor: 23 bytes -#define TUD_MSC_DESC_LEN (9 + 7 + 7) - -// Interface number, string index, EP Out & EP In address, EP size -#define TUD_MSC_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \ - /* Interface */\ - 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_MSC, MSC_SUBCLASS_SCSI, MSC_PROTOCOL_BOT, _stridx,\ - /* Endpoint Out */\ - 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ - /* Endpoint In */\ - 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 - -//------------- HID -------------// - -// Length of template descriptor: 25 bytes -#define TUD_HID_DESC_LEN (9 + 9 + 7) - -// HID Input only descriptor -// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval -#define TUD_HID_DESCRIPTOR(_itfnum, _stridx, _boot_protocol, _report_desc_len, _epin, _epsize, _ep_interval) \ - /* Interface */\ - 9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUSB_CLASS_HID, (uint8_t)((_boot_protocol) ? HID_SUBCLASS_BOOT : 0), _boot_protocol, _stridx,\ - /* HID descriptor */\ - 9, HID_DESC_TYPE_HID, U16_TO_U8S_LE(0x0111), 0, 1, HID_DESC_TYPE_REPORT, U16_TO_U8S_LE(_report_desc_len),\ - /* Endpoint In */\ - 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _ep_interval - -// Length of template descriptor: 32 bytes -#define TUD_HID_INOUT_DESC_LEN (9 + 9 + 7 + 7) - -// HID Input & Output descriptor -// Interface number, string index, protocol, report descriptor len, EP OUT & IN address, size & polling interval -#define TUD_HID_INOUT_DESCRIPTOR(_itfnum, _stridx, _boot_protocol, _report_desc_len, _epout, _epin, _epsize, _ep_interval) \ - /* Interface */\ - 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_HID, (uint8_t)((_boot_protocol) ? HID_SUBCLASS_BOOT : 0), _boot_protocol, _stridx,\ - /* HID descriptor */\ - 9, HID_DESC_TYPE_HID, U16_TO_U8S_LE(0x0111), 0, 1, HID_DESC_TYPE_REPORT, U16_TO_U8S_LE(_report_desc_len),\ - /* Endpoint Out */\ - 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _ep_interval, \ - /* Endpoint In */\ - 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _ep_interval - -//------------- MIDI -------------// - -// Length of template descriptor (96 bytes) -#define TUD_MIDI_DESC_LEN (9 + 9 + 9 + 7 + 6 + 6 + 9 + 9 + 7 + 5 + 7 + 5) - -// MIDI simple descriptor -// - 1 Embedded Jack In connected to 1 External Jack Out -// - 1 Embedded Jack out connected to 1 External Jack In -#define TUD_MIDI_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \ - /* Audio Control (AC) Interface */\ - 9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_CONTROL, AUDIO_PROTOCOL_V1, _stridx,\ - /* AC Header */\ - 9, TUSB_DESC_CS_INTERFACE, AUDIO_CS_INTERFACE_HEADER, U16_TO_U8S_LE(0x0100), U16_TO_U8S_LE(0x0009), 1, (uint8_t)((_itfnum)+1),\ - /* MIDI Streaming (MS) Interface */\ - 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 2, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_MIDI_STREAMING, AUDIO_PROTOCOL_V1, 0,\ - /* MS Header */\ - 7, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_HEADER, U16_TO_U8S_LE(0x0100), U16_TO_U8S_LE(0x0025),\ - /* MS In Jack (Embedded) */\ - 6, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_IN_JACK, MIDI_JACK_EMBEDDED, 1, 0,\ - /* MS In Jack (External) */\ - 6, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_IN_JACK, MIDI_JACK_EXTERNAL, 2, 0,\ - /* MS Out Jack (Embedded), connected to In Jack External */\ - 9, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_OUT_JACK, MIDI_JACK_EMBEDDED, 3, 1, 2, 1, 0,\ - /* MS Out Jack (External), connected to In Jack Embedded */\ - 9, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_OUT_JACK, MIDI_JACK_EXTERNAL, 4, 1, 1, 1, 0,\ - /* Endpoint Out */\ - 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ - /* MS Endpoint (connected to embedded jack in) */\ - 5, TUSB_DESC_CS_ENDPOINT, MIDI_CS_ENDPOINT_GENERAL, 1, 1,\ - /* Endpoint In */\ - 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ - /* MS Endpoint (connected to embedded jack out) */\ - 5, TUSB_DESC_CS_ENDPOINT, MIDI_CS_ENDPOINT_GENERAL, 1, 3 - -//------------- TUD_USBTMC/USB488 -------------// -#define TUD_USBTMC_APP_CLASS (TUSB_CLASS_APPLICATION_SPECIFIC) -#define TUD_USBTMC_APP_SUBCLASS 0x03u - -#define TUD_USBTMC_PROTOCOL_STD 0x00u -#define TUD_USBTMC_PROTOCOL_USB488 0x01u - -// Interface number, number of endpoints, EP string index, USB_TMC_PROTOCOL*, bulk-out endpoint ID, -// bulk-in endpoint ID -#define TUD_USBTMC_IF_DESCRIPTOR(_itfnum, _bNumEndpoints, _stridx, _itfProtocol) \ -/* Interface */ \ - 0x09, TUSB_DESC_INTERFACE, _itfnum, 0x00, _bNumEndpoints, TUD_USBTMC_APP_CLASS, TUD_USBTMC_APP_SUBCLASS, _itfProtocol, _stridx - -#define TUD_USBTMC_IF_DESCRIPTOR_LEN 9u - -#define TUD_USBTMC_BULK_DESCRIPTORS(_epout, _epin, _bulk_epsize) \ -/* Endpoint Out */ \ -7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_bulk_epsize), 0u, \ -/* Endpoint In */ \ -7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_bulk_epsize), 0u - -#define TUD_USBTMC_BULK_DESCRIPTORS_LEN (7u+7u) - -/* optional interrupt endpoint */ \ -// _int_pollingInterval : for LS/FS, expressed in frames (1ms each). 16 may be a good number? -#define TUD_USBTMC_INT_DESCRIPTOR(_ep_interrupt, _ep_interrupt_size, _int_pollingInterval ) \ -7, TUSB_DESC_ENDPOINT, _ep_interrupt, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_interrupt_size), 0x16 - -#define TUD_USBTMC_INT_DESCRIPTOR_LEN (7u) - - -//------------- Vendor -------------// -#define TUD_VENDOR_DESC_LEN (9+7+7) - -// Interface number, string index, EP Out & IN address, EP size -#define TUD_VENDOR_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \ - /* Interface */\ - 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_VENDOR_SPECIFIC, 0x00, 0x00, _stridx,\ - /* Endpoint Out */\ - 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ - /* Endpoint In */\ - 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 - -//------------- DFU Runtime -------------// -#define TUD_DFU_APP_CLASS (TUSB_CLASS_APPLICATION_SPECIFIC) -#define TUD_DFU_APP_SUBCLASS 0x01u - -// Length of template descriptr: 18 bytes -#define TUD_DFU_RT_DESC_LEN (9 + 9) - -// DFU runtime descriptor -// Interface number, string index, attributes, detach timeout, transfer size -#define TUD_DFU_RT_DESCRIPTOR(_itfnum, _stridx, _attr, _timeout, _xfer_size) \ - /* Interface */ \ - 9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUD_DFU_APP_CLASS, TUD_DFU_APP_SUBCLASS, DFU_PROTOCOL_RT, _stridx, \ - /* Function */ \ - 9, DFU_DESC_FUNCTIONAL, _attr, U16_TO_U8S_LE(_timeout), U16_TO_U8S_LE(_xfer_size), U16_TO_U8S_LE(0x0101) - - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_USBD_H_ */ - -/** @} */ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +/** \ingroup group_usbd + * @{ */ + +#ifndef _TUSB_USBD_H_ +#define _TUSB_USBD_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include "common/tusb_common.h" +#include "dcd.h" + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ + +// Init device stack +bool tud_init (void); + +// Task function should be called in main/rtos loop +void tud_task (void); + +// Interrupt handler, name alias to DCD +#define tud_isr dcd_isr + +// Check if device is connected and configured +bool tud_mounted(void); + +// Check if device is suspended +bool tud_suspended(void); + +// Check if device is ready to transfer +static inline bool tud_ready(void) +{ + return tud_mounted() && !tud_suspended(); +} + +// Remote wake up host, only if suspended and enabled by host +bool tud_remote_wakeup(void); + +// Carry out Data and Status stage of control transfer +// - If len = 0, it is equivalent to sending status only +// - If len > wLength : it will be truncated +bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, void* buffer, uint16_t len); + +// Send STATUS (zero length) packet +bool tud_control_status(uint8_t rhport, tusb_control_request_t const * request); + +//--------------------------------------------------------------------+ +// Application Callbacks (WEAK is optional) +//--------------------------------------------------------------------+ + +// Invoked when received GET DEVICE DESCRIPTOR request +// Application return pointer to descriptor +uint8_t const * tud_descriptor_device_cb(void); + +// Invoked when received GET BOS DESCRIPTOR request +// Application return pointer to descriptor +TU_ATTR_WEAK uint8_t const * tud_descriptor_bos_cb(void); + +// Invoked when received GET CONFIGURATION DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint8_t const * tud_descriptor_configuration_cb(uint8_t index); + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint16_t const* tud_descriptor_string_cb(uint8_t index); + +// Invoked when device is mounted (configured) +TU_ATTR_WEAK void tud_mount_cb(void); + +// Invoked when device is unmounted +TU_ATTR_WEAK void tud_umount_cb(void); + +// Invoked when usb bus is suspended +// Within 7ms, device must draw an average of current less than 2.5 mA from bus +TU_ATTR_WEAK void tud_suspend_cb(bool remote_wakeup_en); + +// Invoked when usb bus is resumed +TU_ATTR_WEAK void tud_resume_cb(void); + +// Invoked when received control request with VENDOR TYPE +TU_ATTR_WEAK bool tud_vendor_control_request_cb(uint8_t rhport, tusb_control_request_t const * request); +TU_ATTR_WEAK bool tud_vendor_control_complete_cb(uint8_t rhport, tusb_control_request_t const * request); + + +//--------------------------------------------------------------------+ +// Binary Device Object Store (BOS) Descriptor Templates +//--------------------------------------------------------------------+ + +#define TUD_BOS_DESC_LEN 5 + +// total length, number of device caps +#define TUD_BOS_DESCRIPTOR(_total_len, _caps_num) \ + 5, TUSB_DESC_BOS, U16_TO_U8S_LE(_total_len), _caps_num + +// Device Capability Platform 128-bit UUID + Data +#define TUD_BOS_PLATFORM_DESCRIPTOR(...) \ + 4+TU_ARGS_NUM(__VA_ARGS__), TUSB_DESC_DEVICE_CAPABILITY, DEVICE_CAPABILITY_PLATFORM, 0x00, __VA_ARGS__ + +//------------- WebUSB BOS Platform -------------// + +// Descriptor Length +#define TUD_BOS_WEBUSB_DESC_LEN 24 + +// Vendor Code, iLandingPage +#define TUD_BOS_WEBUSB_DESCRIPTOR(_vendor_code, _ipage) \ + TUD_BOS_PLATFORM_DESCRIPTOR(TUD_BOS_WEBUSB_UUID, U16_TO_U8S_LE(0x0100), _vendor_code, _ipage) + +#define TUD_BOS_WEBUSB_UUID \ + 0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47, \ + 0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65 + +//------------- Microsoft OS 2.0 Platform -------------// +#define TUD_BOS_MICROSOFT_OS_DESC_LEN 28 + +// Total Length of descriptor set, vendor code +#define TUD_BOS_MS_OS_20_DESCRIPTOR(_desc_set_len, _vendor_code) \ + TUD_BOS_PLATFORM_DESCRIPTOR(TUD_BOS_MS_OS_20_UUID, U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(_desc_set_len), _vendor_code, 0) + +#define TUD_BOS_MS_OS_20_UUID \ + 0xDF, 0x60, 0xDD, 0xD8, 0x89, 0x45, 0xC7, 0x4C, \ + 0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F + +//--------------------------------------------------------------------+ +// Configuration & Interface Descriptor Templates +//--------------------------------------------------------------------+ + +//------------- Configuration -------------// +#define TUD_CONFIG_DESC_LEN (9) + +// Interface count, string index, total length, attribute, power in mA +#define TUD_CONFIG_DESCRIPTOR(_itfcount, _stridx, _total_len, _attribute, _power_ma) \ + 9, TUSB_DESC_CONFIGURATION, U16_TO_U8S_LE(_total_len), _itfcount, 1, _stridx, TU_BIT(7) | _attribute, (_power_ma)/2 + +//------------- CDC -------------// + +// Length of template descriptor: 66 bytes +#define TUD_CDC_DESC_LEN (8+9+5+5+4+5+7+9+7+7) + +// CDC Descriptor Template +// Interface number, string index, EP notification address and size, EP data address (out, in) and size. +#define TUD_CDC_DESCRIPTOR(_itfnum, _stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize) \ + /* Interface Associate */\ + 8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL, CDC_COMM_PROTOCOL_ATCOMMAND, 0,\ + /* CDC Control Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL, CDC_COMM_PROTOCOL_ATCOMMAND, _stridx,\ + /* CDC Header */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0120),\ + /* CDC Call */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_CALL_MANAGEMENT, 0, (uint8_t)((_itfnum) + 1),\ + /* CDC ACM: support line request */\ + 4, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, 2,\ + /* CDC Union */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\ + /* Endpoint Notification */\ + 7, TUSB_DESC_ENDPOINT, _ep_notif, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_notif_size), 16,\ + /* CDC Data Interface */\ + 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 2, TUSB_CLASS_CDC_DATA, 0, 0, 0,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 + +//------------- MSC -------------// + +// Length of template descriptor: 23 bytes +#define TUD_MSC_DESC_LEN (9 + 7 + 7) + +// Interface number, string index, EP Out & EP In address, EP size +#define TUD_MSC_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \ + /* Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_MSC, MSC_SUBCLASS_SCSI, MSC_PROTOCOL_BOT, _stridx,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 + +//------------- HID -------------// + +// Length of template descriptor: 25 bytes +#define TUD_HID_DESC_LEN (9 + 9 + 7) + +// HID Input only descriptor +// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval +#define TUD_HID_DESCRIPTOR(_itfnum, _stridx, _boot_protocol, _report_desc_len, _epin, _epsize, _ep_interval) \ + /* Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUSB_CLASS_HID, (uint8_t)((_boot_protocol) ? HID_SUBCLASS_BOOT : 0), _boot_protocol, _stridx,\ + /* HID descriptor */\ + 9, HID_DESC_TYPE_HID, U16_TO_U8S_LE(0x0111), 0, 1, HID_DESC_TYPE_REPORT, U16_TO_U8S_LE(_report_desc_len),\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _ep_interval + +// Length of template descriptor: 32 bytes +#define TUD_HID_INOUT_DESC_LEN (9 + 9 + 7 + 7) + +// HID Input & Output descriptor +// Interface number, string index, protocol, report descriptor len, EP OUT & IN address, size & polling interval +#define TUD_HID_INOUT_DESCRIPTOR(_itfnum, _stridx, _boot_protocol, _report_desc_len, _epout, _epin, _epsize, _ep_interval) \ + /* Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_HID, (uint8_t)((_boot_protocol) ? HID_SUBCLASS_BOOT : 0), _boot_protocol, _stridx,\ + /* HID descriptor */\ + 9, HID_DESC_TYPE_HID, U16_TO_U8S_LE(0x0111), 0, 1, HID_DESC_TYPE_REPORT, U16_TO_U8S_LE(_report_desc_len),\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _ep_interval, \ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _ep_interval + +//------------- MIDI -------------// + +// Length of template descriptor (96 bytes) +#define TUD_MIDI_DESC_LEN (9 + 9 + 9 + 7 + 6 + 6 + 9 + 9 + 7 + 5 + 7 + 5) + +// MIDI simple descriptor +// - 1 Embedded Jack In connected to 1 External Jack Out +// - 1 Embedded Jack out connected to 1 External Jack In +#define TUD_MIDI_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \ + /* Audio Control (AC) Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_CONTROL, AUDIO_PROTOCOL_V1, _stridx,\ + /* AC Header */\ + 9, TUSB_DESC_CS_INTERFACE, AUDIO_CS_INTERFACE_HEADER, U16_TO_U8S_LE(0x0100), U16_TO_U8S_LE(0x0009), 1, (uint8_t)((_itfnum)+1),\ + /* MIDI Streaming (MS) Interface */\ + 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 2, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_MIDI_STREAMING, AUDIO_PROTOCOL_V1, 0,\ + /* MS Header */\ + 7, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_HEADER, U16_TO_U8S_LE(0x0100), U16_TO_U8S_LE(0x0025),\ + /* MS In Jack (Embedded) */\ + 6, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_IN_JACK, MIDI_JACK_EMBEDDED, 1, 0,\ + /* MS In Jack (External) */\ + 6, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_IN_JACK, MIDI_JACK_EXTERNAL, 2, 0,\ + /* MS Out Jack (Embedded), connected to In Jack External */\ + 9, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_OUT_JACK, MIDI_JACK_EMBEDDED, 3, 1, 2, 1, 0,\ + /* MS Out Jack (External), connected to In Jack Embedded */\ + 9, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_OUT_JACK, MIDI_JACK_EXTERNAL, 4, 1, 1, 1, 0,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* MS Endpoint (connected to embedded jack in) */\ + 5, TUSB_DESC_CS_ENDPOINT, MIDI_CS_ENDPOINT_GENERAL, 1, 1,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* MS Endpoint (connected to embedded jack out) */\ + 5, TUSB_DESC_CS_ENDPOINT, MIDI_CS_ENDPOINT_GENERAL, 1, 3 + +//------------- TUD_USBTMC/USB488 -------------// +#define TUD_USBTMC_APP_CLASS (TUSB_CLASS_APPLICATION_SPECIFIC) +#define TUD_USBTMC_APP_SUBCLASS 0x03u + +#define TUD_USBTMC_PROTOCOL_STD 0x00u +#define TUD_USBTMC_PROTOCOL_USB488 0x01u + +// Interface number, number of endpoints, EP string index, USB_TMC_PROTOCOL*, bulk-out endpoint ID, +// bulk-in endpoint ID +#define TUD_USBTMC_IF_DESCRIPTOR(_itfnum, _bNumEndpoints, _stridx, _itfProtocol) \ +/* Interface */ \ + 0x09, TUSB_DESC_INTERFACE, _itfnum, 0x00, _bNumEndpoints, TUD_USBTMC_APP_CLASS, TUD_USBTMC_APP_SUBCLASS, _itfProtocol, _stridx + +#define TUD_USBTMC_IF_DESCRIPTOR_LEN 9u + +#define TUD_USBTMC_BULK_DESCRIPTORS(_epout, _epin, _bulk_epsize) \ +/* Endpoint Out */ \ +7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_bulk_epsize), 0u, \ +/* Endpoint In */ \ +7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_bulk_epsize), 0u + +#define TUD_USBTMC_BULK_DESCRIPTORS_LEN (7u+7u) + +/* optional interrupt endpoint */ \ +// _int_pollingInterval : for LS/FS, expressed in frames (1ms each). 16 may be a good number? +#define TUD_USBTMC_INT_DESCRIPTOR(_ep_interrupt, _ep_interrupt_size, _int_pollingInterval ) \ +7, TUSB_DESC_ENDPOINT, _ep_interrupt, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_interrupt_size), 0x16 + +#define TUD_USBTMC_INT_DESCRIPTOR_LEN (7u) + + +//------------- Vendor -------------// +#define TUD_VENDOR_DESC_LEN (9+7+7) + +// Interface number, string index, EP Out & IN address, EP size +#define TUD_VENDOR_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \ + /* Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_VENDOR_SPECIFIC, 0x00, 0x00, _stridx,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 + +//------------- DFU Runtime -------------// +#define TUD_DFU_APP_CLASS (TUSB_CLASS_APPLICATION_SPECIFIC) +#define TUD_DFU_APP_SUBCLASS 0x01u + +// Length of template descriptr: 18 bytes +#define TUD_DFU_RT_DESC_LEN (9 + 9) + +// DFU runtime descriptor +// Interface number, string index, attributes, detach timeout, transfer size +#define TUD_DFU_RT_DESCRIPTOR(_itfnum, _stridx, _attr, _timeout, _xfer_size) \ + /* Interface */ \ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUD_DFU_APP_CLASS, TUD_DFU_APP_SUBCLASS, DFU_PROTOCOL_RT, _stridx, \ + /* Function */ \ + 9, DFU_DESC_FUNCTIONAL, _attr, U16_TO_U8S_LE(_timeout), U16_TO_U8S_LE(_xfer_size), U16_TO_U8S_LE(0x0101) + + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_USBD_H_ */ + +/** @} */ -- cgit v1.3.1 From b6b9fe42af1828c2ed8f794a930999ecb4da2902 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 7 Feb 2020 16:43:44 +0700 Subject: more log for debugging --- src/class/cdc/cdc_device.c | 11 ++++++++++- src/device/usbd_control.c | 14 +++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) (limited to 'src/device') diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c index c0de0cadd..f63418dae 100644 --- a/src/class/cdc/cdc_device.c +++ b/src/class/cdc/cdc_device.c @@ -335,25 +335,34 @@ bool cdcd_control_request(uint8_t rhport, tusb_control_request_t const * request switch ( request->bRequest ) { case CDC_REQUEST_SET_LINE_CODING: + TU_LOG2(" Set Line Coding\n"); tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t)); break; case CDC_REQUEST_GET_LINE_CODING: + TU_LOG2(" Get Line Coding\n"); tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t)); break; case CDC_REQUEST_SET_CONTROL_LINE_STATE: + { // CDC PSTN v1.2 section 6.3.12 // Bit 0: Indicates if DTE is present or not. // This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR (Data Terminal Ready) // Bit 1: Carrier control for half-duplex modems. // This signal corresponds to V.24 signal 105 and RS-232 signal RTS (Request to Send) + bool const dtr = tu_bit_test(request->wValue, 0); + bool const rts = tu_bit_test(request->wValue, 1); + p_cdc->line_state = (uint8_t) request->wValue; + TU_LOG2(" Set Control Line State: DTR = %d, RTS = %d\n", dtr, rts); + tud_control_status(rhport, request); // Invoke callback - if ( tud_cdc_line_state_cb) tud_cdc_line_state_cb(itf, tu_bit_test(request->wValue, 0), tu_bit_test(request->wValue, 1)); + if ( tud_cdc_line_state_cb) tud_cdc_line_state_cb(itf, dtr, rts); + } break; default: return false; // stall unsupported request diff --git a/src/device/usbd_control.c b/src/device/usbd_control.c index e6d1caf4b..5a0ba412f 100644 --- a/src/device/usbd_control.c +++ b/src/device/usbd_control.c @@ -51,8 +51,8 @@ typedef struct static usbd_control_xfer_t _ctrl_xfer; -CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _usbd_ctrl_buf[CFG_TUD_ENDPOINT0_SIZE]; - +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN +static uint8_t _usbd_ctrl_buf[CFG_TUD_ENDPOINT0_SIZE]; //--------------------------------------------------------------------+ // Application API @@ -60,8 +60,14 @@ CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _usbd_ctrl_buf[CFG_TUD_EN static inline bool _status_stage_xact(uint8_t rhport, tusb_control_request_t const * request) { + // Opposite to endpoint in Data Phase + uint8_t const ep_addr = request->bmRequestType_bit.direction ? EDPT_CTRL_OUT : EDPT_CTRL_IN; + + TU_LOG2(" XFER Endpoint: 0x%02X, Bytes: %d\n", ep_addr, 0); + // status direction is reversed to one in the setup packet - return dcd_edpt_xfer(rhport, request->bmRequestType_bit.direction ? EDPT_CTRL_OUT : EDPT_CTRL_IN, NULL, 0); + // Note: Status must always be DATA1 + return dcd_edpt_xfer(rhport, ep_addr, NULL, 0); } // Status phase @@ -106,6 +112,8 @@ bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, vo // Data stage TU_ASSERT( _data_stage_xact(rhport) ); + + TU_LOG2(" XFER Endpoint: 0x%02X, Bytes: %d\n", request->bmRequestType_bit.direction ? EDPT_CTRL_IN : EDPT_CTRL_OUT, _ctrl_xfer.data_len); }else { // Status stage -- cgit v1.3.1 From b8ea0f0a6b11bff6a4bb3f63f09485b2445969de Mon Sep 17 00:00:00 2001 From: Chang Feng Date: Wed, 26 Feb 2020 13:07:15 +0800 Subject: Add langid support to descriptor string callback. Signed-off-by: Chang Feng --- src/device/usbd.c | 2 +- src/device/usbd.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/device') diff --git a/src/device/usbd.c b/src/device/usbd.c index 16670b944..68c5c5160 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -809,7 +809,7 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const return false; }else { - uint8_t const* desc_str = (uint8_t const*) tud_descriptor_string_cb(desc_index); + uint8_t const* desc_str = (uint8_t const*) tud_descriptor_string_cb(desc_index, p_request->wIndex); TU_ASSERT(desc_str); // first byte of descriptor is its size diff --git a/src/device/usbd.h b/src/device/usbd.h index 847251caa..d80abf17c 100644 --- a/src/device/usbd.h +++ b/src/device/usbd.h @@ -91,7 +91,7 @@ uint8_t const * tud_descriptor_configuration_cb(uint8_t index); // Invoked when received GET STRING DESCRIPTOR request // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete -uint16_t const* tud_descriptor_string_cb(uint8_t index); +uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid); // Invoked when device is mounted (configured) TU_ATTR_WEAK void tud_mount_cb(void); -- cgit v1.3.1 From fee79d746604f671483f5872337ea366f2d2c1a2 Mon Sep 17 00:00:00 2001 From: Peter Lawrence <12226419+majbthrd@users.noreply.github.com> Date: Mon, 2 Mar 2020 21:15:01 -0600 Subject: add CDC-ECM/RNDIS/CDC-EEM network device class with example --- .gitmodules | 5 +- examples/device/lwip_webserver/Makefile | 57 ++++ examples/device/lwip_webserver/src/arch/cc.h | 75 +++++ examples/device/lwip_webserver/src/lwipopts.h | 57 ++++ examples/device/lwip_webserver/src/main.c | 214 +++++++++++++ examples/device/lwip_webserver/src/tusb_config.h | 90 ++++++ .../device/lwip_webserver/src/usb_descriptors.c | 199 ++++++++++++ .../device/lwip_webserver/src/usb_descriptors.h | 28 ++ examples/rules.mk | 1 + lib/lwip | 1 + lib/networking/dhserver.c | 347 +++++++++++++++++++++ lib/networking/dhserver.h | 63 ++++ lib/networking/dnserver.c | 200 ++++++++++++ lib/networking/dnserver.h | 47 +++ lib/networking/ndis.h | 266 ++++++++++++++++ lib/networking/rndis_protocol.h | 307 ++++++++++++++++++ lib/networking/rndis_reports.c | 303 ++++++++++++++++++ src/class/net/net_device.c | 345 ++++++++++++++++++++ src/class/net/net_device.h | 84 +++++ src/device/usbd.c | 18 ++ src/device/usbd.h | 84 +++++ src/tusb.h | 4 + src/tusb_option.h | 12 + 23 files changed, 2806 insertions(+), 1 deletion(-) create mode 100644 examples/device/lwip_webserver/Makefile create mode 100644 examples/device/lwip_webserver/src/arch/cc.h create mode 100644 examples/device/lwip_webserver/src/lwipopts.h create mode 100644 examples/device/lwip_webserver/src/main.c create mode 100644 examples/device/lwip_webserver/src/tusb_config.h create mode 100644 examples/device/lwip_webserver/src/usb_descriptors.c create mode 100644 examples/device/lwip_webserver/src/usb_descriptors.h create mode 160000 lib/lwip create mode 100644 lib/networking/dhserver.c create mode 100644 lib/networking/dhserver.h create mode 100644 lib/networking/dnserver.c create mode 100644 lib/networking/dnserver.h create mode 100644 lib/networking/ndis.h create mode 100644 lib/networking/rndis_protocol.h create mode 100644 lib/networking/rndis_reports.c create mode 100644 src/class/net/net_device.c create mode 100644 src/class/net/net_device.h (limited to 'src/device') diff --git a/.gitmodules b/.gitmodules index 18b627bcf..0dda17d64 100644 --- a/.gitmodules +++ b/.gitmodules @@ -21,4 +21,7 @@ url = https://github.com/hathach/microchip_driver.git [submodule "hw/mcu/nuvoton"] path = hw/mcu/nuvoton - url = https://github.com/majbthrd/nuc_driver.git \ No newline at end of file + url = https://github.com/majbthrd/nuc_driver.git +[submodule "lib/lwip"] + path = lib/lwip + url = https://git.savannah.nongnu.org/git/lwip.git diff --git a/examples/device/lwip_webserver/Makefile b/examples/device/lwip_webserver/Makefile new file mode 100644 index 000000000..a153c30be --- /dev/null +++ b/examples/device/lwip_webserver/Makefile @@ -0,0 +1,57 @@ +include ../../../tools/top.mk +include ../../make.mk + +CFLAGS += \ + -DPBUF_POOL_SIZE=2 \ + -DTCP_WND=2*TCP_MSS \ + -DHTTPD_USE_CUSTOM_FSDATA=0 + +INC += \ + src \ + $(TOP)/hw \ + $(TOP)/lib/lwip/src/include \ + $(TOP)/lib/lwip/src/include/ipv4 \ + $(TOP)/lib/lwip/src/include/lwip/apps \ + $(TOP)/lib/networking + +# Example source +EXAMPLE_SOURCE += $(wildcard src/*.c) +SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE)) +SRC_C += \ + lib/lwip/src/core/altcp.c \ + lib/lwip/src/core/altcp_alloc.c \ + lib/lwip/src/core/altcp_tcp.c \ + lib/lwip/src/core/def.c \ + lib/lwip/src/core/dns.c \ + lib/lwip/src/core/inet_chksum.c \ + lib/lwip/src/core/init.c \ + lib/lwip/src/core/ip.c \ + lib/lwip/src/core/mem.c \ + lib/lwip/src/core/memp.c \ + lib/lwip/src/core/netif.c \ + lib/lwip/src/core/pbuf.c \ + lib/lwip/src/core/raw.c \ + lib/lwip/src/core/stats.c \ + lib/lwip/src/core/sys.c \ + lib/lwip/src/core/tcp.c \ + lib/lwip/src/core/tcp_in.c \ + lib/lwip/src/core/tcp_out.c \ + lib/lwip/src/core/timeouts.c \ + lib/lwip/src/core/udp.c \ + lib/lwip/src/core/ipv4/autoip.c \ + lib/lwip/src/core/ipv4/dhcp.c \ + lib/lwip/src/core/ipv4/etharp.c \ + lib/lwip/src/core/ipv4/icmp.c \ + lib/lwip/src/core/ipv4/igmp.c \ + lib/lwip/src/core/ipv4/ip4.c \ + lib/lwip/src/core/ipv4/ip4_addr.c \ + lib/lwip/src/core/ipv4/ip4_frag.c \ + lib/lwip/src/netif/ethernet.c \ + lib/lwip/src/netif/slipif.c \ + lib/lwip/src/apps/http/httpd.c \ + lib/lwip/src/apps/http/fs.c \ + lib/networking/dhserver.c \ + lib/networking/dnserver.c \ + lib/networking/rndis_reports.c + +include ../../rules.mk diff --git a/examples/device/lwip_webserver/src/arch/cc.h b/examples/device/lwip_webserver/src/arch/cc.h new file mode 100644 index 000000000..56a0cacf7 --- /dev/null +++ b/examples/device/lwip_webserver/src/arch/cc.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __CC_H__ +#define __CC_H__ + +//#include "cpu.h" + +typedef int sys_prot_t; + + + +/* define compiler specific symbols */ +#if defined (__ICCARM__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x +#define PACK_STRUCT_USE_INCLUDES + +#elif defined (__CC_ARM) + +#define PACK_STRUCT_BEGIN __packed +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#elif defined (__GNUC__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT __attribute__ ((__packed__)) +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#elif defined (__TASKING__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#endif + +#define LWIP_PLATFORM_ASSERT(x) do { if(!(x)) while(1); } while(0) + +#endif /* __CC_H__ */ diff --git a/examples/device/lwip_webserver/src/lwipopts.h b/examples/device/lwip_webserver/src/lwipopts.h new file mode 100644 index 000000000..23319592d --- /dev/null +++ b/examples/device/lwip_webserver/src/lwipopts.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ +#ifndef __LWIPOPTS_H__ +#define __LWIPOPTS_H__ + +/* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */ +#define NO_SYS 1 +#define MEM_ALIGNMENT 4 +#define LWIP_RAW 1 +#define LWIP_NETCONN 0 +#define LWIP_SOCKET 0 +#define LWIP_DHCP 0 +#define LWIP_ICMP 1 +#define LWIP_UDP 1 +#define LWIP_TCP 1 +#define ETH_PAD_SIZE 0 +#define LWIP_IP_ACCEPT_UDP_PORT(p) ((p) == PP_NTOHS(67)) + +#define TCP_MSS (1500 /*mtu*/ - 20 /*iphdr*/ - 20 /*tcphhr*/) +#define TCP_SND_BUF (2 * TCP_MSS) + +#define ETHARP_SUPPORT_STATIC_ENTRIES 1 + +#define LWIP_HTTPD_CGI 0 +#define LWIP_HTTPD_SSI 0 +#define LWIP_HTTPD_SSI_INCLUDE_TAG 0 + +#endif /* __LWIPOPTS_H__ */ diff --git a/examples/device/lwip_webserver/src/main.c b/examples/device/lwip_webserver/src/main.c new file mode 100644 index 000000000..364a26788 --- /dev/null +++ b/examples/device/lwip_webserver/src/main.c @@ -0,0 +1,214 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020 Peter Lawrence + * + * influenced by lrndis https://github.com/fetisov/lrndis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +/* +depending on the value of CFG_TUD_NET (tusb_config.h), this can be a CDC-ECM, RNDIS, or CDC-EEM USB virtual network adapter + +CDC-ECM should be valid on Linux and MacOS hosts +RNDIS should be valid on Linux and Windows hosts +CDC-EEM should be valid on Linux hosts + +You *must* customize tusb_config.h to set the CFG_TUD_NET definition to the type of these network adapters to emulate. + +The MCU appears to the host as IP address 192.168.7.1, and provides a DHCP server, DNS server, and web server. +*/ + +#include "bsp/board.h" +#include "tusb.h" + +#include "dhserver.h" +#include "dnserver.h" +#include "lwip/init.h" +#include "httpd.h" + +/* lwip context */ +static struct netif netif_data; + +/* shared between network_recv_callback() and service_traffic() */ +static struct pbuf *received_frame; + +/* this is used by this code, ./class/net/net_driver.c, and usb_descriptors.c */ +/* ideally speaking, this should be generated from the hardware's unique ID (if available) */ +const uint8_t network_mac_address[6] = {0x20,0x89,0x84,0x6A,0x96,0x00}; + +/* network parameters of this MCU */ +static const ip_addr_t ipaddr = IPADDR4_INIT_BYTES(192, 168, 7, 1); +static const ip_addr_t netmask = IPADDR4_INIT_BYTES(255, 255, 255, 0); +static const ip_addr_t gateway = IPADDR4_INIT_BYTES(0, 0, 0, 0); + +/* database IP addresses that can be offered to the host; this must be in RAM to store assigned MAC addresses */ +static dhcp_entry_t entries[] = +{ + /* mac ip address subnet mask lease time */ + { {0}, {192, 168, 7, 2}, {255, 255, 255, 0}, 24 * 60 * 60 }, + { {0}, {192, 168, 7, 3}, {255, 255, 255, 0}, 24 * 60 * 60 }, + { {0}, {192, 168, 7, 4}, {255, 255, 255, 0}, 24 * 60 * 60 } +}; + +/* DHCP configuration parameters, leveraging "entries" above */ +static const dhcp_config_t dhcp_config = +{ + {192, 168, 7, 1}, 67, /* server address (self), port */ + {192, 168, 7, 1}, /* dns server (self) */ + "usb", /* dns suffix */ + TU_ARRAY_SIZE(entries), /* number of entries */ + entries /* pointer to entries */ +}; + +static err_t linkoutput_fn(struct netif *netif, struct pbuf *p) +{ + (void)netif; + + for (;;) + { + /* if TinyUSB isn't ready, we must signal back to lwip that there is nothing we can do */ + if (!tud_ready()) + return ERR_USE; + + /* if the network driver can accept another packet, we make it happen */ + if (network_can_xmit()) + { + network_xmit(p); + return ERR_OK; + } + + /* transfer execution to TinyUSB in the hopes that it will finish transmitting the prior packet */ + tud_task(); + } +} + +static err_t output_fn(struct netif *netif, struct pbuf *p, const ip_addr_t *addr) +{ + return etharp_output(netif, p, addr); +} + +static err_t netif_init_cb(struct netif *netif) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + netif->mtu = CFG_TUD_NET_MTU; + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP | NETIF_FLAG_UP; + netif->state = NULL; + netif->name[0] = 'E'; + netif->name[1] = 'X'; + netif->linkoutput = linkoutput_fn; + netif->output = output_fn; + return ERR_OK; +} + +static void init_lwip(void) +{ + struct netif *netif = &netif_data; + + lwip_init(); + netif->hwaddr_len = sizeof(network_mac_address); + memcpy(netif->hwaddr, network_mac_address, sizeof(network_mac_address)); + + netif = netif_add(netif, &ipaddr, &netmask, &gateway, NULL, netif_init_cb, ip_input); + netif_set_default(netif); +} + +/* handle any DNS requests from dns-server */ +bool dns_query_proc(const char *name, ip_addr_t *addr) +{ + if (0 == strcmp(name, "tiny.usb")) + { + *addr = ipaddr; + return true; + } + return false; +} + +bool network_recv_callback(struct pbuf *p) +{ + /* this shouldn't happen, but if we get another packet before + parsing the previous, we must signal our inability to accept it */ + if (received_frame) return false; + + /* store away the pointer for service_traffic() to later handle */ + received_frame = p; + return true; +} + +static void service_traffic(void) +{ + /* handle any packet received by network_recv_callback() */ + if (received_frame) + { + ethernet_input(received_frame, &netif_data); + pbuf_free(received_frame); + received_frame = NULL; + network_recv_renew(); + } +} + +void network_init_callback(void) +{ + /* if the network is re-initializing and we have a leftover packet, we must do a cleanup */ + if (received_frame) + { + pbuf_free(received_frame); + received_frame = NULL; + } +} + +int main(void) +{ + /* initialize TinyUSB */ + board_init(); + tusb_init(); + + /* initialize lwip, dhcp-server, dns-server, and http */ + init_lwip(); + while (!netif_is_up(&netif_data)); + while (dhserv_init(&dhcp_config) != ERR_OK); + while (dnserv_init(&ipaddr, 53, dns_query_proc) != ERR_OK); + httpd_init(); + + while (1) + { + tud_task(); + service_traffic(); + } + + return 0; +} + +/* lwip has provision for using a mutex, when applicable */ +sys_prot_t sys_arch_protect(void) +{ + return 0; +} +void sys_arch_unprotect(sys_prot_t pval) +{ + (void)pval; +} + +/* lwip needs a millisecond time source, and the TinyUSB board support code has one available */ +uint32_t sys_now(void) +{ + return board_millis(); +} diff --git a/examples/device/lwip_webserver/src/tusb_config.h b/examples/device/lwip_webserver/src/tusb_config.h new file mode 100644 index 000000000..329fc80b9 --- /dev/null +++ b/examples/device/lwip_webserver/src/tusb_config.h @@ -0,0 +1,90 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU + #error CFG_TUSB_MCU must be defined +#endif + +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX +#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) +#else +#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE +#endif + +#define CFG_TUSB_OS OPT_OS_NONE + +// CFG_TUSB_DEBUG is defined by compiler in DEBUG build +// #define CFG_TUSB_DEBUG 0 + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE +#define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +//------------- CLASS -------------// +#define CFG_TUD_CDC 0 +#define CFG_TUD_MSC 0 +#define CFG_TUD_HID 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 0 +//#define CFG_TUD_NET OPT_NET_ECM +#define CFG_TUD_NET OPT_NET_RNDIS +//#define CFG_TUD_NET OPT_NET_EEM + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/examples/device/lwip_webserver/src/usb_descriptors.c b/examples/device/lwip_webserver/src/usb_descriptors.c new file mode 100644 index 000000000..803c99c02 --- /dev/null +++ b/examples/device/lwip_webserver/src/usb_descriptors.c @@ -0,0 +1,199 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "tusb.h" +#include "usb_descriptors.h" + +/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. + * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. + * + * Auto ProductID layout's Bitmap: + * [MSB] NET1:NET0 | VENDOR | MIDI | HID | MSC | CDC [LSB] + */ +#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) ) +#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ + _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) | _PID_MAP(NET, 5) ) + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ +tusb_desc_device_t const desc_device = +{ + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + + .bDeviceClass = TUSB_CLASS_UNSPECIFIED, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = 0xCafe, + .idProduct = USB_PID, + .bcdDevice = 0x0100, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01 +}; + +// Invoked when received GET DEVICE DESCRIPTOR +// Application return pointer to descriptor +uint8_t const * tud_descriptor_device_cb(void) +{ + return (uint8_t const *) &desc_device; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ +enum +{ + ITF_NUM_CDC = 0, + ITF_NUM_CDC_DATA, + ITF_NUM_TOTAL +}; + +enum +{ + STR_LANGID = 0, + STR_MANUFACTURER, + STR_PRODUCT, + STR_ITFNAME, + STR_MAC, +}; + +#if CFG_TUD_NET == OPT_NET_ECM +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_ECM_DESC_LEN) +#elif CFG_TUD_NET == OPT_NET_RNDIS +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_RNDIS_DESC_LEN) +#elif CFG_TUD_NET == OPT_NET_EEM +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_EEM_DESC_LEN) +#endif + +#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX + // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number + // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ... + #define EPNUM_CDC 2 +#else + #define EPNUM_CDC 2 +#endif + +uint8_t const desc_configuration[] = +{ + // Interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0, 100), + +#if CFG_TUD_NET == OPT_NET_ECM + // Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size. + TUD_CDC_ECM_DESCRIPTOR(ITF_NUM_CDC, STR_ITFNAME, STR_MAC, 0x81, 8, EPNUM_CDC, 0x80 | EPNUM_CDC, CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU), +#elif CFG_TUD_NET == OPT_NET_RNDIS + // Interface number, string index, EP notification address and size, EP data address (out, in) and size. + TUD_RNDIS_DESCRIPTOR(ITF_NUM_CDC, STR_ITFNAME, 0x81, 8, EPNUM_CDC, 0x80 | EPNUM_CDC, CFG_TUD_NET_ENDPOINT_SIZE), +#elif CFG_TUD_NET == OPT_NET_EEM + // Interface number, description string index, EP data address (out, in) and size. + TUD_CDC_EEM_DESCRIPTOR(ITF_NUM_CDC, STR_ITFNAME, EPNUM_CDC, 0x80 | EPNUM_CDC, CFG_TUD_NET_ENDPOINT_SIZE), +#endif +}; + +// Invoked when received GET CONFIGURATION DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const * tud_descriptor_configuration_cb(uint8_t index) +{ + (void) index; // for multiple configurations + return desc_configuration; +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// array of pointer to string descriptors +char const* string_desc_arr [] = +{ + [STR_LANGID] = (const char[]) { 0x09, 0x04 }, // supported language is English (0x0409) + [STR_MANUFACTURER] = "TinyUSB", // Manufacturer + [STR_PRODUCT] = "TinyUSB Device", // Product + [STR_ITFNAME] = // CDC-ECM Interface +#if CFG_TUD_NET == OPT_NET_ECM + "TinyUSB CDC-ECM", +#elif CFG_TUD_NET == OPT_NET_RNDIS + "TinyUSB RNDIS", +#elif CFG_TUD_NET == OPT_NET_EEM + "TinyUSB CDC-EEM", +#endif +}; + +static uint16_t _desc_str[32]; + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) +{ + (void)langid; + + unsigned chr_count = 0; + + if (STR_LANGID == index) + { + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + } + else if (STR_MAC == index) + { + // Convert MAC address into UTF-16 + + for (unsigned i=0; i> 4) & 0xf]; + _desc_str[1+chr_count++] = "0123456789ABCDEF"[(network_mac_address[i] >> 0) & 0xf]; + } + } + else + { + // Convert ASCII string into UTF-16 + + if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL; + + const char* str = string_desc_arr[index]; + + // Cap at max char + chr_count = strlen(str); + if ( chr_count > (TU_ARRAY_SIZE(_desc_str) - 1)) chr_count = TU_ARRAY_SIZE(_desc_str) - 1; + + for (unsigned i=0; i + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "dhserver.h" + +/* DHCP message type */ +#define DHCP_DISCOVER 1 +#define DHCP_OFFER 2 +#define DHCP_REQUEST 3 +#define DHCP_DECLINE 4 +#define DHCP_ACK 5 +#define DHCP_NAK 6 +#define DHCP_RELEASE 7 +#define DHCP_INFORM 8 + +/* DHCP options */ +enum DHCP_OPTIONS +{ + DHCP_PAD = 0, + DHCP_SUBNETMASK = 1, + DHCP_ROUTER = 3, + DHCP_DNSSERVER = 6, + DHCP_HOSTNAME = 12, + DHCP_DNSDOMAIN = 15, + DHCP_MTU = 26, + DHCP_BROADCAST = 28, + DHCP_PERFORMROUTERDISC = 31, + DHCP_STATICROUTE = 33, + DHCP_NISDOMAIN = 40, + DHCP_NISSERVER = 41, + DHCP_NTPSERVER = 42, + DHCP_VENDOR = 43, + DHCP_IPADDRESS = 50, + DHCP_LEASETIME = 51, + DHCP_OPTIONSOVERLOADED = 52, + DHCP_MESSAGETYPE = 53, + DHCP_SERVERID = 54, + DHCP_PARAMETERREQUESTLIST = 55, + DHCP_MESSAGE = 56, + DHCP_MAXMESSAGESIZE = 57, + DHCP_RENEWALTIME = 58, + DHCP_REBINDTIME = 59, + DHCP_CLASSID = 60, + DHCP_CLIENTID = 61, + DHCP_USERCLASS = 77, /* RFC 3004 */ + DHCP_FQDN = 81, + DHCP_DNSSEARCH = 119, /* RFC 3397 */ + DHCP_CSR = 121, /* RFC 3442 */ + DHCP_MSCSR = 249, /* MS code for RFC 3442 */ + DHCP_END = 255 +}; + +typedef struct +{ + uint8_t dp_op; /* packet opcode type */ + uint8_t dp_htype; /* hardware addr type */ + uint8_t dp_hlen; /* hardware addr length */ + uint8_t dp_hops; /* gateway hops */ + uint32_t dp_xid; /* transaction ID */ + uint16_t dp_secs; /* seconds since boot began */ + uint16_t dp_flags; + uint8_t dp_ciaddr[4]; /* client IP address */ + uint8_t dp_yiaddr[4]; /* 'your' IP address */ + uint8_t dp_siaddr[4]; /* server IP address */ + uint8_t dp_giaddr[4]; /* gateway IP address */ + uint8_t dp_chaddr[16]; /* client hardware address */ + uint8_t dp_legacy[192]; + uint8_t dp_magic[4]; + uint8_t dp_options[275]; /* options area */ +} DHCP_TYPE; + +DHCP_TYPE dhcp_data; +static struct udp_pcb *pcb = NULL; +static const dhcp_config_t *config = NULL; + +char magic_cookie[] = {0x63,0x82,0x53,0x63}; + +static uint32_t get_ip(const uint8_t *pnt) +{ + uint32_t result; + memcpy(&result, pnt, sizeof(result)); + return result; +} + +static void set_ip(uint8_t *pnt, uint32_t value) +{ + memcpy(pnt, &value, sizeof(value)); +} + +static dhcp_entry_t *entry_by_ip(uint32_t ip) +{ + int i; + for (i = 0; i < config->num_entry; i++) + if (get_ip(config->entries[i].addr) == ip) + return &config->entries[i]; + return NULL; +} + +static dhcp_entry_t *entry_by_mac(uint8_t *mac) +{ + int i; + for (i = 0; i < config->num_entry; i++) + if (memcmp(config->entries[i].mac, mac, 6) == 0) + return &config->entries[i]; + return NULL; +} + +static __inline bool is_vacant(dhcp_entry_t *entry) +{ + return memcmp("\0\0\0\0\0", entry->mac, 6) == 0; +} + +static dhcp_entry_t *vacant_address(void) +{ + int i; + for (i = 0; i < config->num_entry; i++) + if (is_vacant(config->entries + i)) + return config->entries + i; + return NULL; +} + +static __inline void free_entry(dhcp_entry_t *entry) +{ + memset(entry->mac, 0, 6); +} + +uint8_t *find_dhcp_option(uint8_t *attrs, int size, uint8_t attr) +{ + int i = 0; + while ((i + 1) < size) + { + int next = i + attrs[i + 1] + 2; + if (next > size) return NULL; + if (attrs[i] == attr) + return attrs + i; + i = next; + } + return NULL; +} + +int fill_options(void *dest, + uint8_t msg_type, + const char *domain, + uint32_t dns, + int lease_time, + uint32_t serverid, + uint32_t router, + uint32_t subnet) +{ + uint8_t *ptr = (uint8_t *)dest; + /* ACK message type */ + *ptr++ = 53; + *ptr++ = 1; + *ptr++ = msg_type; + + /* dhcp server identifier */ + *ptr++ = DHCP_SERVERID; + *ptr++ = 4; + set_ip(ptr, serverid); + ptr += 4; + + /* lease time */ + *ptr++ = DHCP_LEASETIME; + *ptr++ = 4; + *ptr++ = (lease_time >> 24) & 0xFF; + *ptr++ = (lease_time >> 16) & 0xFF; + *ptr++ = (lease_time >> 8) & 0xFF; + *ptr++ = (lease_time >> 0) & 0xFF; + + /* subnet mask */ + *ptr++ = DHCP_SUBNETMASK; + *ptr++ = 4; + set_ip(ptr, subnet); + ptr += 4; + + /* router */ + if (router != 0) + { + *ptr++ = DHCP_ROUTER; + *ptr++ = 4; + set_ip(ptr, router); + ptr += 4; + } + + /* domain name */ + if (domain != NULL) + { + int len = strlen(domain); + *ptr++ = DHCP_DNSDOMAIN; + *ptr++ = len; + memcpy(ptr, domain, len); + ptr += len; + } + + /* domain name server (DNS) */ + if (dns != 0) + { + *ptr++ = DHCP_DNSSERVER; + *ptr++ = 4; + set_ip(ptr, dns); + ptr += 4; + } + + /* end */ + *ptr++ = DHCP_END; + return ptr - (uint8_t *)dest; +} + +static void udp_recv_proc(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) +{ + uint8_t *ptr; + dhcp_entry_t *entry; + struct pbuf *pp; + + (void)arg; + (void)addr; + + unsigned n = p->len; + if (n > sizeof(dhcp_data)) n = sizeof(dhcp_data); + memcpy(&dhcp_data, p->payload, n); + switch (dhcp_data.dp_options[2]) + { + case DHCP_DISCOVER: + entry = entry_by_mac(dhcp_data.dp_chaddr); + if (entry == NULL) entry = vacant_address(); + if (entry == NULL) break; + + dhcp_data.dp_op = 2; /* reply */ + dhcp_data.dp_secs = 0; + dhcp_data.dp_flags = 0; + set_ip(dhcp_data.dp_yiaddr, get_ip(entry->addr)); + memcpy(dhcp_data.dp_magic, magic_cookie, 4); + + memset(dhcp_data.dp_options, 0, sizeof(dhcp_data.dp_options)); + + fill_options(dhcp_data.dp_options, + DHCP_OFFER, + config->domain, + get_ip(config->dns), + entry->lease, + get_ip(config->addr), + get_ip(config->addr), + get_ip(entry->subnet)); + + pp = pbuf_alloc(PBUF_TRANSPORT, sizeof(dhcp_data), PBUF_POOL); + if (pp == NULL) break; + memcpy(pp->payload, &dhcp_data, sizeof(dhcp_data)); + udp_sendto(upcb, pp, IP_ADDR_BROADCAST, port); + pbuf_free(pp); + break; + + case DHCP_REQUEST: + /* 1. find requested ipaddr in option list */ + ptr = find_dhcp_option(dhcp_data.dp_options, sizeof(dhcp_data.dp_options), DHCP_IPADDRESS); + if (ptr == NULL) break; + if (ptr[1] != 4) break; + ptr += 2; + + /* 2. does hw-address registered? */ + entry = entry_by_mac(dhcp_data.dp_chaddr); + if (entry != NULL) free_entry(entry); + + /* 3. find requested ipaddr */ + entry = entry_by_ip(get_ip(ptr)); + if (entry == NULL) break; + if (!is_vacant(entry)) break; + + /* 4. fill struct fields */ + memcpy(dhcp_data.dp_yiaddr, ptr, 4); + dhcp_data.dp_op = 2; /* reply */ + dhcp_data.dp_secs = 0; + dhcp_data.dp_flags = 0; + memcpy(dhcp_data.dp_magic, magic_cookie, 4); + + /* 5. fill options */ + memset(dhcp_data.dp_options, 0, sizeof(dhcp_data.dp_options)); + + fill_options(dhcp_data.dp_options, + DHCP_ACK, + config->domain, + get_ip(config->dns), + entry->lease, + get_ip(config->addr), + get_ip(config->addr), + get_ip(entry->subnet)); + + /* 6. send ACK */ + pp = pbuf_alloc(PBUF_TRANSPORT, sizeof(dhcp_data), PBUF_POOL); + if (pp == NULL) break; + memcpy(entry->mac, dhcp_data.dp_chaddr, 6); + memcpy(pp->payload, &dhcp_data, sizeof(dhcp_data)); + udp_sendto(upcb, pp, IP_ADDR_BROADCAST, port); + pbuf_free(pp); + break; + + default: + break; + } + pbuf_free(p); +} + +err_t dhserv_init(const dhcp_config_t *c) +{ + err_t err; + udp_init(); + dhserv_free(); + pcb = udp_new(); + if (pcb == NULL) + return ERR_MEM; + err = udp_bind(pcb, IP_ADDR_ANY, c->port); + if (err != ERR_OK) + { + dhserv_free(); + return err; + } + udp_recv(pcb, udp_recv_proc, NULL); + config = c; + return ERR_OK; +} + +void dhserv_free(void) +{ + if (pcb == NULL) return; + udp_remove(pcb); + pcb = NULL; +} diff --git a/lib/networking/dhserver.h b/lib/networking/dhserver.h new file mode 100644 index 000000000..901f6e351 --- /dev/null +++ b/lib/networking/dhserver.h @@ -0,0 +1,63 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 by Sergey Fetisov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * version: 1.0 demo (7.02.2015) + * brief: tiny dhcp ipv4 server using lwip (pcb) + * ref: https://lists.gnu.org/archive/html/lwip-users/2012-12/msg00016.html + */ + +#ifndef DHSERVER_H +#define DHSERVER_H + +#include +#include +#include +#include +#include "lwip/err.h" +#include "lwip/udp.h" +#include "netif/etharp.h" + +typedef struct dhcp_entry +{ + uint8_t mac[6]; + uint8_t addr[4]; + uint8_t subnet[4]; + uint32_t lease; +} dhcp_entry_t; + +typedef struct dhcp_config +{ + uint8_t addr[4]; + uint16_t port; + uint8_t dns[4]; + const char *domain; + int num_entry; + dhcp_entry_t *entries; +} dhcp_config_t; + +err_t dhserv_init(const dhcp_config_t *config); +void dhserv_free(void); + +#endif /* DHSERVER_H */ diff --git a/lib/networking/dnserver.c b/lib/networking/dnserver.c new file mode 100644 index 000000000..6df0bd0c4 --- /dev/null +++ b/lib/networking/dnserver.c @@ -0,0 +1,200 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 by Sergey Fetisov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * version: 1.0 demo (7.02.2015) + * brief: tiny dns ipv4 server using lwip (pcb) + */ + +#include "dnserver.h" + +#define DNS_MAX_HOST_NAME_LEN 128 + +static struct udp_pcb *pcb = NULL; +dns_query_proc_t query_proc = NULL; + +#pragma pack(push, 1) +typedef struct +{ +#if BYTE_ORDER == LITTLE_ENDIAN + uint8_t rd: 1, /* Recursion Desired */ + tc: 1, /* Truncation Flag */ + aa: 1, /* Authoritative Answer Flag */ + opcode: 4, /* Operation code */ + qr: 1; /* Query/Response Flag */ + uint8_t rcode: 4, /* Response Code */ + z: 3, /* Zero */ + ra: 1; /* Recursion Available */ +#else + uint8_t qr: 1, /* Query/Response Flag */ + opcode: 4, /* Operation code */ + aa: 1, /* Authoritative Answer Flag */ + tc: 1, /* Truncation Flag */ + rd: 1; /* Recursion Desired */ + uint8_t ra: 1, /* Recursion Available */ + z: 3, /* Zero */ + rcode: 4; /* Response Code */ +#endif +} dns_header_flags_t; + +typedef struct +{ + uint16_t id; + dns_header_flags_t flags; + uint16_t n_record[4]; +} dns_header_t; + +typedef struct dns_answer +{ + uint16_t name; + uint16_t type; + uint16_t Class; + uint32_t ttl; + uint16_t len; + uint32_t addr; +} dns_answer_t; +#pragma pack(pop) + +typedef struct dns_query +{ + char name[DNS_MAX_HOST_NAME_LEN]; + uint16_t type; + uint16_t Class; +} dns_query_t; + +static uint16_t get_uint16(const uint8_t *pnt) +{ + uint16_t result; + memcpy(&result, pnt, sizeof(result)); + return result; +} + +static int parse_next_query(void *data, int size, dns_query_t *query) +{ + int len; + int lables; + uint8_t *ptr; + + len = 0; + lables = 0; + ptr = (uint8_t *)data; + + while (true) + { + uint8_t lable_len; + if (size <= 0) return -1; + lable_len = *ptr++; + size--; + if (lable_len == 0) break; + if (lables > 0) + { + if (len == DNS_MAX_HOST_NAME_LEN) return -2; + query->name[len++] = '.'; + } + if (lable_len > size) return -1; + if (len + lable_len >= DNS_MAX_HOST_NAME_LEN) return -2; + memcpy(&query->name[len], ptr, lable_len); + len += lable_len; + ptr += lable_len; + size -= lable_len; + lables++; + } + + if (size < 4) return -1; + query->name[len] = 0; + query->type = get_uint16(ptr); + ptr += 2; + query->Class = get_uint16(ptr); + ptr += 2; + return ptr - (uint8_t *)data; +} + +static void udp_recv_proc(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) +{ + int len; + dns_header_t *header; + static dns_query_t query; + struct pbuf *out; + ip_addr_t host_addr; + dns_answer_t *answer; + + (void)arg; + + if (p->len <= sizeof(dns_header_t)) goto error; + header = (dns_header_t *)p->payload; + if (header->flags.qr != 0) goto error; + if (ntohs(header->n_record[0]) != 1) goto error; + + len = parse_next_query(header + 1, p->len - sizeof(dns_header_t), &query); + if (len < 0) goto error; + if (!query_proc(query.name, &host_addr)) goto error; + + len += sizeof(dns_header_t); + out = pbuf_alloc(PBUF_TRANSPORT, len + 16, PBUF_POOL); + if (out == NULL) goto error; + + memcpy(out->payload, p->payload, len); + header = (dns_header_t *)out->payload; + header->flags.qr = 1; + header->n_record[1] = htons(1); + answer = (struct dns_answer *)((uint8_t *)out->payload + len); + answer->name = htons(0xC00C); + answer->type = htons(1); + answer->Class = htons(1); + answer->ttl = htonl(32); + answer->len = htons(4); + answer->addr = host_addr.addr; + + udp_sendto(upcb, out, addr, port); + pbuf_free(out); + +error: + pbuf_free(p); +} + +err_t dnserv_init(const ip_addr_t *bind, uint16_t port, dns_query_proc_t qp) +{ + err_t err; + udp_init(); + dnserv_free(); + pcb = udp_new(); + if (pcb == NULL) + return ERR_MEM; + err = udp_bind(pcb, bind, port); + if (err != ERR_OK) + { + dnserv_free(); + return err; + } + udp_recv(pcb, udp_recv_proc, NULL); + query_proc = qp; + return ERR_OK; +} + +void dnserv_free() +{ + if (pcb == NULL) return; + udp_remove(pcb); + pcb = NULL; +} diff --git a/lib/networking/dnserver.h b/lib/networking/dnserver.h new file mode 100644 index 000000000..130991f5f --- /dev/null +++ b/lib/networking/dnserver.h @@ -0,0 +1,47 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 by Sergey Fetisov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * version: 1.0 demo (7.02.2015) + * brief: tiny dns ipv4 server using lwip (pcb) + */ + +#ifndef DNSERVER +#define DNSERVER + +#include +#include +#include +#include +#include "lwip/def.h" +#include "lwip/err.h" +#include "lwip/udp.h" +#include "netif/etharp.h" + +typedef bool (*dns_query_proc_t)(const char *name, ip_addr_t *addr); + +err_t dnserv_init(const ip_addr_t *bind, uint16_t port, dns_query_proc_t query_proc); +void dnserv_free(void); + +#endif diff --git a/lib/networking/ndis.h b/lib/networking/ndis.h new file mode 100644 index 000000000..1c737574c --- /dev/null +++ b/lib/networking/ndis.h @@ -0,0 +1,266 @@ +/* This file has been prepared for Doxygen automatic documentation generation.*/ +/*! \file ndis.h *************************************************************** + * + * \brief + * This file contains the possible external configuration of the USB. + * + * \addtogroup usbstick + * + * + ******************************************************************************/ + +/** + \ingroup usbstick + \defgroup RNDIS RNDIS Support + @{ + */ + +/* + * ndis.h + * + * Modified by Colin O'Flynn + * ntddndis.h modified by Benedikt Spranger + * + * Thanks to the cygwin development team, + * espacially to Casper S. Hornstrup + * + * THIS SOFTWARE IS NOT COPYRIGHTED + * + * This source code is offered for use in the public domain. You may + * use, modify or distribute it freely. + * + * This code is distributed in the hope that it will be useful but + * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY + * DISCLAIMED. This includes but is not limited to warranties of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef _LINUX_NDIS_H +#define _LINUX_NDIS_H + + +#define NDIS_STATUS_MULTICAST_FULL 0xC0010009 +#define NDIS_STATUS_MULTICAST_EXISTS 0xC001000A +#define NDIS_STATUS_MULTICAST_NOT_FOUND 0xC001000B + +/* from drivers/net/sk98lin/h/skgepnmi.h */ +#define OID_PNP_CAPABILITIES 0xFD010100 +#define OID_PNP_SET_POWER 0xFD010101 +#define OID_PNP_QUERY_POWER 0xFD010102 +#define OID_PNP_ADD_WAKE_UP_PATTERN 0xFD010103 +#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xFD010104 +#define OID_PNP_ENABLE_WAKE_UP 0xFD010106 + +enum NDIS_DEVICE_POWER_STATE { + NdisDeviceStateUnspecified = 0, + NdisDeviceStateD0, + NdisDeviceStateD1, + NdisDeviceStateD2, + NdisDeviceStateD3, + NdisDeviceStateMaximum +}; + +struct NDIS_PM_WAKE_UP_CAPABILITIES { + enum NDIS_DEVICE_POWER_STATE MinMagicPacketWakeUp; + enum NDIS_DEVICE_POWER_STATE MinPatternWakeUp; + enum NDIS_DEVICE_POWER_STATE MinLinkChangeWakeUp; +}; + +/* NDIS_PNP_CAPABILITIES.Flags constants */ +#define NDIS_DEVICE_WAKE_UP_ENABLE 0x00000001 +#define NDIS_DEVICE_WAKE_ON_PATTERN_MATCH_ENABLE 0x00000002 +#define NDIS_DEVICE_WAKE_ON_MAGIC_PACKET_ENABLE 0x00000004 + +/* +struct NDIS_PNP_CAPABILITIES { + __le32 Flags; + struct NDIS_PM_WAKE_UP_CAPABILITIES WakeUpCapabilities; +}; + +struct NDIS_PM_PACKET_PATTERN { + __le32 Priority; + __le32 Reserved; + __le32 MaskSize; + __le32 PatternOffset; + __le32 PatternSize; + __le32 PatternFlags; +}; +*/ + +/* Required Object IDs (OIDs) */ +#define OID_GEN_SUPPORTED_LIST 0x00010101 +#define OID_GEN_HARDWARE_STATUS 0x00010102 +#define OID_GEN_MEDIA_SUPPORTED 0x00010103 +#define OID_GEN_MEDIA_IN_USE 0x00010104 +#define OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105 +#define OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106 +#define OID_GEN_LINK_SPEED 0x00010107 +#define OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108 +#define OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109 +#define OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A +#define OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B +#define OID_GEN_VENDOR_ID 0x0001010C +#define OID_GEN_VENDOR_DESCRIPTION 0x0001010D +#define OID_GEN_CURRENT_PACKET_FILTER 0x0001010E +#define OID_GEN_CURRENT_LOOKAHEAD 0x0001010F +#define OID_GEN_DRIVER_VERSION 0x00010110 +#define OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111 +#define OID_GEN_PROTOCOL_OPTIONS 0x00010112 +#define OID_GEN_MAC_OPTIONS 0x00010113 +#define OID_GEN_MEDIA_CONNECT_STATUS 0x00010114 +#define OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115 +#define OID_GEN_VENDOR_DRIVER_VERSION 0x00010116 +#define OID_GEN_SUPPORTED_GUIDS 0x00010117 +#define OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118 +#define OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119 +#define OID_GEN_MACHINE_NAME 0x0001021A +#define OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B +#define OID_GEN_VLAN_ID 0x0001021C + +/* Optional OIDs */ +#define OID_GEN_MEDIA_CAPABILITIES 0x00010201 +#define OID_GEN_PHYSICAL_MEDIUM 0x00010202 + +/* Required statistics OIDs */ +#define OID_GEN_XMIT_OK 0x00020101 +#define OID_GEN_RCV_OK 0x00020102 +#define OID_GEN_XMIT_ERROR 0x00020103 +#define OID_GEN_RCV_ERROR 0x00020104 +#define OID_GEN_RCV_NO_BUFFER 0x00020105 + +/* Optional statistics OIDs */ +#define OID_GEN_DIRECTED_BYTES_XMIT 0x00020201 +#define OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202 +#define OID_GEN_MULTICAST_BYTES_XMIT 0x00020203 +#define OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204 +#define OID_GEN_BROADCAST_BYTES_XMIT 0x00020205 +#define OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206 +#define OID_GEN_DIRECTED_BYTES_RCV 0x00020207 +#define OID_GEN_DIRECTED_FRAMES_RCV 0x00020208 +#define OID_GEN_MULTICAST_BYTES_RCV 0x00020209 +#define OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A +#define OID_GEN_BROADCAST_BYTES_RCV 0x0002020B +#define OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C +#define OID_GEN_RCV_CRC_ERROR 0x0002020D +#define OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E +#define OID_GEN_GET_TIME_CAPS 0x0002020F +#define OID_GEN_GET_NETCARD_TIME 0x00020210 +#define OID_GEN_NETCARD_LOAD 0x00020211 +#define OID_GEN_DEVICE_PROFILE 0x00020212 +#define OID_GEN_INIT_TIME_MS 0x00020213 +#define OID_GEN_RESET_COUNTS 0x00020214 +#define OID_GEN_MEDIA_SENSE_COUNTS 0x00020215 +#define OID_GEN_FRIENDLY_NAME 0x00020216 +#define OID_GEN_MINIPORT_INFO 0x00020217 +#define OID_GEN_RESET_VERIFY_PARAMETERS 0x00020218 + +/* IEEE 802.3 (Ethernet) OIDs */ +#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001 + +#define OID_802_3_PERMANENT_ADDRESS 0x01010101 +#define OID_802_3_CURRENT_ADDRESS 0x01010102 +#define OID_802_3_MULTICAST_LIST 0x01010103 +#define OID_802_3_MAXIMUM_LIST_SIZE 0x01010104 +#define OID_802_3_MAC_OPTIONS 0x01010105 +#define OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101 +#define OID_802_3_XMIT_ONE_COLLISION 0x01020102 +#define OID_802_3_XMIT_MORE_COLLISIONS 0x01020103 +#define OID_802_3_XMIT_DEFERRED 0x01020201 +#define OID_802_3_XMIT_MAX_COLLISIONS 0x01020202 +#define OID_802_3_RCV_OVERRUN 0x01020203 +#define OID_802_3_XMIT_UNDERRUN 0x01020204 +#define OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205 +#define OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206 +#define OID_802_3_XMIT_LATE_COLLISIONS 0x01020207 + +/* Wireless LAN OIDs */ +/* Mandatory */ +#define OID_802_11_BSSID 0x0D010101 /* Q S */ +#define OID_802_11_SSID 0x0D010102 /* Q S */ +#define OID_802_11_NETWORK_TYPE_IN_USE 0x0D010204 /* Q S */ +#define OID_802_11_RSSI 0x0D010206 /* Q I */ +#define OID_802_11_BSSID_LIST 0x0D010217 /* Q */ +#define OID_802_11_BSSID_LIST_SCAN 0x0D01011A /* S */ +#define OID_802_11_INFRASTRUCTURE_MODE 0x0D010108 /* Q S */ +#define OID_802_11_SUPPORTED_RATES 0x0D01020E /* Q */ +#define OID_802_11_CONFIGURATION 0x0D010211 /* Q S */ +#define OID_802_11_ADD_WEP 0x0D010113 /* S */ +#define OID_802_11_WEP_STATUS 0x0D01011B /* Q S */ +#define OID_802_11_REMOVE_WEP 0x0D010114 /* S */ +#define OID_802_11_DISASSOCIATE 0x0D010115 /* S */ +#define OID_802_11_AUTHENTICATION_MODE 0x0D010118 /* Q S */ +#define OID_802_11_RELOAD_DEFAULTS 0x0D01011C /* S */ + + + +/* OID_GEN_MINIPORT_INFO constants */ +#define NDIS_MINIPORT_BUS_MASTER 0x00000001 +#define NDIS_MINIPORT_WDM_DRIVER 0x00000002 +#define NDIS_MINIPORT_SG_LIST 0x00000004 +#define NDIS_MINIPORT_SUPPORTS_MEDIA_QUERY 0x00000008 +#define NDIS_MINIPORT_INDICATES_PACKETS 0x00000010 +#define NDIS_MINIPORT_IGNORE_PACKET_QUEUE 0x00000020 +#define NDIS_MINIPORT_IGNORE_REQUEST_QUEUE 0x00000040 +#define NDIS_MINIPORT_IGNORE_TOKEN_RING_ERRORS 0x00000080 +#define NDIS_MINIPORT_INTERMEDIATE_DRIVER 0x00000100 +#define NDIS_MINIPORT_IS_NDIS_5 0x00000200 +#define NDIS_MINIPORT_IS_CO 0x00000400 +#define NDIS_MINIPORT_DESERIALIZE 0x00000800 +#define NDIS_MINIPORT_REQUIRES_MEDIA_POLLING 0x00001000 +#define NDIS_MINIPORT_SUPPORTS_MEDIA_SENSE 0x00002000 +#define NDIS_MINIPORT_NETBOOT_CARD 0x00004000 +#define NDIS_MINIPORT_PM_SUPPORTED 0x00008000 +#define NDIS_MINIPORT_SUPPORTS_MAC_ADDRESS_OVERWRITE 0x00010000 +#define NDIS_MINIPORT_USES_SAFE_BUFFER_APIS 0x00020000 +#define NDIS_MINIPORT_HIDDEN 0x00040000 +#define NDIS_MINIPORT_SWENUM 0x00080000 +#define NDIS_MINIPORT_SURPRISE_REMOVE_OK 0x00100000 +#define NDIS_MINIPORT_NO_HALT_ON_SUSPEND 0x00200000 +#define NDIS_MINIPORT_HARDWARE_DEVICE 0x00400000 +#define NDIS_MINIPORT_SUPPORTS_CANCEL_SEND_PACKETS 0x00800000 +#define NDIS_MINIPORT_64BITS_DMA 0x01000000 + +#define NDIS_MEDIUM_802_3 0x00000000 +#define NDIS_MEDIUM_802_5 0x00000001 +#define NDIS_MEDIUM_FDDI 0x00000002 +#define NDIS_MEDIUM_WAN 0x00000003 +#define NDIS_MEDIUM_LOCAL_TALK 0x00000004 +#define NDIS_MEDIUM_DIX 0x00000005 +#define NDIS_MEDIUM_ARCENT_RAW 0x00000006 +#define NDIS_MEDIUM_ARCENT_878_2 0x00000007 +#define NDIS_MEDIUM_ATM 0x00000008 +#define NDIS_MEDIUM_WIRELESS_LAN 0x00000009 +#define NDIS_MEDIUM_IRDA 0x0000000A +#define NDIS_MEDIUM_BPC 0x0000000B +#define NDIS_MEDIUM_CO_WAN 0x0000000C +#define NDIS_MEDIUM_1394 0x0000000D + +#define NDIS_PACKET_TYPE_DIRECTED 0x00000001 +#define NDIS_PACKET_TYPE_MULTICAST 0x00000002 +#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 +#define NDIS_PACKET_TYPE_BROADCAST 0x00000008 +#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010 +#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020 +#define NDIS_PACKET_TYPE_SMT 0x00000040 +#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080 +#define NDIS_PACKET_TYPE_GROUP 0x00000100 +#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200 +#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400 +#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 + +#define NDIS_MEDIA_STATE_CONNECTED 0x00000000 +#define NDIS_MEDIA_STATE_DISCONNECTED 0x00000001 + +#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA 0x00000001 +#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED 0x00000002 +#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND 0x00000004 +#define NDIS_MAC_OPTION_NO_LOOPBACK 0x00000008 +#define NDIS_MAC_OPTION_FULL_DUPLEX 0x00000010 +#define NDIS_MAC_OPTION_EOTX_INDICATION 0x00000020 +#define NDIS_MAC_OPTION_8021P_PRIORITY 0x00000040 +#define NDIS_MAC_OPTION_RESERVED 0x80000000 + +#endif /* _LINUX_NDIS_H */ + +/** @} */ diff --git a/lib/networking/rndis_protocol.h b/lib/networking/rndis_protocol.h new file mode 100644 index 000000000..b45860eeb --- /dev/null +++ b/lib/networking/rndis_protocol.h @@ -0,0 +1,307 @@ +/** + * \file rndis_protocol.h + * RNDIS Defines + * + * \author + * Colin O'Flynn + * + * \addtogroup usbstick + */ + +/* Copyright (c) 2008 Colin O'Flynn + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of the copyright holders nor the names of + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _RNDIS_H +#define _RNDIS_H + +/** + \addtogroup RNDIS + @{ + */ + +#include + +#define RNDIS_MAJOR_VERSION 1 +#define RNDIS_MINOR_VERSION 0 + +#define RNDIS_STATUS_SUCCESS 0X00000000 +#define RNDIS_STATUS_FAILURE 0XC0000001 +#define RNDIS_STATUS_INVALID_DATA 0XC0010015 +#define RNDIS_STATUS_NOT_SUPPORTED 0XC00000BB +#define RNDIS_STATUS_MEDIA_CONNECT 0X4001000B +#define RNDIS_STATUS_MEDIA_DISCONNECT 0X4001000C + + +/* Message set for Connectionless (802.3) Devices */ +#define REMOTE_NDIS_PACKET_MSG 0x00000001 +#define REMOTE_NDIS_INITIALIZE_MSG 0X00000002 +#define REMOTE_NDIS_HALT_MSG 0X00000003 +#define REMOTE_NDIS_QUERY_MSG 0X00000004 +#define REMOTE_NDIS_SET_MSG 0X00000005 +#define REMOTE_NDIS_RESET_MSG 0X00000006 +#define REMOTE_NDIS_INDICATE_STATUS_MSG 0X00000007 +#define REMOTE_NDIS_KEEPALIVE_MSG 0X00000008 +#define REMOTE_NDIS_INITIALIZE_CMPLT 0X80000002 +#define REMOTE_NDIS_QUERY_CMPLT 0X80000004 +#define REMOTE_NDIS_SET_CMPLT 0X80000005 +#define REMOTE_NDIS_RESET_CMPLT 0X80000006 +#define REMOTE_NDIS_KEEPALIVE_CMPLT 0X80000008 + +typedef uint32_t rndis_MessageType_t; +typedef uint32_t rndis_MessageLength_t; +typedef uint32_t rndis_RequestId_t; +typedef uint32_t rndis_MajorVersion_t; +typedef uint32_t rndis_MinorVersion_t; +typedef uint32_t rndis_MaxTransferSize_t; +typedef uint32_t rndis_Status_t; + + +/* Device Flags */ +#define RNDIS_DF_CONNECTIONLESS 0x00000001 +#define RNDIS_DF_CONNECTION_ORIENTED 0x00000002 +typedef uint32_t rndis_DeviceFlags_t; + +/* Mediums */ +#define RNDIS_MEDIUM_802_3 0x00000000 +typedef uint32_t rndis_Medium_t; + + +typedef uint32_t rndis_MaxPacketsPerTransfer_t; +typedef uint32_t rndis_PacketAlignmentFactor_t; +typedef uint32_t rndis_AfListOffset_t; +typedef uint32_t rndis_AfListSize_t; + +/*** Remote NDIS Generic Message type ***/ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + } rndis_generic_msg_t; + + +/*** Remote NDIS Initialize Message ***/ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_RequestId_t RequestId; + rndis_MajorVersion_t MajorVersion; + rndis_MinorVersion_t MinorVersion; + rndis_MaxTransferSize_t MaxTransferSize; + } rndis_initialize_msg_t; + +/* Response: */ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_RequestId_t RequestId; + rndis_Status_t Status; + rndis_MajorVersion_t MajorVersion; + rndis_MinorVersion_t MinorVersion; + rndis_DeviceFlags_t DeviceFlags; + rndis_Medium_t Medium; + rndis_MaxPacketsPerTransfer_t MaxPacketsPerTransfer; + rndis_MaxTransferSize_t MaxTransferSize; + rndis_PacketAlignmentFactor_t PacketAlignmentFactor; + rndis_AfListOffset_t AfListOffset; + rndis_AfListSize_t AfListSize; + } rndis_initialize_cmplt_t; + + +/*** Remote NDIS Halt Message ***/ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_RequestId_t RequestId; + } rndis_halt_msg_t; + +typedef uint32_t rndis_Oid_t; +typedef uint32_t rndis_InformationBufferLength_t; +typedef uint32_t rndis_InformationBufferOffset_t; +typedef uint32_t rndis_DeviceVcHandle_t; + +/*** Remote NDIS Query Message ***/ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_RequestId_t RequestId; + rndis_Oid_t Oid; + rndis_InformationBufferLength_t InformationBufferLength; + rndis_InformationBufferOffset_t InformationBufferOffset; + rndis_DeviceVcHandle_t DeviceVcHandle; + } rndis_query_msg_t; + +/* Response: */ + +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_RequestId_t RequestId; + rndis_Status_t Status; + rndis_InformationBufferLength_t InformationBufferLength; + rndis_InformationBufferOffset_t InformationBufferOffset; + } rndis_query_cmplt_t; + +/*** Remote NDIS Set Message ***/ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_RequestId_t RequestId; + rndis_Oid_t Oid; + rndis_InformationBufferLength_t InformationBufferLength; + rndis_InformationBufferOffset_t InformationBufferOffset; + rndis_DeviceVcHandle_t DeviceVcHandle; + } rndis_set_msg_t; + +/* Response */ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_RequestId_t RequestId; + rndis_Status_t Status; + }rndis_set_cmplt_t; + +/* Information buffer layout for OID_GEN_RNDIS_CONFIG_PARAMETER */ +typedef uint32_t rndis_ParameterNameOffset_t; +typedef uint32_t rndis_ParameterNameLength_t; +typedef uint32_t rndis_ParameterType_t; +typedef uint32_t rndis_ParameterValueOffset_t; +typedef uint32_t rndis_ParameterValueLength_t; + +#define PARAMETER_TYPE_STRING 2 +#define PARAMETER_TYPE_NUMERICAL 0 + +typedef struct{ + rndis_ParameterNameOffset_t ParameterNameOffset; + rndis_ParameterNameLength_t ParameterNameLength; + rndis_ParameterType_t ParameterType; + rndis_ParameterValueOffset_t ParameterValueOffset; + rndis_ParameterValueLength_t ParameterValueLength; + }rndis_config_parameter_t; + +typedef uint32_t rndis_Reserved_t; + +/*** Remote NDIS Soft Reset Message ***/ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_Reserved_t Reserved; + } rndis_reset_msg_t; + +typedef uint32_t rndis_AddressingReset_t; + +/* Response: */ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_Status_t Status; + rndis_AddressingReset_t AddressingReset; + } rndis_reset_cmplt_t; + +/*** Remote NDIS Indicate Status Message ***/ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_Status_t Status; + rndis_Status_t StatusBufferLength; + rndis_Status_t StatusBufferOffset; + } rndis_indicate_status_t; + +typedef uint32_t rndis_DiagStatus_t; +typedef uint32_t rndis_ErrorOffset_t; + +typedef struct { + rndis_DiagStatus_t DiagStatus; + rndis_ErrorOffset_t ErrorOffset; + }rndis_diagnostic_info_t; + +/*** Remote NDIS Keepalive Message */ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_RequestId_t RequestId; + }rndis_keepalive_msg_t; + +/* Response: */ +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_RequestId_t RequestId; + rndis_Status_t Status; + }rndis_keepalive_cmplt_t; + +/*** Remote NDIS Data Packet ***/ + +typedef uint32_t rndis_DataOffset_t; +typedef uint32_t rndis_DataLength_t; +typedef uint32_t rndis_OOBDataOffset_t; +typedef uint32_t rndis_OOBDataLength_t; +typedef uint32_t rndis_NumOOBDataElements_t; +typedef uint32_t rndis_PerPacketInfoOffset_t; +typedef uint32_t rndis_PerPacketInfoLength_t; + +typedef struct{ + rndis_MessageType_t MessageType; + rndis_MessageLength_t MessageLength; + rndis_DataOffset_t DataOffset; + rndis_DataLength_t DataLength; + rndis_OOBDataOffset_t OOBDataOffset; + rndis_OOBDataLength_t OOBDataLength; + rndis_NumOOBDataElements_t NumOOBDataElements; + rndis_PerPacketInfoOffset_t PerPacketInfoOffset; + rndis_PerPacketInfoLength_t PerPacketInfoLength; + rndis_DeviceVcHandle_t DeviceVcHandle; + rndis_Reserved_t Reserved; + }rndis_data_packet_t; + +typedef uint32_t rndis_ClassInformationOffset_t; +typedef uint32_t rndis_Size_t; +typedef uint32_t rndis_Type_t; + +typedef struct{ + rndis_Size_t Size; + rndis_Type_t Type; + rndis_ClassInformationOffset_t ClassInformationType; + }rndis_OOB_packet_t; + +#include "ndis.h" + +typedef enum rnids_state_e { + rndis_uninitialized, + rndis_initialized, + rndis_data_initialized + } rndis_state_t; + +typedef struct { + uint32_t txok; + uint32_t rxok; + uint32_t txbad; + uint32_t rxbad; +} usb_eth_stat_t; + +#endif /* _RNDIS_H */ + +/** @} */ diff --git a/lib/networking/rndis_reports.c b/lib/networking/rndis_reports.c new file mode 100644 index 000000000..535bdf851 --- /dev/null +++ b/lib/networking/rndis_reports.c @@ -0,0 +1,303 @@ +/* + The original version of this code was lrndis/usbd_rndis_core.c from https://github.com/fetisov/lrndis + It has since been overhauled to suit this application +*/ + +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 by Sergey Fetisov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "class/net/net_device.h" +#include "rndis_protocol.h" +#include "netif/ethernet.h" + +#define RNDIS_LINK_SPEED 12000000 /* Link baudrate (12Mbit/s for USB-FS) */ +#define RNDIS_VENDOR "TinyUSB" /* NIC vendor name */ + +static const uint8_t *const station_hwaddr = network_mac_address; +static const uint8_t *const permanent_hwaddr = network_mac_address; + +static usb_eth_stat_t usb_eth_stat = { 0, 0, 0, 0 }; +static uint32_t oid_packet_filter = 0x0000000; +static rndis_state_t rndis_state; + +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t ndis_report[8] = { 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; + +static const uint32_t OIDSupportedList[] = +{ + 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_PROTOCOL_OPTIONS, + OID_GEN_MAC_OPTIONS, + OID_GEN_MEDIA_CONNECT_STATUS, + OID_GEN_MAXIMUM_SEND_PACKETS, + OID_802_3_PERMANENT_ADDRESS, + OID_802_3_CURRENT_ADDRESS, + OID_802_3_MULTICAST_LIST, + OID_802_3_MAXIMUM_LIST_SIZE, + OID_802_3_MAC_OPTIONS +}; + +#define OID_LIST_LENGTH TU_ARRAY_SIZE(OIDSupportedList) +#define ENC_BUF_SIZE (OID_LIST_LENGTH * 4 + 32) + +static uint8_t *encapsulated_buffer; + +static void rndis_report(void) +{ + netd_report(ndis_report, sizeof(ndis_report)); +} + +static void rndis_query_cmplt32(int status, uint32_t data) +{ + rndis_query_cmplt_t *c; + c = (rndis_query_cmplt_t *)encapsulated_buffer; + c->MessageType = REMOTE_NDIS_QUERY_CMPLT; + c->MessageLength = sizeof(rndis_query_cmplt_t) + 4; + c->InformationBufferLength = 4; + c->InformationBufferOffset = 16; + c->Status = status; + memcpy(c + 1, &data, sizeof(data)); + rndis_report(); +} + +static void rndis_query_cmplt(int status, const void *data, int size) +{ + rndis_query_cmplt_t *c; + c = (rndis_query_cmplt_t *)encapsulated_buffer; + c->MessageType = REMOTE_NDIS_QUERY_CMPLT; + c->MessageLength = sizeof(rndis_query_cmplt_t) + size; + c->InformationBufferLength = size; + c->InformationBufferOffset = 16; + c->Status = status; + memcpy(c + 1, data, size); + rndis_report(); +} + +#define MAC_OPT NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA | \ + NDIS_MAC_OPTION_RECEIVE_SERIALIZED | \ + NDIS_MAC_OPTION_TRANSFERS_NOT_PEND | \ + NDIS_MAC_OPTION_NO_LOOPBACK + +static const char *rndis_vendor = RNDIS_VENDOR; + +static void rndis_query(void) +{ + switch (((rndis_query_msg_t *)encapsulated_buffer)->Oid) + { + case OID_GEN_SUPPORTED_LIST: rndis_query_cmplt(RNDIS_STATUS_SUCCESS, OIDSupportedList, 4 * OID_LIST_LENGTH); return; + case OID_GEN_VENDOR_DRIVER_VERSION: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0x00001000); return; + case OID_802_3_CURRENT_ADDRESS: rndis_query_cmplt(RNDIS_STATUS_SUCCESS, &station_hwaddr, 6); return; + case OID_802_3_PERMANENT_ADDRESS: rndis_query_cmplt(RNDIS_STATUS_SUCCESS, &permanent_hwaddr, 6); return; + case OID_GEN_MEDIA_SUPPORTED: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, NDIS_MEDIUM_802_3); return; + case OID_GEN_MEDIA_IN_USE: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, NDIS_MEDIUM_802_3); return; + case OID_GEN_PHYSICAL_MEDIUM: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, NDIS_MEDIUM_802_3); return; + case OID_GEN_HARDWARE_STATUS: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0); return; + case OID_GEN_LINK_SPEED: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, RNDIS_LINK_SPEED / 100); return; + case OID_GEN_VENDOR_ID: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0x00FFFFFF); return; + case OID_GEN_VENDOR_DESCRIPTION: rndis_query_cmplt(RNDIS_STATUS_SUCCESS, rndis_vendor, strlen(rndis_vendor) + 1); return; + case OID_GEN_CURRENT_PACKET_FILTER: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, oid_packet_filter); return; + case OID_GEN_MAXIMUM_FRAME_SIZE: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, CFG_TUD_NET_MTU - SIZEOF_ETH_HDR); return; + case OID_GEN_MAXIMUM_TOTAL_SIZE: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, CFG_TUD_NET_MTU); return; + case OID_GEN_TRANSMIT_BLOCK_SIZE: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, CFG_TUD_NET_MTU); return; + case OID_GEN_RECEIVE_BLOCK_SIZE: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, CFG_TUD_NET_MTU); return; + case OID_GEN_MEDIA_CONNECT_STATUS: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, NDIS_MEDIA_STATE_CONNECTED); return; + case OID_GEN_RNDIS_CONFIG_PARAMETER: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0); return; + case OID_802_3_MAXIMUM_LIST_SIZE: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 1); return; + case OID_802_3_MULTICAST_LIST: rndis_query_cmplt32(RNDIS_STATUS_NOT_SUPPORTED, 0); return; + case OID_802_3_MAC_OPTIONS: rndis_query_cmplt32(RNDIS_STATUS_NOT_SUPPORTED, 0); return; + case OID_GEN_MAC_OPTIONS: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, /*MAC_OPT*/ 0); return; + case OID_802_3_RCV_ERROR_ALIGNMENT: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0); return; + case OID_802_3_XMIT_ONE_COLLISION: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0); return; + case OID_802_3_XMIT_MORE_COLLISIONS: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0); return; + case OID_GEN_XMIT_OK: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, usb_eth_stat.txok); return; + case OID_GEN_RCV_OK: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, usb_eth_stat.rxok); return; + case OID_GEN_RCV_ERROR: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, usb_eth_stat.rxbad); return; + case OID_GEN_XMIT_ERROR: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, usb_eth_stat.txbad); return; + case OID_GEN_RCV_NO_BUFFER: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0); return; + default: rndis_query_cmplt(RNDIS_STATUS_FAILURE, NULL, 0); return; + } +} + +#define INFBUF ((uint32_t *)((uint8_t *)&(m->RequestId) + m->InformationBufferOffset)) + +static void rndis_handle_config_parm(const char *data, int keyoffset, int valoffset, int keylen, int vallen) +{ + (void)data; + (void)keyoffset; + (void)valoffset; + (void)keylen; + (void)vallen; +} + +static void rndis_packetFilter(uint32_t newfilter) +{ + (void)newfilter; +} + +static void rndis_handle_set_msg(void) +{ + rndis_set_cmplt_t *c; + rndis_set_msg_t *m; + rndis_Oid_t oid; + + c = (rndis_set_cmplt_t *)encapsulated_buffer; + m = (rndis_set_msg_t *)encapsulated_buffer; + + oid = m->Oid; + c->MessageType = REMOTE_NDIS_SET_CMPLT; + c->MessageLength = sizeof(rndis_set_cmplt_t); + c->Status = RNDIS_STATUS_SUCCESS; + + switch (oid) + { + /* Parameters set up in 'Advanced' tab */ + case OID_GEN_RNDIS_CONFIG_PARAMETER: + { + rndis_config_parameter_t *p; + char *ptr = (char *)m; + ptr += sizeof(rndis_generic_msg_t); + ptr += m->InformationBufferOffset; + p = (rndis_config_parameter_t *)ptr; + rndis_handle_config_parm(ptr, p->ParameterNameOffset, p->ParameterValueOffset, p->ParameterNameLength, p->ParameterValueLength); + } + break; + + /* Mandatory general OIDs */ + case OID_GEN_CURRENT_PACKET_FILTER: + oid_packet_filter = *INFBUF; + if (oid_packet_filter) + { + rndis_packetFilter(oid_packet_filter); + rndis_state = rndis_data_initialized; + } + else + { + rndis_state = rndis_initialized; + } + break; + + case OID_GEN_CURRENT_LOOKAHEAD: + break; + + case OID_GEN_PROTOCOL_OPTIONS: + break; + + /* Mandatory 802_3 OIDs */ + case OID_802_3_MULTICAST_LIST: + break; + + /* Power Managment: fails for now */ + case OID_PNP_ADD_WAKE_UP_PATTERN: + case OID_PNP_REMOVE_WAKE_UP_PATTERN: + case OID_PNP_ENABLE_WAKE_UP: + default: + c->Status = RNDIS_STATUS_FAILURE; + break; + } + + /* c->MessageID is same as before */ + rndis_report(); + return; +} + +void rndis_class_set_handler(uint8_t *data, int size) +{ + encapsulated_buffer = data; + (void)size; + + switch (((rndis_generic_msg_t *)data)->MessageType) + { + case REMOTE_NDIS_INITIALIZE_MSG: + { + rndis_initialize_cmplt_t *m; + m = ((rndis_initialize_cmplt_t *)encapsulated_buffer); + /* m->MessageID is same as before */ + m->MessageType = REMOTE_NDIS_INITIALIZE_CMPLT; + m->MessageLength = sizeof(rndis_initialize_cmplt_t); + m->MajorVersion = RNDIS_MAJOR_VERSION; + m->MinorVersion = RNDIS_MINOR_VERSION; + m->Status = RNDIS_STATUS_SUCCESS; + m->DeviceFlags = RNDIS_DF_CONNECTIONLESS; + m->Medium = RNDIS_MEDIUM_802_3; + m->MaxPacketsPerTransfer = 1; + m->MaxTransferSize = CFG_TUD_NET_MTU + sizeof(rndis_data_packet_t); + m->PacketAlignmentFactor = 0; + m->AfListOffset = 0; + m->AfListSize = 0; + rndis_state = rndis_initialized; + rndis_report(); + } + break; + + case REMOTE_NDIS_QUERY_MSG: + rndis_query(); + break; + + case REMOTE_NDIS_SET_MSG: + rndis_handle_set_msg(); + break; + + case REMOTE_NDIS_RESET_MSG: + { + rndis_reset_cmplt_t * m; + m = ((rndis_reset_cmplt_t *)encapsulated_buffer); + rndis_state = rndis_uninitialized; + m->MessageType = REMOTE_NDIS_RESET_CMPLT; + m->MessageLength = sizeof(rndis_reset_cmplt_t); + m->Status = RNDIS_STATUS_SUCCESS; + m->AddressingReset = 1; /* Make it look like we did something */ + /* m->AddressingReset = 0; - Windows halts if set to 1 for some reason */ + rndis_report(); + } + break; + + case REMOTE_NDIS_KEEPALIVE_MSG: + { + rndis_keepalive_cmplt_t * m; + m = (rndis_keepalive_cmplt_t *)encapsulated_buffer; + m->MessageType = REMOTE_NDIS_KEEPALIVE_CMPLT; + m->MessageLength = sizeof(rndis_keepalive_cmplt_t); + m->Status = RNDIS_STATUS_SUCCESS; + } + /* We have data to send back */ + rndis_report(); + break; + + default: + break; + } +} diff --git a/src/class/net/net_device.c b/src/class/net/net_device.c new file mode 100644 index 000000000..422d435c6 --- /dev/null +++ b/src/class/net/net_device.c @@ -0,0 +1,345 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020 Peter Lawrence + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if ( TUSB_OPT_DEVICE_ENABLED && (CFG_TUD_NET != OPT_NET_NONE) ) + +#include "net_device.h" +#include "device/usbd_pvt.h" +#include "rndis_protocol.h" + +void rndis_class_set_handler(uint8_t *data, int size); /* found in ./misc/networking/rndis_reports.c */ + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +typedef struct +{ + uint8_t itf_num; + uint8_t ep_notif; + uint8_t ep_in; + uint8_t ep_out; +} netd_interface_t; + +#if CFG_TUD_NET == OPT_NET_ECM + #define CFG_TUD_NET_PACKET_PREFIX_LEN 0 + #define CFG_TUD_NET_PACKET_SUFFIX_LEN 0 + #define CFG_TUD_NET_INTERFACESUBCLASS CDC_COMM_SUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL +#elif CFG_TUD_NET == OPT_NET_RNDIS + #define CFG_TUD_NET_PACKET_PREFIX_LEN sizeof(rndis_data_packet_t) + #define CFG_TUD_NET_PACKET_SUFFIX_LEN 0 + #define CFG_TUD_NET_INTERFACESUBCLASS TUD_RNDIS_ITF_SUBCLASS +#elif CFG_TUD_NET == OPT_NET_EEM + #define CFG_TUD_NET_PACKET_PREFIX_LEN 2 + #define CFG_TUD_NET_PACKET_SUFFIX_LEN 4 + #define CFG_TUD_NET_INTERFACESUBCLASS CDC_COMM_SUBCLASS_ETHERNET_EMULATION_MODEL +#endif + +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t received[CFG_TUD_NET_PACKET_PREFIX_LEN + CFG_TUD_NET_MTU + CFG_TUD_NET_PACKET_PREFIX_LEN]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t transmitted[CFG_TUD_NET_PACKET_PREFIX_LEN + CFG_TUD_NET_MTU + CFG_TUD_NET_PACKET_PREFIX_LEN]; + +#if CFG_TUD_NET == OPT_NET_RNDIS + CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t rndis_buf[128]; +#endif + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ +CFG_TUSB_MEM_SECTION static netd_interface_t _netd_itf; + +static bool can_xmit; + +void network_recv_renew(void) +{ + usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_out, received, sizeof(received)); +} + +static void do_in_xfer(uint8_t *buf, uint16_t len) +{ + can_xmit = false; + usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_in, buf, len); +} + +void netd_report(uint8_t *buf, uint16_t len) +{ + usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_notif, buf, len); +} + +//--------------------------------------------------------------------+ +// USBD Driver API +//--------------------------------------------------------------------+ +void netd_init(void) +{ + tu_memclr(&_netd_itf, sizeof(_netd_itf)); +} + +void netd_reset(uint8_t rhport) +{ + (void) rhport; + + netd_init(); +} + +bool netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length) +{ + // sanity check the descriptor + TU_ASSERT (CFG_TUD_NET_INTERFACESUBCLASS == itf_desc->bInterfaceSubClass); + + // confirm interface hasn't already been allocated + TU_ASSERT(0 == _netd_itf.ep_in); + + //------------- first Interface -------------// + _netd_itf.itf_num = itf_desc->bInterfaceNumber; + + uint8_t const * p_desc = tu_desc_next( itf_desc ); + (*p_length) = sizeof(tusb_desc_interface_t); + +#if CFG_TUD_NET != OPT_NET_EEM + // Communication Functional Descriptors + while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) ) + { + (*p_length) += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } + + // notification endpoint (if any) + if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) ) + { + TU_ASSERT( dcd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc) ); + + _netd_itf.ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress; + + (*p_length) += p_desc[DESC_OFFSET_LEN]; + p_desc = tu_desc_next(p_desc); + } + + //------------- second Interface -------------// + if ( (TUSB_DESC_INTERFACE == p_desc[DESC_OFFSET_TYPE]) && + (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) ) + { + // next to endpoint descriptor + p_desc = tu_desc_next(p_desc); + (*p_length) += sizeof(tusb_desc_interface_t); + } +#endif + + if (TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE]) + { + // Open endpoint pair + TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in) ); + + (*p_length) += 2*sizeof(tusb_desc_endpoint_t); + } + + network_init_callback(); + + // we are ready to transmit a packet + can_xmit = true; + + // prepare for incoming packets + network_recv_renew(); + + return true; +} + +// Invoked when class request DATA stage is finished. +// return false to stall control endpoint (e.g Host send nonsense DATA) +bool netd_control_complete(uint8_t rhport, tusb_control_request_t const * request) +{ + (void) rhport; + + // Handle class request only + TU_VERIFY (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); + + TU_VERIFY (_netd_itf.itf_num == request->wIndex); + +#if CFG_TUD_NET == OPT_NET_RNDIS + if (request->bmRequestType_bit.direction == TUSB_DIR_OUT) + { + rndis_class_set_handler(rndis_buf, request->wLength); + } +#endif + + return true; +} + +// Handle class control request +// return false to stall control endpoint (e.g unsupported request) +bool netd_control_request(uint8_t rhport, tusb_control_request_t const * request) +{ + // Handle class request only + TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); + + TU_VERIFY (_netd_itf.itf_num == request->wIndex); + +#if CFG_TUD_NET == OPT_NET_RNDIS + tud_control_xfer(rhport, request, rndis_buf, sizeof(rndis_buf)); +#else + (void)rhport; +#endif + + return true; +} + +struct cdc_eem_packet_header +{ + uint16_t length:14; + uint16_t bmCRC:1; + uint16_t bmType:1; +}; + +static void handle_incoming_packet(uint32_t len) +{ + uint8_t *pnt = received; + uint32_t size = 0; + +#if CFG_TUD_NET == OPT_NET_ECM + size = len; +#elif CFG_TUD_NET == OPT_NET_RNDIS + rndis_data_packet_t *r = (rndis_data_packet_t *)pnt; + if (len >= sizeof(rndis_data_packet_t)) + if ( (r->MessageType == REMOTE_NDIS_PACKET_MSG) && (r->MessageLength <= len)) + if ( (r->DataOffset + offsetof(rndis_data_packet_t, DataOffset) + r->DataLength) <= len) + { + pnt = &received[r->DataOffset + offsetof(rndis_data_packet_t, DataOffset)]; + size = r->DataLength; + } +#elif CFG_TUD_NET == OPT_NET_EEM + struct cdc_eem_packet_header *hdr = (struct cdc_eem_packet_header *)pnt; + + (void)len; + + if (hdr->bmType) + { + /* EEM Control Packet: discard it */ + network_recv_renew(); + } + else + { + /* EEM Data Packet */ + pnt += CFG_TUD_NET_PACKET_PREFIX_LEN; + size = hdr->length - 4; /* discard the unused CRC-32 */ + } +#endif + + if (size) + { + struct pbuf *p = pbuf_alloc(PBUF_RAW, size, PBUF_POOL); + bool accepted = true; + + if (p) + { + memcpy(p->payload, pnt, size); + p->len = size; + accepted = network_recv_callback(p); + } + + if (!p || !accepted) + { + /* if a buffer couldn't be allocated or accepted by the callback, we must discard this packet */ + network_recv_renew(); + } + } +} + +bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) +{ + (void) rhport; + (void) result; + + /* new packet received */ + if ( ep_addr == _netd_itf.ep_out ) + { + handle_incoming_packet(xferred_bytes); + } + + /* data transmission finished */ + if ( ep_addr == _netd_itf.ep_in ) + { + /* TinyUSB requires the class driver to implement ZLP (since ZLP usage is class-specific) */ + + if ( xferred_bytes && (0 == (xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE)) ) + { + do_in_xfer(NULL, 0); /* a ZLP is needed */ + } + else + { + /* we're finally finished */ + can_xmit = true; + } + } + + return true; +} + +bool network_can_xmit(void) +{ + return can_xmit; +} + +void network_xmit(struct pbuf *p) +{ + struct pbuf *q; + uint8_t *data; + uint16_t len; + + if (!can_xmit) + return; + + len = CFG_TUD_NET_PACKET_PREFIX_LEN; + data = transmitted + len; + + for(q = p; q != NULL; q = q->next) + { + memcpy(data, (char *)q->payload, q->len); + data += q->len; + len += q->len; + } + +#if CFG_TUD_NET == OPT_NET_RNDIS + rndis_data_packet_t *hdr = (rndis_data_packet_t *)transmitted; + memset(hdr, 0, sizeof(rndis_data_packet_t)); + hdr->MessageType = REMOTE_NDIS_PACKET_MSG; + hdr->MessageLength = len; + hdr->DataOffset = sizeof(rndis_data_packet_t) - offsetof(rndis_data_packet_t, DataOffset); + hdr->DataLength = len - sizeof(rndis_data_packet_t); +#elif CFG_TUD_NET == OPT_NET_EEM + struct cdc_eem_packet_header *hdr = (struct cdc_eem_packet_header *)transmitted; + /* append a fake CRC-32; the standard allows 0xDEADBEEF, which takes less CPU time */ + data[0] = 0xDE; data[1] = 0xAD; data[2] = 0xBE; data[3] = 0xEF; + /* adjust length to reflect added fake CRC-32 */ + len += 4; + hdr->bmType = 0; /* EEM Data Packet */ + hdr->length = len - sizeof(struct cdc_eem_packet_header); + hdr->bmCRC = 0; /* Ethernet Frame CRC-32 set to 0xDEADBEEF */ +#endif + + do_in_xfer(transmitted, len); +} + +#endif diff --git a/src/class/net/net_device.h b/src/class/net/net_device.h new file mode 100644 index 000000000..c61efc86c --- /dev/null +++ b/src/class/net/net_device.h @@ -0,0 +1,84 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020 Peter Lawrence + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_NET_DEVICE_H_ +#define _TUSB_NET_DEVICE_H_ + +#include "common/tusb_common.h" +#include "device/usbd.h" +#include "class/cdc/cdc.h" +#include "lwip/pbuf.h" +#include "netif/ethernet.h" + +/* declared here, NOT in usb_descriptors.c, so that the driver can intelligently ZLP as needed */ +#define CFG_TUD_NET_ENDPOINT_SIZE ((CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 512 : 64) + +/* Maximum Tranmission Unit (in bytes) of the network, including Ethernet header */ +#define CFG_TUD_NET_MTU (1500 + SIZEOF_ETH_HDR) + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ + +// client must provide this: initialize any network state back to the beginning +void network_init_callback(void); + +// client must provide this: return false if the packet buffer was not accepted +bool network_recv_callback(struct pbuf *p); + +// client must provide this: 48-bit MAC address +extern const uint8_t network_mac_address[6]; + +// indicate to network driver that client has finished with the packet provided to network_recv_callback() +void network_recv_renew(void); + +// poll network driver for its ability to accept another packet to transmit +bool network_can_xmit(void); + +// if network_can_xmit() returns true, network_xmit() can be called once +void network_xmit(struct pbuf *p); + +//--------------------------------------------------------------------+ +// INTERNAL USBD-CLASS DRIVER API +//--------------------------------------------------------------------+ +void netd_init (void); +void netd_reset (uint8_t rhport); +bool netd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length); +bool netd_control_request (uint8_t rhport, tusb_control_request_t const * request); +bool netd_control_complete (uint8_t rhport, tusb_control_request_t const * request); +bool netd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); +void netd_report (uint8_t *buf, uint16_t len); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_NET_DEVICE_H_ */ diff --git a/src/device/usbd.c b/src/device/usbd.c index 68c5c5160..32a89889e 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -181,6 +181,24 @@ static usbd_class_driver_t const _usbd_driver[] = .sof = NULL }, #endif + + #if CFG_TUD_NET + { + .class_code = +#if CFG_TUD_NET == OPT_NET_RNDIS + TUD_RNDIS_ITF_CLASS, +#else + TUSB_CLASS_CDC, +#endif + .init = netd_init, + .reset = netd_reset, + .open = netd_open, + .control_request = netd_control_request, + .control_complete = netd_control_complete, + .xfer_cb = netd_xfer_cb, + .sof = NULL + }, + #endif }; enum { USBD_CLASS_DRIVER_COUNT = TU_ARRAY_SIZE(_usbd_driver) }; diff --git a/src/device/usbd.h b/src/device/usbd.h index d80abf17c..07163a013 100644 --- a/src/device/usbd.h +++ b/src/device/usbd.h @@ -326,6 +326,90 @@ TU_ATTR_WEAK bool tud_vendor_control_complete_cb(uint8_t rhport, tusb_control_re 9, DFU_DESC_FUNCTIONAL, _attr, U16_TO_U8S_LE(_timeout), U16_TO_U8S_LE(_xfer_size), U16_TO_U8S_LE(0x0101) +//------------- CDC-ECM -------------// + +// Length of template descriptor: 62 bytes +#define TUD_CDC_ECM_DESC_LEN (9+5+5+13+7+9+7+7) + +// CDC-ECM Descriptor Template +// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size. +#define TUD_CDC_ECM_DESCRIPTOR(_itfnum, _desc_stridx, _mac_stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize, _maxsegmentsize) \ + /* CDC Control Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL, 0, _desc_stridx,\ + /* CDC-ECM Header */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0120),\ + /* CDC-ECM Union */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\ + /* CDC-ECM Functional Descriptor */\ + 13, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ETHERNET_NETWORKING, _mac_stridx, 0, 0, 0, 0, U16_TO_U8S_LE(_maxsegmentsize), U16_TO_U8S_LE(0), 0,\ + /* Endpoint Notification */\ + 7, TUSB_DESC_ENDPOINT, _ep_notif, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_notif_size), 1,\ + /* CDC Data Interface */\ + 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 2, TUSB_CLASS_CDC_DATA, 0, 0, 0,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 + + +//------------- RNDIS -------------// + +#if 0 + /* Windows XP */ + #define TUD_RNDIS_ITF_CLASS TUSB_CLASS_CDC + #define TUD_RNDIS_ITF_SUBCLASS CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL + #define TUD_RNDIS_ITF_PROTOCOL CDC_COMM_PROTOCOL_MICROSOFT_RNDIS +#else + /* Windows 7+ */ + #define TUD_RNDIS_ITF_CLASS 0xE0 + #define TUD_RNDIS_ITF_SUBCLASS 0x01 + #define TUD_RNDIS_ITF_PROTOCOL 0x03 +#endif + +// Length of template descriptor: 66 bytes +#define TUD_RNDIS_DESC_LEN (8+9+5+5+4+5+7+9+7+7) + +// RNDIS Descriptor Template +// Interface number, string index, EP notification address and size, EP data address (out, in) and size. +#define TUD_RNDIS_DESCRIPTOR(_itfnum, _stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize) \ + /* Interface Association */\ + 8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUD_RNDIS_ITF_CLASS, TUD_RNDIS_ITF_SUBCLASS, TUD_RNDIS_ITF_PROTOCOL, 0,\ + /* CDC Control Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUD_RNDIS_ITF_CLASS, TUD_RNDIS_ITF_SUBCLASS, TUD_RNDIS_ITF_PROTOCOL, _stridx,\ + /* CDC-ACM Header */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0110),\ + /* CDC Call Management */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_CALL_MANAGEMENT, 0, (uint8_t)((_itfnum) + 1),\ + /* ACM */\ + 4, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, 0,\ + /* CDC Union */\ + 5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\ + /* Endpoint Notification */\ + 7, TUSB_DESC_ENDPOINT, _ep_notif, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_notif_size), 1,\ + /* CDC Data Interface */\ + 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 2, TUSB_CLASS_CDC_DATA, 0, 0, 0,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 + + +//------------- CDC-EEM -------------// + +// Length of template descriptor: 23 bytes +#define TUD_CDC_EEM_DESC_LEN (9+7+7) + +// CDC-EEM Descriptor Template +// Interface number, description string index, EP data address (out, in) and size. +#define TUD_CDC_EEM_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \ + /* EEM Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_ETHERNET_EMULATION_MODEL, CDC_COMM_PROTOCOL_ETHERNET_EMULATION_MODEL, _stridx,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 + + #ifdef __cplusplus } #endif diff --git a/src/tusb.h b/src/tusb.h index 7dcddf645..55c105121 100644 --- a/src/tusb.h +++ b/src/tusb.h @@ -91,6 +91,10 @@ #if CFG_TUD_DFU_RT #include "class/dfu/dfu_rt_device.h" #endif + + #if CFG_TUD_NET + #include "class/net/net_device.h" + #endif #endif diff --git a/src/tusb_option.h b/src/tusb_option.h index 97f14532f..00d4a39dc 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -117,6 +117,15 @@ #define OPT_MODE_HIGH_SPEED 0x10 ///< High speed /** @} */ +/** \defgroup group_supported_netif Supported Network Interface + * \ref CFG_TUD_NET must be defined to one of these + * @{ */ +#define OPT_NET_NONE 0 ///< No network interface +#define OPT_NET_ECM 1 ///< CDC-ECM +#define OPT_NET_RNDIS 2 ///< RNDIS +#define OPT_NET_EEM 3 ///< CDC-EEM +/** @} */ + #ifndef CFG_TUSB_RHPORT0_MODE #define CFG_TUSB_RHPORT0_MODE OPT_MODE_NONE #endif @@ -204,6 +213,9 @@ #define CFG_TUD_DFU_RT 0 #endif +#ifndef CFG_TUD_NET + #define CFG_TUD_NET 0 +#endif //-------------------------------------------------------------------- // HOST OPTIONS -- cgit v1.3.1 From 7aa5a53652270a06dc60e6293393755350bf7c85 Mon Sep 17 00:00:00 2001 From: hathach Date: Sun, 8 Mar 2020 13:26:36 +0700 Subject: fix log printf --- src/device/usbd.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/device') diff --git a/src/device/usbd.c b/src/device/usbd.c index 32a89889e..335c68e8e 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -264,6 +264,9 @@ static char const* const _usbd_driver_str[USBD_CLASS_DRIVER_COUNT] = #if CFG_TUD_USBTMC "USBTMC" #endif + #if CFG_TUD_NET + "NET" + #endif }; static char const* const _tusb_std_request_str[] = -- cgit v1.3.1 From c8247f0907040eb86c301e8bff8bb322fc9cb566 Mon Sep 17 00:00:00 2001 From: hathach Date: Sun, 8 Mar 2020 14:20:28 +0700 Subject: fix zlp for nrf52840 --- src/device/usbd_control.c | 6 ++++-- src/portable/nordic/nrf5x/dcd_nrf5x.c | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'src/device') diff --git a/src/device/usbd_control.c b/src/device/usbd_control.c index 5a0ba412f..f0a4cf84a 100644 --- a/src/device/usbd_control.c +++ b/src/device/usbd_control.c @@ -96,6 +96,8 @@ static bool _data_stage_xact(uint8_t rhport) if ( xact_len ) memcpy(_usbd_ctrl_buf, _ctrl_xfer.buffer, xact_len); } + TU_LOG2(" XACT Control: 0x%02X, Bytes: %d\n", ep_addr, xact_len); + return dcd_edpt_xfer(rhport, ep_addr, xact_len ? _usbd_ctrl_buf : NULL, xact_len); } @@ -110,10 +112,10 @@ bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, vo { TU_ASSERT(buffer); + TU_LOG2(" XFER Endpoint: 0x%02X, Bytes: %d\n", request->bmRequestType_bit.direction ? EDPT_CTRL_IN : EDPT_CTRL_OUT, _ctrl_xfer.data_len); + // Data stage TU_ASSERT( _data_stage_xact(rhport) ); - - TU_LOG2(" XFER Endpoint: 0x%02X, Bytes: %d\n", request->bmRequestType_bit.direction ? EDPT_CTRL_IN : EDPT_CTRL_OUT, _ctrl_xfer.data_len); }else { // Status stage diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c index 59c2f36aa..2ce288d6c 100644 --- a/src/portable/nordic/nrf5x/dcd_nrf5x.c +++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c @@ -276,8 +276,10 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t xfer->total_len = total_bytes; xfer->actual_len = 0; - // Control endpoint with zero-length packet --> status stage - if ( epnum == 0 && total_bytes == 0 ) + // Control endpoint with zero-length packet and opposite direction to 1st request byte --> status stage + bool const control_status = (epnum == 0 && total_bytes == 0 && dir != tu_edpt_dir(NRF_USBD->BMREQUESTTYPE)); + + if ( control_status ) { // Status Phase also require Easy DMA has to be free as well !!!! edpt_dma_start(&NRF_USBD->TASKS_EP0STATUS); -- cgit v1.3.1