From 6baa79b3308d4637f7a22ad73a5ac26f9ef6b491 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 4 Nov 2019 14:54:04 +0700 Subject: board test works --- src/portable/template/dcd_template.c | 116 +++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 src/portable/template/dcd_template.c (limited to 'src/portable') diff --git a/src/portable/template/dcd_template.c b/src/portable/template/dcd_template.c new file mode 100644 index 000000000..102910509 --- /dev/null +++ b/src/portable/template/dcd_template.c @@ -0,0 +1,116 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018, hathach (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 CFG_TUSB_MCU == OPT_MCU_NONE + +#include "device/dcd.h" + +//--------------------------------------------------------------------+ +// MACRO TYPEDEF CONSTANT ENUM DECLARATION +//--------------------------------------------------------------------+ + + +/*------------------------------------------------------------------*/ +/* Device API + *------------------------------------------------------------------*/ + +// Initialize controller to device mode +void dcd_init (uint8_t rhport) +{ + (void) rhport; +} + +// Enable device interrupt +void dcd_int_enable (uint8_t rhport) +{ + (void) rhport; +} + +// Disable device interrupt +void dcd_int_disable (uint8_t rhport) +{ + (void) rhport; +} + +// Receive Set Address request, mcu port must also include status IN response +void dcd_set_address (uint8_t rhport, uint8_t dev_addr) +{ + (void) rhport; + (void) dev_addr; +} + +// Receive Set Configure request +void dcd_set_config (uint8_t rhport, uint8_t config_num) +{ + (void) rhport; + (void) config_num; +} + +// Wake up host +void dcd_remote_wakeup (uint8_t rhport) +{ + (void) rhport; +} + +//--------------------------------------------------------------------+ +// Endpoint API +//--------------------------------------------------------------------+ + +// Configure endpoint's registers according to descriptor +bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) +{ + (void) rhport; + (void) ep_desc; + return false; +} + +// 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) +{ + (void) rhport; + (void) ep_addr; + (void) buffer; + (void) total_bytes; + return false; +} + +// Stall endpoint +void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + (void) ep_addr; +} + +// clear stall, data toggle is also reset to DATA0 +void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + (void) ep_addr; +} + +#endif -- cgit v1.3.1 From 4008f0d1e66dff6bb42a0fb0c682bf4bfe8e6c4a Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 11 Nov 2019 00:01:12 +0700 Subject: update dcd nrf5x to be indepent from nrf_usbd.h fix build error with nrfx 2.0 --- hw/bsp/pca10056/pca10056.c | 8 +++++--- hw/mcu/nordic/nrfx_config.h | 12 +++++++++-- src/portable/nordic/nrf5x/dcd_nrf5x.c | 38 ++++++++++++++++++++--------------- 3 files changed, 37 insertions(+), 21 deletions(-) (limited to 'src/portable') diff --git a/hw/bsp/pca10056/pca10056.c b/hw/bsp/pca10056/pca10056.c index 31fd5e9be..b9cc9fc36 100644 --- a/hw/bsp/pca10056/pca10056.c +++ b/hw/bsp/pca10056/pca10056.c @@ -84,10 +84,12 @@ void board_init(void) .pselcts = NRF_UARTE_PSEL_DISCONNECTED, .pselrts = NRF_UARTE_PSEL_DISCONNECTED, .p_context = NULL, - .hwfc = NRF_UARTE_HWFC_DISABLED, - .parity = NRF_UARTE_PARITY_EXCLUDED, .baudrate = NRF_UARTE_BAUDRATE_115200, // CFG_BOARD_UART_BAUDRATE - .interrupt_priority = 7 + .interrupt_priority = 7, + .hal_cfg = { + .hwfc = NRF_UARTE_HWFC_DISABLED, + .parity = NRF_UARTE_PARITY_EXCLUDED, + } }; nrfx_uarte_init(&_uart_id, &uart_cfg, NULL); //uart_handler); diff --git a/hw/mcu/nordic/nrfx_config.h b/hw/mcu/nordic/nrfx_config.h index 8f9aeffe5..6a974ba70 100644 --- a/hw/mcu/nordic/nrfx_config.h +++ b/hw/mcu/nordic/nrfx_config.h @@ -1,10 +1,18 @@ #ifndef NRFX_CONFIG_H__ #define NRFX_CONFIG_H__ -#define NRFX_POWER_ENABLED 1 -#define NRFX_POWER_CONFIG_IRQ_PRIORITY 7 +#define NRFX_POWER_ENABLED 1 +#define NRFX_POWER_DEFAULT_CONFIG_IRQ_PRIORITY 7 + +#define NRFX_CLOCK_ENABLED 0 #define NRFX_UARTE_ENABLED 1 #define NRFX_UARTE0_ENABLED 1 +#define NRFX_UARTE1_ENABLED 0 +#define NRFX_UARTE2_ENABLED 0 +#define NRFX_UARTE3_ENABLED 0 + +#define NRFX_PRS_ENABLED 0 + #endif // NRFX_CONFIG_H__ diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c index 17140ee75..59c2f36aa 100644 --- a/src/portable/nordic/nrf5x/dcd_nrf5x.c +++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c @@ -31,7 +31,6 @@ #include "nrf.h" #include "nrf_clock.h" #include "nrf_power.h" -#include "nrf_usbd.h" #include "nrfx_usbd_errata.h" #ifdef SOFTDEVICE_PRESENT @@ -498,7 +497,8 @@ void USBD_IRQHandler(void) if ( int_status & (USBD_INTEN_EPDATA_Msk | USBD_INTEN_EP0DATADONE_Msk) ) { uint32_t data_status = NRF_USBD->EPDATASTATUS; - nrf_usbd_epdatastatus_clear(data_status); + NRF_USBD->EPDATASTATUS = data_status; + __ISB(); __DSB(); // EP0DATADONE is set with either Control Out on IN Data // Since EPDATASTATUS cannot be used to determine whether it is control OUT or IN. @@ -572,7 +572,7 @@ static bool hfclk_running(void) } #endif - return nrf_clock_hf_is_running(NRF_CLOCK_HFCLK_HIGH_ACCURACY); + return nrf_clock_hf_is_running(NRF_CLOCK, NRF_CLOCK_HFCLK_HIGH_ACCURACY); } static void hfclk_enable(void) @@ -588,8 +588,8 @@ static void hfclk_enable(void) } #endif - nrf_clock_event_clear(NRF_CLOCK_EVENT_HFCLKSTARTED); - nrf_clock_task_trigger(NRF_CLOCK_TASK_HFCLKSTART); + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_HFCLKSTARTED); + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTART); } static void hfclk_disable(void) @@ -602,7 +602,7 @@ static void hfclk_disable(void) } #endif - nrf_clock_task_trigger(NRF_CLOCK_TASK_HFCLKSTOP); + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTOP); } // Power & Clock Peripheral on nRF5x to manage USB @@ -630,7 +630,8 @@ void tusb_hal_nrf_power_event (uint32_t event) if ( !NRF_USBD->ENABLE ) { /* Prepare for READY event receiving */ - nrf_usbd_eventcause_clear(NRF_USBD_EVENTCAUSE_READY_MASK); + NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk; + __ISB(); __DSB(); // for sync /* Enable the peripheral */ // ERRATA 171, 187, 166 @@ -667,7 +668,8 @@ void tusb_hal_nrf_power_event (uint32_t event) // CRITICAL_REGION_EXIT(); } - nrf_usbd_enable(); + NRF_USBD->ENABLE = 1; + __ISB(); __DSB(); // for sync // Enable HFCLK hfclk_enable(); @@ -678,8 +680,8 @@ void tusb_hal_nrf_power_event (uint32_t event) /* Waiting for USBD peripheral enabled */ while ( !(USBD_EVENTCAUSE_READY_Msk & NRF_USBD->EVENTCAUSE) ) { } - nrf_usbd_eventcause_clear(USBD_EVENTCAUSE_READY_Msk); - nrf_usbd_event_clear(USBD_EVENTCAUSE_READY_Msk); + NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk; + __ISB(); __DSB(); // for sync if ( nrfx_usbd_errata_171() ) { @@ -719,11 +721,11 @@ void tusb_hal_nrf_power_event (uint32_t event) *((volatile uint32_t *) (NRF_USBD_BASE + 0x800)) = 0x7E3; *((volatile uint32_t *) (NRF_USBD_BASE + 0x804)) = 0x40; - __ISB(); - __DSB(); + __ISB(); __DSB(); } - nrf_usbd_isosplit_set(USBD_ISOSPLIT_SPLIT_HalfIN); + // ISO buffer Lower half for IN, upper half for OUT + NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN; // Enable interrupt NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk | USBD_INTEN_EPDATA_Msk | @@ -737,7 +739,8 @@ void tusb_hal_nrf_power_event (uint32_t event) while ( !hfclk_running() ) { } // Enable pull up - nrf_usbd_pullup_enable(); + NRF_USBD->USBPULLUP = 1; + __ISB(); __DSB(); // for sync break; case USB_EVT_REMOVED: @@ -746,7 +749,8 @@ void tusb_hal_nrf_power_event (uint32_t event) // Abort all transfers // Disable pull up - nrf_usbd_pullup_disable(); + NRF_USBD->USBPULLUP = 0; + __ISB(); __DSB(); // for sync // Disable Interrupt NVIC_DisableIRQ(USBD_IRQn); @@ -754,7 +758,9 @@ void tusb_hal_nrf_power_event (uint32_t event) // disable all interrupt NRF_USBD->INTENCLR = NRF_USBD->INTEN; - nrf_usbd_disable(); + NRF_USBD->ENABLE = 0; + __ISB(); __DSB(); // for sync + hfclk_disable(); dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, true); -- cgit v1.3.1 From 36ede44885365faa230b63867e8392addd48e7b3 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Sat, 12 Oct 2019 10:25:10 +0800 Subject: fomu: wip support Signed-off-by: Sean Cross --- src/portable/foosn/fomu/dcd_fomu.c | 335 +++++++++++++++++++++++++++++++++++++ src/portable/foosn/fomu/dcd_fomu.h | 151 +++++++++++++++++ src/portable/foosn/fomu/hal_fomu.c | 33 ++++ 3 files changed, 519 insertions(+) create mode 100644 src/portable/foosn/fomu/dcd_fomu.c create mode 100644 src/portable/foosn/fomu/dcd_fomu.h create mode 100644 src/portable/foosn/fomu/hal_fomu.c (limited to 'src/portable') diff --git a/src/portable/foosn/fomu/dcd_fomu.c b/src/portable/foosn/fomu/dcd_fomu.c new file mode 100644 index 000000000..d7f9d3d1e --- /dev/null +++ b/src/portable/foosn/fomu/dcd_fomu.c @@ -0,0 +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. + */ + +#include "tusb_option.h" + +// #if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_FOMU_EPTRI) +#if 1 + +#include "device/dcd.h" +#include "dcd_fomu.h" +#include "csr.h" +#include "irq.h" +void fomu_error(uint32_t line); +void mputs(const char *str); +void mputln(const char *str); + +//--------------------------------------------------------------------+ +// SIE Command +//--------------------------------------------------------------------+ + +static uint8_t volatile out_buffer_length[16]; +static uint8_t volatile * out_buffer[16]; +static uint8_t volatile out_buffer_max[16]; + +static volatile bool tx_in_progress; +static volatile uint8_t tx_ep; +static volatile uint16_t tx_len; + +//--------------------------------------------------------------------+ +// PIPE HELPER +//--------------------------------------------------------------------+ + +static void finish_tx(void) { + // // Don't allow requeueing -- only queue more data if the system is idle. + // if (!(usb_in_status_read() & 2)) { + // return; + // } + + // Don't send empty data + if (!tx_in_progress) { + return; + } + + tx_in_progress = 0; + dcd_event_xfer_complete(0, tx_ep, tx_len, XFER_RESULT_SUCCESS, true); + return; +} + +static void process_rx(bool in_isr) { + // If there isn't any data in the FIFO, don't do anything. + if (!(usb_out_status_read() & 1)) + return; + + uint8_t out_ep = (usb_out_status_read() >> 2) & 0xf; + uint32_t total_read = 0; + uint32_t current_offset = out_buffer_length[out_ep]; + while (usb_out_status_read() & 1) { + uint8_t c = usb_out_data_read(); + total_read++; + if (out_buffer_length[out_ep] < out_buffer_max[out_ep]) + out_buffer[out_ep][current_offset++] = c; + } + + // Strip off the CRC16 + total_read -= 2; + out_buffer_length[out_ep] += total_read; + if (out_buffer_length[out_ep] > out_buffer_max[out_ep]) + out_buffer_length[out_ep] = out_buffer_max[out_ep]; + + if (out_buffer_max[out_ep] == out_buffer_length[out_ep]) { + out_buffer[out_ep] = NULL; + dcd_event_xfer_complete(0, tu_edpt_addr(out_ep, TUSB_DIR_OUT), out_buffer_length[out_ep], XFER_RESULT_SUCCESS, in_isr); + } + + // Acknowledge having received the data + usb_out_ctrl_write(2); +} + +//--------------------------------------------------------------------+ +// CONTROLLER API +//--------------------------------------------------------------------+ + +// Initializes the USB peripheral for device mode and enables it. +void dcd_init(uint8_t rhport) +{ + (void) rhport; + + usb_pullup_out_write(0); + usb_address_write(0); + usb_out_ctrl_write(0); + + usb_setup_ev_enable_write(0); + usb_in_ev_enable_write(0); + usb_out_ev_enable_write(0); + + // Reset the IN handler + usb_in_ctrl_write(0x20); + + // Reset the SETUP handler + usb_setup_ctrl_write(0x04); + + // Reset the OUT handler + usb_out_ctrl_write(0x04); + + // Enable all event handlers and clear their contents + usb_setup_ev_pending_write(usb_setup_ev_pending_read()); + usb_in_ev_pending_write(usb_in_ev_pending_read()); + usb_out_ev_pending_write(usb_out_ev_pending_read()); + usb_setup_ev_enable_write(3); + usb_in_ev_enable_write(1); + usb_out_ev_enable_write(1); + + // Accept incoming data by default. + usb_out_ctrl_write(2); + + // Turn on the external pullup + usb_pullup_out_write(1); + + dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, false); +} + +// Enables or disables the USB device interrupt(s). May be used to +// prevent concurrency issues when mutating data structures shared +// between main code and the interrupt handler. +void dcd_int_enable(uint8_t rhport) +{ + (void) rhport; + irq_setmask(irq_getmask() | (1 << USB_INTERRUPT)); +} + +void dcd_int_disable(uint8_t rhport) +{ + (void) rhport; + irq_setmask(irq_getmask() & ~(1 << USB_INTERRUPT)); +} + +// Called when the device is given a new bus address. +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) +{ + (void)rhport; + // Set address and then acknowledge the SETUP packet + usb_address_write(dev_addr); + + // ACK the transfer (sets the address) + usb_setup_ctrl_write(2); +} + +// Called when the device received SET_CONFIG request, you can leave this +// empty if your peripheral does not require any specific action. +void dcd_set_config(uint8_t rhport, uint8_t config_num) +{ + (void) rhport; + (void) config_num; +} + +// Called to remote wake up host when suspended (e.g hid keyboard) +void dcd_remote_wakeup(uint8_t rhport) +{ + (void) rhport; +} + +//--------------------------------------------------------------------+ +// DCD Endpoint Port +//--------------------------------------------------------------------+ +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) +{ + (void) rhport; + + if (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) + return false; // Not supported + + return true; +} + +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) + usb_out_stall_write((1 << CSR_USB_OUT_STALL_STALL_OFFSET) | tu_edpt_number(ep_addr)); + else { + usb_in_ctrl_write((1 << CSR_USB_IN_CTRL_STALL_OFFSET) | tu_edpt_number(ep_addr)); + } +} + +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) + usb_out_stall_write((0 << CSR_USB_OUT_STALL_STALL_OFFSET) | tu_edpt_number(ep_addr)); + // IN endpoints will get unstalled when more data is written. +} + +__attribute__((used)) +uint8_t *last_tx_buffer; +__attribute__((used)) +uint16_t last_tx_bytes; + +bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) +{ + (void)rhport; + + if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { + // These sorts of transfers are handled in hardware + if ((tu_edpt_number(ep_addr) == 0) && (total_bytes == 0) && (buffer == 0)) { + dcd_event_xfer_complete(0, ep_addr, total_bytes, XFER_RESULT_SUCCESS, false); + return true; + } + uint32_t offset; + // Wait for the tx pipe to free up + while (tx_in_progress) + ; + tx_in_progress = 1; + tx_ep = ep_addr; + tx_len = total_bytes; + + for (offset = 0; offset < total_bytes; offset++) { + usb_in_data_write(buffer[offset]); + } + // Updating the epno queues the data + usb_in_ctrl_write(tu_edpt_number(ep_addr) & 0xf); + last_tx_buffer = buffer; + last_tx_bytes = total_bytes; + } + else if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) { + + // Wait for the rx pipe to free up + while (out_buffer[tu_edpt_number(ep_addr)]) + ; + out_buffer_max[tu_edpt_number(ep_addr)] = total_bytes; + out_buffer[tu_edpt_number(ep_addr)] = buffer; + out_buffer_length[tu_edpt_number(ep_addr)] = 0; + process_rx(false); + } + return true; +} + +//--------------------------------------------------------------------+ +// ISR +//--------------------------------------------------------------------+ + +void hal_dcd_isr(uint8_t rhport) +{ + uint8_t setup_pending = usb_setup_ev_pending_read(); + uint8_t in_pending = usb_in_ev_pending_read(); + uint8_t out_pending = usb_out_ev_pending_read(); + usb_setup_ev_pending_write(setup_pending); + usb_in_ev_pending_write(in_pending); + usb_out_ev_pending_write(out_pending); + + // This event means a bus reset occurred. Reset everything, and + // abandon any further processing. + if (setup_pending & 2) { + usb_setup_ctrl_write(1 << CSR_USB_SETUP_CTRL_RESET_OFFSET); + usb_in_ctrl_write(1 << CSR_USB_IN_CTRL_RESET_OFFSET); + usb_out_ctrl_write(1 << CSR_USB_OUT_CTRL_RESET_OFFSET); + + dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true); + return; + } + + // An "IN" transaction just completed. + // Note that due to the way tinyusb's callback system is implemented, + // we must handle IN and OUT packets before we handle SETUP packets. + // This ensures that any responses to SETUP packets aren't overwritten. + // For example, oftentimes a host will request part of a descriptor + // to begin with, then make a subsequent request. If we don't handle + // the IN packets first, then the second request will be truncated. + if (in_pending) { + finish_tx(); + } + + // An "OUT" transaction just completed so we have new data. + // (But only if we can accept the data) + if (out_pending) { + process_rx(true); + } + + // We got a SETUP packet. Copy it to the setup buffer and clear + // the "pending" bit. + if (setup_pending & 1) { + // Setup packets are always 8 bytes, plus two bytes + // of crc16 + uint8_t setup_packet[10]; + uint32_t setup_length = 0; + + if (!(usb_setup_status_read() & 1)) + fomu_error(__LINE__); + + while (usb_setup_status_read() & 1) { + uint8_t c = usb_setup_data_read(); + if (setup_length < sizeof(setup_packet)) + setup_packet[setup_length] = c; + setup_length++; + } + + // If we have 10 bytes, that's a full SETUP packet plus CRC16. + // Otherwise, it was an RX error. + if (setup_length == 10) { + dcd_event_setup_received(rhport, setup_packet, true); + // Acknowledge the packet, so long as it isn't a SET_ADDRESS + // packet. If it is, leave it unacknowledged and we'll do this + // in the `dcd_set_address` function instead. + if (!((setup_packet[0] == 0x00) && (setup_packet[1] == 0x05))) + usb_setup_ctrl_write(2); + } + else { + fomu_error(__LINE__); + } + } +} + +#endif diff --git a/src/portable/foosn/fomu/dcd_fomu.h b/src/portable/foosn/fomu/dcd_fomu.h new file mode 100644 index 000000000..60bc1240f --- /dev/null +++ b/src/portable/foosn/fomu/dcd_fomu.h @@ -0,0 +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. + */ + +#ifndef _TUSB_DCD_FOMU_H_ +#define _TUSB_DCD_FOMU_H_ + +#include "common/tusb_common.h" +#ifdef __cplusplus + extern "C" { +#endif + +// //--------------------------------------------------------------------+ +// // Register Interface +// //--------------------------------------------------------------------+ + +// //------------- USB Interrupt USBIntSt -------------// +// //enum { +// // DCD_USB_REQ_LOW_PRIO_MASK = TU_BIT(0), +// // DCD_USB_REQ_HIGH_PRIO_MASK = TU_BIT(1), +// // DCD_USB_REQ_DMA_MASK = TU_BIT(2), +// // DCD_USB_REQ_NEED_CLOCK_MASK = TU_BIT(8), +// // DCD_USB_REQ_ENABLE_MASK = TU_BIT(31) +// //}; + +// //------------- Device Interrupt USBDevInt -------------// +// enum { +// DEV_INT_FRAME_MASK = TU_BIT(0), +// DEV_INT_ENDPOINT_FAST_MASK = TU_BIT(1), +// DEV_INT_ENDPOINT_SLOW_MASK = TU_BIT(2), +// DEV_INT_DEVICE_STATUS_MASK = TU_BIT(3), +// DEV_INT_COMMAND_CODE_EMPTY_MASK = TU_BIT(4), +// DEV_INT_COMMAND_DATA_FULL_MASK = TU_BIT(5), +// DEV_INT_RX_ENDPOINT_PACKET_MASK = TU_BIT(6), +// DEV_INT_TX_ENDPOINT_PACKET_MASK = TU_BIT(7), +// DEV_INT_ENDPOINT_REALIZED_MASK = TU_BIT(8), +// DEV_INT_ERROR_MASK = TU_BIT(9) +// }; + +// //------------- DMA Interrupt USBDMAInt-------------// +// enum { +// DMA_INT_END_OF_XFER_MASK = TU_BIT(0), +// DMA_INT_NEW_DD_REQUEST_MASK = TU_BIT(1), +// DMA_INT_ERROR_MASK = TU_BIT(2) +// }; + +// //------------- USBCtrl -------------// +// enum { +// USBCTRL_READ_ENABLE_MASK = TU_BIT(0), +// USBCTRL_WRITE_ENABLE_MASK = TU_BIT(1), +// }; + +// //------------- USBRxPLen -------------// +// enum { +// USBRXPLEN_PACKET_LENGTH_MASK = (TU_BIT(10)-1), +// USBRXPLEN_DATA_VALID_MASK = TU_BIT(10), +// USBRXPLEN_PACKET_READY_MASK = TU_BIT(11), +// }; + +// //------------- SIE Command Code -------------// +// typedef enum +// { +// SIE_CMDPHASE_WRITE = 1, +// SIE_CMDPHASE_READ = 2, +// SIE_CMDPHASE_COMMAND = 5 +// } sie_cmdphase_t; + +// enum { +// // device commands +// SIE_CMDCODE_SET_ADDRESS = 0xd0, +// SIE_CMDCODE_CONFIGURE_DEVICE = 0xd8, +// SIE_CMDCODE_SET_MODE = 0xf3, +// SIE_CMDCODE_READ_FRAME_NUMBER = 0xf5, +// SIE_CMDCODE_READ_TEST_REGISTER = 0xfd, +// SIE_CMDCODE_DEVICE_STATUS = 0xfe, +// SIE_CMDCODE_GET_ERROR = 0xff, +// SIE_CMDCODE_READ_ERROR_STATUS = 0xfb, + +// // endpoint commands +// SIE_CMDCODE_ENDPOINT_SELECT = 0x00, // + endpoint index +// SIE_CMDCODE_ENDPOINT_SELECT_CLEAR_INTERRUPT = 0x40, // + endpoint index, should use USBEpIntClr instead +// SIE_CMDCODE_ENDPOINT_SET_STATUS = 0x40, // + endpoint index +// SIE_CMDCODE_BUFFER_CLEAR = 0xf2, +// SIE_CMDCODE_BUFFER_VALIDATE = 0xfa +// }; + +// //------------- SIE Device Status (get/set from SIE_CMDCODE_DEVICE_STATUS) -------------// +// enum { +// SIE_DEV_STATUS_CONNECT_STATUS_MASK = TU_BIT(0), +// SIE_DEV_STATUS_CONNECT_CHANGE_MASK = TU_BIT(1), +// SIE_DEV_STATUS_SUSPEND_MASK = TU_BIT(2), +// SIE_DEV_STATUS_SUSPEND_CHANGE_MASK = TU_BIT(3), +// SIE_DEV_STATUS_RESET_MASK = TU_BIT(4) +// }; + +// //------------- SIE Select Endpoint Command -------------// +// enum { +// SIE_SELECT_ENDPOINT_FULL_EMPTY_MASK = TU_BIT(0), // 0: empty, 1 full. IN endpoint checks empty, OUT endpoint check full +// SIE_SELECT_ENDPOINT_STALL_MASK = TU_BIT(1), +// SIE_SELECT_ENDPOINT_SETUP_RECEIVED_MASK = TU_BIT(2), // clear by SIE_CMDCODE_ENDPOINT_SELECT_CLEAR_INTERRUPT +// SIE_SELECT_ENDPOINT_PACKET_OVERWRITTEN_MASK = TU_BIT(3), // previous packet is overwritten by a SETUP packet +// SIE_SELECT_ENDPOINT_NAK_MASK = TU_BIT(4), // last packet response is NAK (auto clear by an ACK) +// SIE_SELECT_ENDPOINT_BUFFER1_FULL_MASK = TU_BIT(5), +// SIE_SELECT_ENDPOINT_BUFFER2_FULL_MASK = TU_BIT(6) +// }; + +// typedef enum +// { +// SIE_SET_ENDPOINT_STALLED_MASK = TU_BIT(0), +// SIE_SET_ENDPOINT_DISABLED_MASK = TU_BIT(5), +// SIE_SET_ENDPOINT_RATE_FEEDBACK_MASK = TU_BIT(6), +// SIE_SET_ENDPOINT_CONDITION_STALLED_MASK = TU_BIT(7), +// }sie_endpoint_set_status_mask_t; + +// //------------- DMA Descriptor Status -------------// +// enum { +// DD_STATUS_NOT_SERVICED = 0, +// DD_STATUS_BEING_SERVICED, +// DD_STATUS_NORMAL, +// DD_STATUS_DATA_UNDERUN, // short packet +// DD_STATUS_DATA_OVERRUN, +// DD_STATUS_SYSTEM_ERROR +// }; + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_DCD_FOMU_H_ */ diff --git a/src/portable/foosn/fomu/hal_fomu.c b/src/portable/foosn/fomu/hal_fomu.c new file mode 100644 index 000000000..ebc0a1d93 --- /dev/null +++ b/src/portable/foosn/fomu/hal_fomu.c @@ -0,0 +1,33 @@ +/* + * 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 "common/tusb_common.h" + +#if (CFG_TUSB_MCU == OPT_MCU_FOMU_EPTRI) + +// No HAL-specific stuff here! + +#endif -- cgit v1.3.1 From 32bb68409ea904af52f6044dd9448ed62226da25 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Sat, 12 Oct 2019 14:52:15 +0800 Subject: portable: fomu: get msc to enumerate Signed-off-by: Sean Cross --- src/portable/foosn/fomu/dcd_fomu.c | 160 ++++++++++++++++++++++--------------- 1 file changed, 95 insertions(+), 65 deletions(-) (limited to 'src/portable') diff --git a/src/portable/foosn/fomu/dcd_fomu.c b/src/portable/foosn/fomu/dcd_fomu.c index d7f9d3d1e..f49e9e508 100644 --- a/src/portable/foosn/fomu/dcd_fomu.c +++ b/src/portable/foosn/fomu/dcd_fomu.c @@ -41,105 +41,130 @@ void mputln(const char *str); // SIE Command //--------------------------------------------------------------------+ -static uint8_t volatile out_buffer_length[16]; -static uint8_t volatile * out_buffer[16]; -static uint8_t volatile out_buffer_max[16]; +#define EP_SIZE 64 + +static uint16_t volatile rx_buffer_length[16]; +static uint8_t volatile * rx_buffer[16]; +static uint16_t volatile rx_buffer_max[16]; static volatile bool tx_in_progress; static volatile uint8_t tx_ep; static volatile uint16_t tx_len; +static uint8_t volatile * tx_buffer; +static volatile uint16_t tx_offset; //--------------------------------------------------------------------+ // PIPE HELPER //--------------------------------------------------------------------+ static void finish_tx(void) { - // // Don't allow requeueing -- only queue more data if the system is idle. - // if (!(usb_in_status_read() & 2)) { - // return; - // } - - // Don't send empty data - if (!tx_in_progress) { - return; - } + // Don't send empty data + if (!tx_in_progress) { + return; + } + tx_offset += EP_SIZE; + if (tx_offset >= tx_len) { tx_in_progress = 0; + tx_buffer = NULL; dcd_event_xfer_complete(0, tx_ep, tx_len, XFER_RESULT_SUCCESS, true); return; + } + + // Send more data + uint8_t added_bytes; + for (added_bytes = 0; (added_bytes < EP_SIZE) && (added_bytes + tx_offset < tx_len); added_bytes++) { + usb_in_data_write(tx_buffer[added_bytes + tx_offset]); + } + + // Updating the epno queues the data + usb_in_ctrl_write(tu_edpt_number(tx_ep) & 0xf); + return; } static void process_rx(bool in_isr) { // If there isn't any data in the FIFO, don't do anything. - if (!(usb_out_status_read() & 1)) - return; + if (!(usb_out_status_read() & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET))) + return; + + uint8_t out_ep = (usb_out_status_read() >> CSR_USB_OUT_STATUS_EPNO_OFFSET) & 0xf; + + // If the destination buffer doesn't exist, don't drain the hardware + // fifo. Note that this can cause deadlocks if the host is waiting + // on some other endpoint's data! + if (rx_buffer[out_ep] == NULL) + return; - uint8_t out_ep = (usb_out_status_read() >> 2) & 0xf; uint32_t total_read = 0; - uint32_t current_offset = out_buffer_length[out_ep]; - while (usb_out_status_read() & 1) { + uint32_t current_offset = rx_buffer_length[out_ep]; + while (usb_out_status_read() & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET)) { uint8_t c = usb_out_data_read(); total_read++; - if (out_buffer_length[out_ep] < out_buffer_max[out_ep]) - out_buffer[out_ep][current_offset++] = c; + if (rx_buffer_length[out_ep] < rx_buffer_max[out_ep]) + rx_buffer[out_ep][current_offset++] = c; } // Strip off the CRC16 - total_read -= 2; - out_buffer_length[out_ep] += total_read; - if (out_buffer_length[out_ep] > out_buffer_max[out_ep]) - out_buffer_length[out_ep] = out_buffer_max[out_ep]; - - if (out_buffer_max[out_ep] == out_buffer_length[out_ep]) { - out_buffer[out_ep] = NULL; - dcd_event_xfer_complete(0, tu_edpt_addr(out_ep, TUSB_DIR_OUT), out_buffer_length[out_ep], XFER_RESULT_SUCCESS, in_isr); + rx_buffer_length[out_ep] += (total_read - 2); + if (rx_buffer_length[out_ep] > rx_buffer_max[out_ep]) + rx_buffer_length[out_ep] = rx_buffer_max[out_ep]; + + if (rx_buffer_max[out_ep] == rx_buffer_length[out_ep]) { + rx_buffer[out_ep] = NULL; + uint16_t len = rx_buffer_length[out_ep]; + dcd_event_xfer_complete(0, tu_edpt_addr(out_ep, TUSB_DIR_OUT), len, XFER_RESULT_SUCCESS, in_isr); } - // Acknowledge having received the data - usb_out_ctrl_write(2); + // Acknowledge having received the data, and re-enable data reception + usb_out_ctrl_write(1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET); } //--------------------------------------------------------------------+ // CONTROLLER API //--------------------------------------------------------------------+ +static void dcd_reset(void) +{ + usb_address_write(0); + + // Reset all three FIFO handlers + usb_setup_ctrl_write(1 << CSR_USB_SETUP_CTRL_RESET_OFFSET); + usb_in_ctrl_write(1 << CSR_USB_IN_CTRL_RESET_OFFSET); + usb_out_ctrl_write(1 << CSR_USB_OUT_CTRL_RESET_OFFSET); + + // Accept incoming data by default. + usb_out_ctrl_write(CSR_USB_OUT_CTRL_ENABLE_OFFSET); + + memset(rx_buffer, 0, sizeof(rx_buffer)); + tx_in_progress = 0; + tx_len = 0; + tx_buffer = NULL; + tx_offset = 0; + + dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true); +} + // Initializes the USB peripheral for device mode and enables it. void dcd_init(uint8_t rhport) { (void) rhport; usb_pullup_out_write(0); - usb_address_write(0); - usb_out_ctrl_write(0); usb_setup_ev_enable_write(0); usb_in_ev_enable_write(0); usb_out_ev_enable_write(0); - // Reset the IN handler - usb_in_ctrl_write(0x20); - - // Reset the SETUP handler - usb_setup_ctrl_write(0x04); - - // Reset the OUT handler - usb_out_ctrl_write(0x04); - // Enable all event handlers and clear their contents usb_setup_ev_pending_write(usb_setup_ev_pending_read()); usb_in_ev_pending_write(usb_in_ev_pending_read()); usb_out_ev_pending_write(usb_out_ev_pending_read()); - usb_setup_ev_enable_write(3); usb_in_ev_enable_write(1); usb_out_ev_enable_write(1); - - // Accept incoming data by default. - usb_out_ctrl_write(2); + usb_setup_ev_enable_write(3); // Turn on the external pullup usb_pullup_out_write(1); - - dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, false); } // Enables or disables the USB device interrupt(s). May be used to @@ -222,36 +247,45 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t t { (void)rhport; + // These sorts of transfers are handled in hardware + if ((tu_edpt_number(ep_addr) == 0) && (total_bytes == 0) && (buffer == NULL)) { + dcd_event_xfer_complete(0, ep_addr, total_bytes, XFER_RESULT_SUCCESS, false); + + // An IN packet is sent to acknowledge an OUT token. Re-enable OUT after this. + if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) + usb_out_ctrl_write(1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET); + return true; + } + if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { - // These sorts of transfers are handled in hardware - if ((tu_edpt_number(ep_addr) == 0) && (total_bytes == 0) && (buffer == 0)) { - dcd_event_xfer_complete(0, ep_addr, total_bytes, XFER_RESULT_SUCCESS, false); - return true; - } uint32_t offset; + // Wait for the tx pipe to free up while (tx_in_progress) ; tx_in_progress = 1; tx_ep = ep_addr; tx_len = total_bytes; + tx_buffer = buffer; + tx_offset = 0; - for (offset = 0; offset < total_bytes; offset++) { + for (offset = 0; (offset < EP_SIZE) && (offset < total_bytes); offset++) { usb_in_data_write(buffer[offset]); } // Updating the epno queues the data usb_in_ctrl_write(tu_edpt_number(ep_addr) & 0xf); - last_tx_buffer = buffer; - last_tx_bytes = total_bytes; } else if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) { + TU_ASSERT(rx_buffer[tu_edpt_number(ep_addr)] == NULL); - // Wait for the rx pipe to free up - while (out_buffer[tu_edpt_number(ep_addr)]) - ; - out_buffer_max[tu_edpt_number(ep_addr)] = total_bytes; - out_buffer[tu_edpt_number(ep_addr)] = buffer; - out_buffer_length[tu_edpt_number(ep_addr)] = 0; + rx_buffer_length[tu_edpt_number(ep_addr)] = 0; + rx_buffer_max[tu_edpt_number(ep_addr)] = total_bytes; + rx_buffer[tu_edpt_number(ep_addr)] = buffer; + + // If there's data in the buffer already, we'll try draining it + // into the current fifo immediately. Note that since this + // bit is set, an interrupt won't fire again, so there is + // no need for a lock here. process_rx(false); } return true; @@ -273,11 +307,7 @@ void hal_dcd_isr(uint8_t rhport) // This event means a bus reset occurred. Reset everything, and // abandon any further processing. if (setup_pending & 2) { - usb_setup_ctrl_write(1 << CSR_USB_SETUP_CTRL_RESET_OFFSET); - usb_in_ctrl_write(1 << CSR_USB_IN_CTRL_RESET_OFFSET); - usb_out_ctrl_write(1 << CSR_USB_OUT_CTRL_RESET_OFFSET); - - dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true); + dcd_reset(); return; } -- cgit v1.3.1 From 0559fd13fb43aa991200c3bddc146c5828fc48b0 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Mon, 14 Oct 2019 11:59:17 +0800 Subject: fomu: fix some issues with dcd_fomu Signed-off-by: Sean Cross --- src/portable/foosn/fomu/dcd_fomu.c | 181 ++++++++++++++++++++++--------------- 1 file changed, 110 insertions(+), 71 deletions(-) (limited to 'src/portable') diff --git a/src/portable/foosn/fomu/dcd_fomu.c b/src/portable/foosn/fomu/dcd_fomu.c index f49e9e508..73cf018eb 100644 --- a/src/portable/foosn/fomu/dcd_fomu.c +++ b/src/portable/foosn/fomu/dcd_fomu.c @@ -43,31 +43,34 @@ void mputln(const char *str); #define EP_SIZE 64 -static uint16_t volatile rx_buffer_length[16]; -static uint8_t volatile * rx_buffer[16]; -static uint16_t volatile rx_buffer_max[16]; +uint16_t volatile rx_buffer_offset[16]; +uint8_t volatile * rx_buffer[16]; +uint16_t volatile rx_buffer_max[16]; -static volatile bool tx_in_progress; -static volatile uint8_t tx_ep; -static volatile uint16_t tx_len; -static uint8_t volatile * tx_buffer; -static volatile uint16_t tx_offset; +volatile uint8_t tx_ep; +volatile uint16_t tx_len; +uint8_t volatile * tx_buffer; +volatile uint16_t tx_offset; +volatile uint8_t reset_count; //--------------------------------------------------------------------+ // PIPE HELPER //--------------------------------------------------------------------+ static void finish_tx(void) { - // Don't send empty data - if (!tx_in_progress) { + // Ignore "ACK" packets where there was no data to send. + if (!tx_buffer) { return; } + uint8_t in_status = usb_in_status_read(); + if (!(in_status & (1 << CSR_USB_IN_STATUS_IDLE_OFFSET))) + fomu_error(__LINE__); + tx_offset += EP_SIZE; if (tx_offset >= tx_len) { - tx_in_progress = 0; + dcd_event_xfer_complete(0, tu_edpt_addr(tx_ep, TUSB_DIR_IN), tx_len, XFER_RESULT_SUCCESS, true); tx_buffer = NULL; - dcd_event_xfer_complete(0, tx_ep, tx_len, XFER_RESULT_SUCCESS, true); return; } @@ -78,41 +81,49 @@ static void finish_tx(void) { } // Updating the epno queues the data - usb_in_ctrl_write(tu_edpt_number(tx_ep) & 0xf); + usb_in_ctrl_write(tx_ep & 0xf); return; } static void process_rx(bool in_isr) { - // If there isn't any data in the FIFO, don't do anything. - if (!(usb_out_status_read() & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET))) + // If the OUT handler is still waiting to send, don't do anything. + uint8_t out_status = usb_out_status_read(); + if (!(out_status & (1 << CSR_USB_OUT_STATUS_IDLE_OFFSET))) return; - uint8_t out_ep = (usb_out_status_read() >> CSR_USB_OUT_STATUS_EPNO_OFFSET) & 0xf; + uint8_t rx_ep = (out_status >> CSR_USB_OUT_STATUS_EPNO_OFFSET) & 0xf; + if ((rx_ep != 0) && (rx_ep != 2) && (rx_ep != 3)) + fomu_error(__LINE__); // If the destination buffer doesn't exist, don't drain the hardware // fifo. Note that this can cause deadlocks if the host is waiting // on some other endpoint's data! - if (rx_buffer[out_ep] == NULL) + if (rx_buffer[rx_ep] == NULL) return; uint32_t total_read = 0; - uint32_t current_offset = rx_buffer_length[out_ep]; + uint32_t current_offset = rx_buffer_offset[rx_ep]; + if (current_offset > rx_buffer_max[rx_ep]) + fomu_error(__LINE__); while (usb_out_status_read() & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET)) { uint8_t c = usb_out_data_read(); total_read++; - if (rx_buffer_length[out_ep] < rx_buffer_max[out_ep]) - rx_buffer[out_ep][current_offset++] = c; + if ((rx_buffer_offset[rx_ep] + current_offset) < rx_buffer_max[rx_ep]) + rx_buffer[rx_ep][current_offset++] = c; } // Strip off the CRC16 - rx_buffer_length[out_ep] += (total_read - 2); - if (rx_buffer_length[out_ep] > rx_buffer_max[out_ep]) - rx_buffer_length[out_ep] = rx_buffer_max[out_ep]; - - if (rx_buffer_max[out_ep] == rx_buffer_length[out_ep]) { - rx_buffer[out_ep] = NULL; - uint16_t len = rx_buffer_length[out_ep]; - dcd_event_xfer_complete(0, tu_edpt_addr(out_ep, TUSB_DIR_OUT), len, XFER_RESULT_SUCCESS, in_isr); + rx_buffer_offset[rx_ep] += (total_read - 2); + if (rx_buffer_offset[rx_ep] > rx_buffer_max[rx_ep]) + rx_buffer_offset[rx_ep] = rx_buffer_max[rx_ep]; + + if ((rx_ep == 3) && (rx_buffer[rx_ep][0] == 0x80) && (rx_buffer[rx_ep][1] == 0x25)) + fomu_error(__LINE__); + + if (rx_buffer_max[rx_ep] == rx_buffer_offset[rx_ep]) { + rx_buffer[rx_ep] = NULL; + uint16_t len = rx_buffer_offset[rx_ep]; + dcd_event_xfer_complete(0, tu_edpt_addr(rx_ep, TUSB_DIR_OUT), len, XFER_RESULT_SUCCESS, in_isr); } // Acknowledge having received the data, and re-enable data reception @@ -125,6 +136,11 @@ static void process_rx(bool in_isr) { static void dcd_reset(void) { + reset_count++; + usb_setup_ev_enable_write(0); + usb_in_ev_enable_write(0); + usb_out_ev_enable_write(0); + usb_address_write(0); // Reset all three FIFO handlers @@ -132,14 +148,24 @@ static void dcd_reset(void) usb_in_ctrl_write(1 << CSR_USB_IN_CTRL_RESET_OFFSET); usb_out_ctrl_write(1 << CSR_USB_OUT_CTRL_RESET_OFFSET); - // Accept incoming data by default. - usb_out_ctrl_write(CSR_USB_OUT_CTRL_ENABLE_OFFSET); - - memset(rx_buffer, 0, sizeof(rx_buffer)); - tx_in_progress = 0; + memset((void *)rx_buffer, 0, sizeof(rx_buffer)); + memset((void *)rx_buffer_max, 0, sizeof(rx_buffer_max)); + memset((void *)rx_buffer_offset, 0, sizeof(rx_buffer_offset)); tx_len = 0; tx_buffer = NULL; tx_offset = 0; + tx_ep = 0; + + // Accept incoming data by default. + usb_out_ctrl_write(1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET); + + // Enable all event handlers and clear their contents + usb_setup_ev_pending_write(usb_setup_ev_pending_read()); + usb_in_ev_pending_write(usb_in_ev_pending_read()); + usb_out_ev_pending_write(usb_out_ev_pending_read()); + usb_in_ev_enable_write(1); + usb_out_ev_enable_write(1); + usb_setup_ev_enable_write(3); dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true); } @@ -151,10 +177,6 @@ void dcd_init(uint8_t rhport) usb_pullup_out_write(0); - usb_setup_ev_enable_write(0); - usb_in_ev_enable_write(0); - usb_out_ev_enable_write(0); - // Enable all event handlers and clear their contents usb_setup_ev_pending_write(usb_setup_ev_pending_read()); usb_in_ev_pending_write(usb_in_ev_pending_read()); @@ -190,7 +212,7 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr) usb_address_write(dev_addr); // ACK the transfer (sets the address) - usb_setup_ctrl_write(2); + usb_setup_ctrl_write(1 << CSR_USB_SETUP_CTRL_HANDLED_OFFSET); } // Called when the device received SET_CONFIG request, you can leave this @@ -213,10 +235,18 @@ void dcd_remote_wakeup(uint8_t rhport) bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) { (void) rhport; + uint8_t ep_num = tu_edpt_number(p_endpoint_desc->bEndpointAddress); + uint8_t ep_dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); if (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) return false; // Not supported + if (ep_dir == TUSB_DIR_OUT) { + rx_buffer_offset[ep_num] = 0; + rx_buffer_max[ep_num] = 0; + rx_buffer[ep_num] = NULL; + } + return true; } @@ -238,55 +268,64 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) // IN endpoints will get unstalled when more data is written. } -__attribute__((used)) -uint8_t *last_tx_buffer; -__attribute__((used)) -uint16_t last_tx_bytes; - bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { (void)rhport; + uint8_t ep_num = tu_edpt_number(ep_addr); + uint8_t ep_dir = tu_edpt_dir(ep_addr); - // These sorts of transfers are handled in hardware - if ((tu_edpt_number(ep_addr) == 0) && (total_bytes == 0) && (buffer == NULL)) { + // These sorts of transfers are handled in hardware automatically, so simply inform + // the core that the transfer was processed. + if ((ep_num == 0) && (total_bytes == 0) && (buffer == NULL)) { dcd_event_xfer_complete(0, ep_addr, total_bytes, XFER_RESULT_SUCCESS, false); // An IN packet is sent to acknowledge an OUT token. Re-enable OUT after this. - if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) - usb_out_ctrl_write(1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET); + // if (ep_dir == TUSB_DIR_IN) { + // usb_out_ev_enable_write(0); + // process_rx(false); + // usb_out_ev_enable_write(1); + // } return true; } - if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { + TU_ASSERT(((uint32_t)buffer) >= 0x10000000); + TU_ASSERT(((uint32_t)buffer) <= 0x10020000); + + if (ep_dir == TUSB_DIR_IN) { uint32_t offset; // Wait for the tx pipe to free up - while (tx_in_progress) + uint8_t previous_reset_count = reset_count; + while ((tx_buffer != NULL) || !(usb_in_status_read() & (1 << CSR_USB_IN_STATUS_IDLE_OFFSET))) ; - tx_in_progress = 1; - tx_ep = ep_addr; + // If a reset happens while we're waiting, abort the transfer + if (previous_reset_count != reset_count) + return true; + + tx_ep = ep_num; tx_len = total_bytes; - tx_buffer = buffer; tx_offset = 0; + tx_buffer = buffer; for (offset = 0; (offset < EP_SIZE) && (offset < total_bytes); offset++) { - usb_in_data_write(buffer[offset]); + usb_in_data_write(buffer[offset]); } // Updating the epno queues the data - usb_in_ctrl_write(tu_edpt_number(ep_addr) & 0xf); + usb_in_ctrl_write(ep_num & 0xf); } - else if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) { - TU_ASSERT(rx_buffer[tu_edpt_number(ep_addr)] == NULL); + else if (ep_dir == TUSB_DIR_OUT) { + TU_ASSERT(rx_buffer[ep_num] == NULL); + TU_ASSERT((ep_num == 0) || (ep_num == 2) || (ep_num == 3)); - rx_buffer_length[tu_edpt_number(ep_addr)] = 0; - rx_buffer_max[tu_edpt_number(ep_addr)] = total_bytes; - rx_buffer[tu_edpt_number(ep_addr)] = buffer; + rx_buffer_offset[ep_num] = 0; + rx_buffer_max[ep_num] = total_bytes; + rx_buffer[ep_num] = buffer; // If there's data in the buffer already, we'll try draining it - // into the current fifo immediately. Note that since this - // bit is set, an interrupt won't fire again, so there is - // no need for a lock here. + // into the current fifo immediately. + usb_out_ev_enable_write(0); process_rx(false); + usb_out_ev_enable_write(1); } return true; } @@ -311,6 +350,13 @@ void hal_dcd_isr(uint8_t rhport) return; } + // An "OUT" transaction just completed so we have new data. + // (But only if we can accept the data) + // if (out_pending) { + if (usb_out_ev_enable_read() && out_pending) { + process_rx(true); + } + // An "IN" transaction just completed. // Note that due to the way tinyusb's callback system is implemented, // we must handle IN and OUT packets before we handle SETUP packets. @@ -322,17 +368,10 @@ void hal_dcd_isr(uint8_t rhport) finish_tx(); } - // An "OUT" transaction just completed so we have new data. - // (But only if we can accept the data) - if (out_pending) { - process_rx(true); - } - // We got a SETUP packet. Copy it to the setup buffer and clear // the "pending" bit. if (setup_pending & 1) { - // Setup packets are always 8 bytes, plus two bytes - // of crc16 + // Setup packets are always 8 bytes, plus two bytes of crc16. uint8_t setup_packet[10]; uint32_t setup_length = 0; @@ -354,7 +393,7 @@ void hal_dcd_isr(uint8_t rhport) // packet. If it is, leave it unacknowledged and we'll do this // in the `dcd_set_address` function instead. if (!((setup_packet[0] == 0x00) && (setup_packet[1] == 0x05))) - usb_setup_ctrl_write(2); + usb_setup_ctrl_write(1 << CSR_USB_SETUP_CTRL_HANDLED_OFFSET); } else { fomu_error(__LINE__); -- cgit v1.3.1 From 22fd7bf85ecc4f1e58f8e6827431d0ae0b5700d1 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Mon, 14 Oct 2019 23:02:05 +0800 Subject: fomu: first fully-working release This is able to transfer lots of data back and forth across MSC. Signed-off-by: Sean Cross --- src/portable/foosn/fomu/dcd_fomu.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'src/portable') diff --git a/src/portable/foosn/fomu/dcd_fomu.c b/src/portable/foosn/fomu/dcd_fomu.c index 73cf018eb..96ac3a9fc 100644 --- a/src/portable/foosn/fomu/dcd_fomu.c +++ b/src/portable/foosn/fomu/dcd_fomu.c @@ -63,6 +63,7 @@ static void finish_tx(void) { return; } + // If the system isn't idle, then something is very wrong. uint8_t in_status = usb_in_status_read(); if (!(in_status & (1 << CSR_USB_IN_STATUS_IDLE_OFFSET))) fomu_error(__LINE__); @@ -92,8 +93,6 @@ static void process_rx(bool in_isr) { return; uint8_t rx_ep = (out_status >> CSR_USB_OUT_STATUS_EPNO_OFFSET) & 0xf; - if ((rx_ep != 0) && (rx_ep != 2) && (rx_ep != 3)) - fomu_error(__LINE__); // If the destination buffer doesn't exist, don't drain the hardware // fifo. Note that this can cause deadlocks if the host is waiting @@ -117,9 +116,6 @@ static void process_rx(bool in_isr) { if (rx_buffer_offset[rx_ep] > rx_buffer_max[rx_ep]) rx_buffer_offset[rx_ep] = rx_buffer_max[rx_ep]; - if ((rx_ep == 3) && (rx_buffer[rx_ep][0] == 0x80) && (rx_buffer[rx_ep][1] == 0x25)) - fomu_error(__LINE__); - if (rx_buffer_max[rx_ep] == rx_buffer_offset[rx_ep]) { rx_buffer[rx_ep] = NULL; uint16_t len = rx_buffer_offset[rx_ep]; @@ -273,6 +269,7 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t t (void)rhport; uint8_t ep_num = tu_edpt_number(ep_addr); uint8_t ep_dir = tu_edpt_dir(ep_addr); + TU_ASSERT(tx_ep < 16); // These sorts of transfers are handled in hardware automatically, so simply inform // the core that the transfer was processed. @@ -296,7 +293,9 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t t // Wait for the tx pipe to free up uint8_t previous_reset_count = reset_count; - while ((tx_buffer != NULL) || !(usb_in_status_read() & (1 << CSR_USB_IN_STATUS_IDLE_OFFSET))) + while (!((tx_buffer == NULL) + && (usb_in_status_read() & (1 << CSR_USB_IN_STATUS_IDLE_OFFSET)) + && !(usb_in_status_read() & (1 << CSR_USB_IN_STATUS_HAVE_OFFSET)))) ; // If a reset happens while we're waiting, abort the transfer if (previous_reset_count != reset_count) @@ -315,7 +314,6 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t t } else if (ep_dir == TUSB_DIR_OUT) { TU_ASSERT(rx_buffer[ep_num] == NULL); - TU_ASSERT((ep_num == 0) || (ep_num == 2) || (ep_num == 3)); rx_buffer_offset[ep_num] = 0; rx_buffer_max[ep_num] = total_bytes; @@ -324,7 +322,8 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t t // If there's data in the buffer already, we'll try draining it // into the current fifo immediately. usb_out_ev_enable_write(0); - process_rx(false); + if (usb_out_status_read() & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET)) + process_rx(false); usb_out_ev_enable_write(1); } return true; -- cgit v1.3.1 From 843136d0e4467e1c554f246d1b69fed3172b6cc0 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Tue, 29 Oct 2019 21:35:03 +0800 Subject: fomu: commit latest version Signed-off-by: Sean Cross --- src/portable/foosn/fomu/dcd_fomu.c | 300 ++++++++++++++++++++++--------------- 1 file changed, 182 insertions(+), 118 deletions(-) (limited to 'src/portable') diff --git a/src/portable/foosn/fomu/dcd_fomu.c b/src/portable/foosn/fomu/dcd_fomu.c index 96ac3a9fc..8b2212c16 100644 --- a/src/portable/foosn/fomu/dcd_fomu.c +++ b/src/portable/foosn/fomu/dcd_fomu.c @@ -48,19 +48,49 @@ uint8_t volatile * rx_buffer[16]; uint16_t volatile rx_buffer_max[16]; volatile uint8_t tx_ep; -volatile uint16_t tx_len; -uint8_t volatile * tx_buffer; -volatile uint16_t tx_offset; +volatile uint16_t tx_buffer_offset[16]; +uint8_t volatile * tx_buffer[16]; +volatile uint16_t tx_buffer_max[16]; volatile uint8_t reset_count; +__attribute__((used)) uint8_t volatile * last_tx_buffer; +__attribute__((used)) volatile uint8_t last_tx_ep; + +uint8_t setup_packet_bfr[10]; + //--------------------------------------------------------------------+ // PIPE HELPER //--------------------------------------------------------------------+ -static void finish_tx(void) { - // Ignore "ACK" packets where there was no data to send. - if (!tx_buffer) { - return; +static bool advance_tx_ep(void) { + // Move on to the next transmit buffer in a round-robin manner + uint8_t prev_tx_ep = tx_ep; + for (tx_ep = (tx_ep + 1) & 0xf; tx_ep != prev_tx_ep; tx_ep = ((tx_ep + 1) & 0xf)) { + if (tx_buffer[tx_ep]) + return true; + } + if (!tx_buffer[tx_ep]) + return false; + return true; +} + +static void tx_more_data(void) { + // Send more data + uint8_t added_bytes; + for (added_bytes = 0; (added_bytes < EP_SIZE) && (added_bytes + tx_buffer_offset[tx_ep] < tx_buffer_max[tx_ep]); added_bytes++) { + usb_in_data_write(tx_buffer[tx_ep][added_bytes + tx_buffer_offset[tx_ep]]); + } + + // Updating the epno queues the data + usb_in_ctrl_write(tx_ep & 0xf); +} + +static void process_tx(bool in_isr) { + // If the buffer is now empty, search for the next buffer to fill. + if (!tx_buffer[tx_ep]) { + if (advance_tx_ep()) + tx_more_data(); + return; } // If the system isn't idle, then something is very wrong. @@ -68,62 +98,78 @@ static void finish_tx(void) { if (!(in_status & (1 << CSR_USB_IN_STATUS_IDLE_OFFSET))) fomu_error(__LINE__); - tx_offset += EP_SIZE; - if (tx_offset >= tx_len) { - dcd_event_xfer_complete(0, tu_edpt_addr(tx_ep, TUSB_DIR_IN), tx_len, XFER_RESULT_SUCCESS, true); - tx_buffer = NULL; - return; - } + tx_buffer_offset[tx_ep] += EP_SIZE; - // Send more data - uint8_t added_bytes; - for (added_bytes = 0; (added_bytes < EP_SIZE) && (added_bytes + tx_offset < tx_len); added_bytes++) { - usb_in_data_write(tx_buffer[added_bytes + tx_offset]); + if (tx_buffer_offset[tx_ep] >= tx_buffer_max[tx_ep]) { + last_tx_buffer = tx_buffer[tx_ep]; + last_tx_ep = tx_ep; + tx_buffer[tx_ep] = NULL; + + dcd_event_xfer_complete(0, tu_edpt_addr(tx_ep, TUSB_DIR_IN), tx_buffer_max[tx_ep], XFER_RESULT_SUCCESS, in_isr); + if (!advance_tx_ep()) + return; } - // Updating the epno queues the data - usb_in_ctrl_write(tx_ep & 0xf); + tx_more_data(); return; } static void process_rx(bool in_isr) { - // If the OUT handler is still waiting to send, don't do anything. - uint8_t out_status = usb_out_status_read(); - if (!(out_status & (1 << CSR_USB_OUT_STATUS_IDLE_OFFSET))) - return; + // If the OUT handler is still waiting to send, don't do anything. + uint8_t out_status = usb_out_status_read(); + if (!(out_status & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET))) + fomu_error(__LINE__); + // return; - uint8_t rx_ep = (out_status >> CSR_USB_OUT_STATUS_EPNO_OFFSET) & 0xf; + uint8_t rx_ep = (out_status >> CSR_USB_OUT_STATUS_EPNO_OFFSET) & 0xf; - // If the destination buffer doesn't exist, don't drain the hardware - // fifo. Note that this can cause deadlocks if the host is waiting - // on some other endpoint's data! - if (rx_buffer[rx_ep] == NULL) - return; + // If the destination buffer doesn't exist, don't drain the hardware + // fifo. Note that this can cause deadlocks if the host is waiting + // on some other endpoint's data! + if (rx_buffer[rx_ep] == NULL) { + fomu_error(__LINE__); + return; + } + + uint32_t total_read = 0; + uint32_t current_offset = rx_buffer_offset[rx_ep]; + if (current_offset > rx_buffer_max[rx_ep]) + fomu_error(__LINE__); + while (usb_out_status_read() & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET)) { + uint8_t c = usb_out_data_read(); + total_read++; + if ((rx_buffer_offset[rx_ep] + current_offset) < rx_buffer_max[rx_ep]) + rx_buffer[rx_ep][current_offset++] = c; + } + if (total_read > 66) + fomu_error(__LINE__); - uint32_t total_read = 0; - uint32_t current_offset = rx_buffer_offset[rx_ep]; - if (current_offset > rx_buffer_max[rx_ep]) + // Strip off the CRC16 + rx_buffer_offset[rx_ep] += (total_read - 2); + if (rx_buffer_offset[rx_ep] > rx_buffer_max[rx_ep]) + rx_buffer_offset[rx_ep] = rx_buffer_max[rx_ep]; + + if (rx_buffer_max[rx_ep] == rx_buffer_offset[rx_ep]) { + if (rx_buffer[rx_ep] == NULL) fomu_error(__LINE__); - while (usb_out_status_read() & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET)) { - uint8_t c = usb_out_data_read(); - total_read++; - if ((rx_buffer_offset[rx_ep] + current_offset) < rx_buffer_max[rx_ep]) - rx_buffer[rx_ep][current_offset++] = c; - } - // Strip off the CRC16 - rx_buffer_offset[rx_ep] += (total_read - 2); - if (rx_buffer_offset[rx_ep] > rx_buffer_max[rx_ep]) - rx_buffer_offset[rx_ep] = rx_buffer_max[rx_ep]; + // Disable this endpoint (causing it to respond NAK) until we have + // a buffer to place the data into. + rx_buffer[rx_ep] = NULL; + uint16_t len = rx_buffer_offset[rx_ep]; - if (rx_buffer_max[rx_ep] == rx_buffer_offset[rx_ep]) { - rx_buffer[rx_ep] = NULL; - uint16_t len = rx_buffer_offset[rx_ep]; - dcd_event_xfer_complete(0, tu_edpt_addr(rx_ep, TUSB_DIR_OUT), len, XFER_RESULT_SUCCESS, in_isr); - } + // uint16_t ep_en_mask = usb_out_enable_status_read(); + // int i; + // for (i = 0; i < 16; i++) { + // if ((!!(ep_en_mask & (1 << i))) ^ (!!(rx_buffer[i]))) + // fomu_error(__LINE__); + // } + dcd_event_xfer_complete(0, tu_edpt_addr(rx_ep, TUSB_DIR_OUT), len, XFER_RESULT_SUCCESS, in_isr); + return; + } - // Acknowledge having received the data, and re-enable data reception - usb_out_ctrl_write(1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET); + // If there's more data, re-enable data reception + usb_out_ctrl_write((1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | rx_ep); } //--------------------------------------------------------------------+ @@ -147,18 +193,16 @@ static void dcd_reset(void) memset((void *)rx_buffer, 0, sizeof(rx_buffer)); memset((void *)rx_buffer_max, 0, sizeof(rx_buffer_max)); memset((void *)rx_buffer_offset, 0, sizeof(rx_buffer_offset)); - tx_len = 0; - tx_buffer = NULL; - tx_offset = 0; - tx_ep = 0; - // Accept incoming data by default. - usb_out_ctrl_write(1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET); + memset((void *)tx_buffer, 0, sizeof(tx_buffer)); + memset((void *)tx_buffer_max, 0, sizeof(tx_buffer_max)); + memset((void *)tx_buffer_offset, 0, sizeof(tx_buffer_offset)); + tx_ep = 0; // Enable all event handlers and clear their contents - usb_setup_ev_pending_write(usb_setup_ev_pending_read()); - usb_in_ev_pending_write(usb_in_ev_pending_read()); - usb_out_ev_pending_write(usb_out_ev_pending_read()); + usb_setup_ev_pending_write(-1); + usb_in_ev_pending_write(-1); + usb_out_ev_pending_write(-1); usb_in_ev_enable_write(1); usb_out_ev_enable_write(1); usb_setup_ev_enable_write(3); @@ -203,12 +247,15 @@ void dcd_int_disable(uint8_t rhport) // Called when the device is given a new bus address. void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { - (void)rhport; - // Set address and then acknowledge the SETUP packet - usb_address_write(dev_addr); + // Respond with ACK status first before changing device address + dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); - // ACK the transfer (sets the address) - usb_setup_ctrl_write(1 << CSR_USB_SETUP_CTRL_HANDLED_OFFSET); + // Wait for the response packet to get sent + while (tx_buffer[0] != NULL) + ; + + // Activate the new address + usb_address_write(dev_addr); } // Called when the device received SET_CONFIG request, you can leave this @@ -243,24 +290,36 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) rx_buffer[ep_num] = NULL; } + else if (ep_dir == TUSB_DIR_OUT) { + tx_buffer_offset[ep_num] = 0; + tx_buffer_max[ep_num] = 0; + tx_buffer[ep_num] = NULL; + } + return true; } void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { (void) rhport; + if (tu_edpt_number(ep_addr) == 2) + fomu_error(__LINE__); + if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) - usb_out_stall_write((1 << CSR_USB_OUT_STALL_STALL_OFFSET) | tu_edpt_number(ep_addr)); - else { + usb_out_ctrl_write((1 << CSR_USB_OUT_CTRL_STALL_OFFSET) | (1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | tu_edpt_number(ep_addr)); + else usb_in_ctrl_write((1 << CSR_USB_IN_CTRL_STALL_OFFSET) | tu_edpt_number(ep_addr)); - } } void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { (void) rhport; - if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) - usb_out_stall_write((0 << CSR_USB_OUT_STALL_STALL_OFFSET) | tu_edpt_number(ep_addr)); + if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) { + uint8_t enable = 0; + if (rx_buffer[ep_addr]) + enable = 1; + usb_out_ctrl_write((0 << CSR_USB_OUT_CTRL_STALL_OFFSET) | (enable << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | tu_edpt_number(ep_addr)); + } // IN endpoints will get unstalled when more data is written. } @@ -269,62 +328,64 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t t (void)rhport; uint8_t ep_num = tu_edpt_number(ep_addr); uint8_t ep_dir = tu_edpt_dir(ep_addr); - TU_ASSERT(tx_ep < 16); - - // These sorts of transfers are handled in hardware automatically, so simply inform - // the core that the transfer was processed. - if ((ep_num == 0) && (total_bytes == 0) && (buffer == NULL)) { - dcd_event_xfer_complete(0, ep_addr, total_bytes, XFER_RESULT_SUCCESS, false); - - // An IN packet is sent to acknowledge an OUT token. Re-enable OUT after this. - // if (ep_dir == TUSB_DIR_IN) { - // usb_out_ev_enable_write(0); - // process_rx(false); - // usb_out_ev_enable_write(1); - // } - return true; + TU_ASSERT(ep_num < 16); + + // Give a nonzero buffer when we transmit 0 bytes, so that the + // system doesn't think the endpoint is idle. + if ((buffer == NULL) && (total_bytes == 0)) { + buffer = (uint8_t *)0xffffffff; } - TU_ASSERT(((uint32_t)buffer) >= 0x10000000); - TU_ASSERT(((uint32_t)buffer) <= 0x10020000); + TU_ASSERT(buffer != NULL); if (ep_dir == TUSB_DIR_IN) { - uint32_t offset; - // Wait for the tx pipe to free up uint8_t previous_reset_count = reset_count; - while (!((tx_buffer == NULL) - && (usb_in_status_read() & (1 << CSR_USB_IN_STATUS_IDLE_OFFSET)) - && !(usb_in_status_read() & (1 << CSR_USB_IN_STATUS_HAVE_OFFSET)))) + // Continue until the buffer is empty, the system is idle, and the fifo is empty. + while (tx_buffer[ep_num] != NULL) ; + // If a reset happens while we're waiting, abort the transfer if (previous_reset_count != reset_count) return true; - tx_ep = ep_num; - tx_len = total_bytes; - tx_offset = 0; - tx_buffer = buffer; - - for (offset = 0; (offset < EP_SIZE) && (offset < total_bytes); offset++) { - usb_in_data_write(buffer[offset]); + dcd_int_disable(0); + TU_ASSERT(tx_buffer[ep_num] == NULL); + tx_buffer_offset[ep_num] = 0; + tx_buffer_max[ep_num] = total_bytes; + tx_buffer[ep_num] = buffer; + + // If the current buffer is NULL, then that means the tx logic is idle. + // Update the tx_ep to point to our endpoint number and queue the data. + // Otherwise, let it be and it'll get picked up after the next transfer + // finishes. + if ((tx_buffer[tx_ep] == NULL) || (tx_ep == ep_num)) { + tx_ep = ep_num; + tx_more_data(); } - // Updating the epno queues the data - usb_in_ctrl_write(ep_num & 0xf); + dcd_int_enable(0); } else if (ep_dir == TUSB_DIR_OUT) { - TU_ASSERT(rx_buffer[ep_num] == NULL); + while (rx_buffer[ep_num] != NULL) + ; rx_buffer_offset[ep_num] = 0; rx_buffer_max[ep_num] = total_bytes; - rx_buffer[ep_num] = buffer; - // If there's data in the buffer already, we'll try draining it - // into the current fifo immediately. - usb_out_ev_enable_write(0); - if (usb_out_status_read() & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET)) - process_rx(false); - usb_out_ev_enable_write(1); + dcd_int_disable(0); + rx_buffer[ep_num] = buffer; + usb_out_ctrl_write((1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | ep_num); + + // uint16_t ep_en_mask = usb_out_enable_status_read(); + // int i; + // for (i = 0; i < 16; i++) { + // if ((!!(ep_en_mask & (1 << i))) ^ (!!(rx_buffer[i]))) { + // if (rx_buffer[i] && usb_out_ev_pending_read() && (usb_out_status_read() & 0xf) == i) + // continue; + // fomu_error(__LINE__); + // } + // } + dcd_int_enable(0); } return true; } @@ -339,8 +400,6 @@ void hal_dcd_isr(uint8_t rhport) uint8_t in_pending = usb_in_ev_pending_read(); uint8_t out_pending = usb_out_ev_pending_read(); usb_setup_ev_pending_write(setup_pending); - usb_in_ev_pending_write(in_pending); - usb_out_ev_pending_write(out_pending); // This event means a bus reset occurred. Reset everything, and // abandon any further processing. @@ -352,7 +411,10 @@ void hal_dcd_isr(uint8_t rhport) // An "OUT" transaction just completed so we have new data. // (But only if we can accept the data) // if (out_pending) { - if (usb_out_ev_enable_read() && out_pending) { + if (out_pending) { + if (!usb_out_ev_enable_read()) + fomu_error(__LINE__); + usb_out_ev_pending_write(out_pending); process_rx(true); } @@ -364,35 +426,37 @@ void hal_dcd_isr(uint8_t rhport) // to begin with, then make a subsequent request. If we don't handle // the IN packets first, then the second request will be truncated. if (in_pending) { - finish_tx(); + if (!usb_in_ev_enable_read()) + fomu_error(__LINE__); + usb_in_ev_pending_write(in_pending); + process_tx(true); } // We got a SETUP packet. Copy it to the setup buffer and clear // the "pending" bit. if (setup_pending & 1) { // Setup packets are always 8 bytes, plus two bytes of crc16. - uint8_t setup_packet[10]; uint32_t setup_length = 0; - if (!(usb_setup_status_read() & 1)) + if (!(usb_setup_status_read() & (1 << CSR_USB_SETUP_STATUS_HAVE_OFFSET))) fomu_error(__LINE__); - while (usb_setup_status_read() & 1) { + while (usb_setup_status_read() & (1 << CSR_USB_SETUP_STATUS_HAVE_OFFSET)) { uint8_t c = usb_setup_data_read(); - if (setup_length < sizeof(setup_packet)) - setup_packet[setup_length] = c; + if (setup_length < sizeof(setup_packet_bfr)) + setup_packet_bfr[setup_length] = c; setup_length++; } // If we have 10 bytes, that's a full SETUP packet plus CRC16. // Otherwise, it was an RX error. if (setup_length == 10) { - dcd_event_setup_received(rhport, setup_packet, true); + dcd_event_setup_received(rhport, setup_packet_bfr, true); // Acknowledge the packet, so long as it isn't a SET_ADDRESS // packet. If it is, leave it unacknowledged and we'll do this // in the `dcd_set_address` function instead. - if (!((setup_packet[0] == 0x00) && (setup_packet[1] == 0x05))) - usb_setup_ctrl_write(1 << CSR_USB_SETUP_CTRL_HANDLED_OFFSET); + // if (!((setup_packet_bfr[0] == 0x00) && (setup_packet_bfr[1] == 0x05))) + usb_setup_ctrl_write(1 << CSR_USB_SETUP_CTRL_ACK_OFFSET); } else { fomu_error(__LINE__); -- cgit v1.3.1 From 835a72c59599e83183e623972fe85e779ed01383 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Thu, 31 Oct 2019 08:35:04 +0800 Subject: fomu: semi-working dcd file Signed-off-by: Sean Cross --- src/portable/foosn/fomu/dcd_fomu.c | 129 ++++++++++++++++++++++++++----------- 1 file changed, 91 insertions(+), 38 deletions(-) (limited to 'src/portable') diff --git a/src/portable/foosn/fomu/dcd_fomu.c b/src/portable/foosn/fomu/dcd_fomu.c index 8b2212c16..07efcce7a 100644 --- a/src/portable/foosn/fomu/dcd_fomu.c +++ b/src/portable/foosn/fomu/dcd_fomu.c @@ -26,6 +26,10 @@ #include "tusb_option.h" +#ifndef DEBUG +#define DEBUG 0 +#endif + // #if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_FOMU_EPTRI) #if 1 @@ -48,16 +52,17 @@ uint8_t volatile * rx_buffer[16]; uint16_t volatile rx_buffer_max[16]; volatile uint8_t tx_ep; +volatile bool tx_active; volatile uint16_t tx_buffer_offset[16]; uint8_t volatile * tx_buffer[16]; volatile uint16_t tx_buffer_max[16]; volatile uint8_t reset_count; +#ifdef DEBUG __attribute__((used)) uint8_t volatile * last_tx_buffer; __attribute__((used)) volatile uint8_t last_tx_ep; - +#endif uint8_t setup_packet_bfr[10]; - //--------------------------------------------------------------------+ // PIPE HELPER //--------------------------------------------------------------------+ @@ -86,27 +91,35 @@ static void tx_more_data(void) { } static void process_tx(bool in_isr) { +#if DEBUG + // If the system isn't idle, then something is very wrong. + uint8_t in_status = usb_in_status_read(); + if (!(in_status & (1 << CSR_USB_IN_STATUS_IDLE_OFFSET))) + fomu_error(__LINE__); +#endif + // If the buffer is now empty, search for the next buffer to fill. if (!tx_buffer[tx_ep]) { if (advance_tx_ep()) tx_more_data(); + else + tx_active = false; return; } - // If the system isn't idle, then something is very wrong. - uint8_t in_status = usb_in_status_read(); - if (!(in_status & (1 << CSR_USB_IN_STATUS_IDLE_OFFSET))) - fomu_error(__LINE__); - tx_buffer_offset[tx_ep] += EP_SIZE; if (tx_buffer_offset[tx_ep] >= tx_buffer_max[tx_ep]) { +#if DEBUG last_tx_buffer = tx_buffer[tx_ep]; last_tx_ep = tx_ep; +#endif tx_buffer[tx_ep] = NULL; - dcd_event_xfer_complete(0, tu_edpt_addr(tx_ep, TUSB_DIR_IN), tx_buffer_max[tx_ep], XFER_RESULT_SUCCESS, in_isr); if (!advance_tx_ep()) + tx_active = false; + dcd_event_xfer_complete(0, tu_edpt_addr(tx_ep, TUSB_DIR_IN), tx_buffer_max[tx_ep], XFER_RESULT_SUCCESS, in_isr); + if (!tx_active) return; } @@ -115,61 +128,85 @@ static void process_tx(bool in_isr) { } static void process_rx(bool in_isr) { - // If the OUT handler is still waiting to send, don't do anything. uint8_t out_status = usb_out_status_read(); +#if DEBUG + // If the OUT handler is still waiting to send, don't do anything. if (!(out_status & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET))) fomu_error(__LINE__); // return; - +#endif uint8_t rx_ep = (out_status >> CSR_USB_OUT_STATUS_EPNO_OFFSET) & 0xf; // If the destination buffer doesn't exist, don't drain the hardware // fifo. Note that this can cause deadlocks if the host is waiting // on some other endpoint's data! +#if DEBUG if (rx_buffer[rx_ep] == NULL) { fomu_error(__LINE__); return; } +#endif + // Drain the FIFO into the destination buffer uint32_t total_read = 0; uint32_t current_offset = rx_buffer_offset[rx_ep]; +#if DEBUG if (current_offset > rx_buffer_max[rx_ep]) fomu_error(__LINE__); +#endif while (usb_out_status_read() & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET)) { uint8_t c = usb_out_data_read(); total_read++; if ((rx_buffer_offset[rx_ep] + current_offset) < rx_buffer_max[rx_ep]) rx_buffer[rx_ep][current_offset++] = c; } +#if DEBUG if (total_read > 66) fomu_error(__LINE__); + if (total_read < 2) + fomu_error(__LINE__); +#endif // Strip off the CRC16 rx_buffer_offset[rx_ep] += (total_read - 2); if (rx_buffer_offset[rx_ep] > rx_buffer_max[rx_ep]) rx_buffer_offset[rx_ep] = rx_buffer_max[rx_ep]; + // If there's no more data, complete the transfer to tinyusb if (rx_buffer_max[rx_ep] == rx_buffer_offset[rx_ep]) { +#if DEBUG if (rx_buffer[rx_ep] == NULL) fomu_error(__LINE__); +#endif - // Disable this endpoint (causing it to respond NAK) until we have - // a buffer to place the data into. + // Free up this buffer. rx_buffer[rx_ep] = NULL; uint16_t len = rx_buffer_offset[rx_ep]; - // uint16_t ep_en_mask = usb_out_enable_status_read(); - // int i; - // for (i = 0; i < 16; i++) { - // if ((!!(ep_en_mask & (1 << i))) ^ (!!(rx_buffer[i]))) - // fomu_error(__LINE__); - // } +#if DEBUG + // Validate that all enabled endpoints have buffers, + // and no disabled endpoints have buffers. + uint16_t ep_en_mask = usb_out_enable_status_read(); + int i; + for (i = 0; i < 16; i++) { + if ((!!(ep_en_mask & (1 << i))) ^ (!!(rx_buffer[i]))) { + uint8_t new_status = usb_out_status_read(); + // Another IRQ came in while we were processing, so ignore this endpoint. + if ((new_status & 0x20) && ((new_status & 0xf) == i)) + continue; + fomu_error(__LINE__); + } + } +#endif dcd_event_xfer_complete(0, tu_edpt_addr(rx_ep, TUSB_DIR_OUT), len, XFER_RESULT_SUCCESS, in_isr); - return; + } + else { + // If there's more data, re-enable data reception on this endpoint + usb_out_ctrl_write((1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | rx_ep); } - // If there's more data, re-enable data reception - usb_out_ctrl_write((1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | rx_ep); + // Now that the buffer is drained, clear the pending IRQ. + usb_out_ev_pending_write(usb_out_ev_pending_read()); } //--------------------------------------------------------------------+ @@ -198,6 +235,7 @@ static void dcd_reset(void) memset((void *)tx_buffer_max, 0, sizeof(tx_buffer_max)); memset((void *)tx_buffer_offset, 0, sizeof(tx_buffer_offset)); tx_ep = 0; + tx_active = false; // Enable all event handlers and clear their contents usb_setup_ev_pending_write(-1); @@ -251,7 +289,7 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr) dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); // Wait for the response packet to get sent - while (tx_buffer[0] != NULL) + while (tx_active) ; // Activate the new address @@ -305,8 +343,12 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) if (tu_edpt_number(ep_addr) == 2) fomu_error(__LINE__); - if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) - usb_out_ctrl_write((1 << CSR_USB_OUT_CTRL_STALL_OFFSET) | (1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | tu_edpt_number(ep_addr)); + if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) { + uint8_t enable = 0; + if (rx_buffer[ep_addr]) + enable = 1; + usb_out_ctrl_write((1 << CSR_USB_OUT_CTRL_STALL_OFFSET) | (enable << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | tu_edpt_number(ep_addr)); + } else usb_in_ctrl_write((1 << CSR_USB_IN_CTRL_STALL_OFFSET) | tu_edpt_number(ep_addr)); } @@ -359,32 +401,36 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t t // Update the tx_ep to point to our endpoint number and queue the data. // Otherwise, let it be and it'll get picked up after the next transfer // finishes. - if ((tx_buffer[tx_ep] == NULL) || (tx_ep == ep_num)) { + if (!tx_active) { tx_ep = ep_num; + tx_active = true; tx_more_data(); } dcd_int_enable(0); } + else if (ep_dir == TUSB_DIR_OUT) { while (rx_buffer[ep_num] != NULL) ; + dcd_int_disable(0); + TU_ASSERT(rx_buffer[ep_num] == NULL); + rx_buffer[ep_num] = buffer; rx_buffer_offset[ep_num] = 0; rx_buffer_max[ep_num] = total_bytes; - dcd_int_disable(0); - rx_buffer[ep_num] = buffer; usb_out_ctrl_write((1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | ep_num); - - // uint16_t ep_en_mask = usb_out_enable_status_read(); - // int i; - // for (i = 0; i < 16; i++) { - // if ((!!(ep_en_mask & (1 << i))) ^ (!!(rx_buffer[i]))) { - // if (rx_buffer[i] && usb_out_ev_pending_read() && (usb_out_status_read() & 0xf) == i) - // continue; - // fomu_error(__LINE__); - // } - // } +#if DEBUG + uint16_t ep_en_mask = usb_out_enable_status_read(); + int i; + for (i = 0; i < 16; i++) { + if ((!!(ep_en_mask & (1 << i))) ^ (!!(rx_buffer[i]))) { + if (rx_buffer[i] && usb_out_ev_pending_read() && (usb_out_status_read() & 0xf) == i) + continue; + fomu_error(__LINE__); + } + } +#endif dcd_int_enable(0); } return true; @@ -412,9 +458,10 @@ void hal_dcd_isr(uint8_t rhport) // (But only if we can accept the data) // if (out_pending) { if (out_pending) { +#if DEBUG if (!usb_out_ev_enable_read()) fomu_error(__LINE__); - usb_out_ev_pending_write(out_pending); +#endif process_rx(true); } @@ -426,8 +473,10 @@ void hal_dcd_isr(uint8_t rhport) // to begin with, then make a subsequent request. If we don't handle // the IN packets first, then the second request will be truncated. if (in_pending) { +#if DEBUG if (!usb_in_ev_enable_read()) fomu_error(__LINE__); +#endif usb_in_ev_pending_write(in_pending); process_tx(true); } @@ -438,8 +487,10 @@ void hal_dcd_isr(uint8_t rhport) // Setup packets are always 8 bytes, plus two bytes of crc16. uint32_t setup_length = 0; +#if DEBUG if (!(usb_setup_status_read() & (1 << CSR_USB_SETUP_STATUS_HAVE_OFFSET))) fomu_error(__LINE__); +#endif while (usb_setup_status_read() & (1 << CSR_USB_SETUP_STATUS_HAVE_OFFSET)) { uint8_t c = usb_setup_data_read(); @@ -458,9 +509,11 @@ void hal_dcd_isr(uint8_t rhport) // if (!((setup_packet_bfr[0] == 0x00) && (setup_packet_bfr[1] == 0x05))) usb_setup_ctrl_write(1 << CSR_USB_SETUP_CTRL_ACK_OFFSET); } +#if DEBUG else { fomu_error(__LINE__); } +#endif } } -- cgit v1.3.1 From 913032ae1d1d4a28e295d150bfa636f296be7bb9 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Thu, 31 Oct 2019 11:14:07 +0800 Subject: dcd_fomu: nearly there Signed-off-by: Sean Cross --- src/portable/foosn/fomu/dcd_fomu.c | 101 +++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 44 deletions(-) (limited to 'src/portable') diff --git a/src/portable/foosn/fomu/dcd_fomu.c b/src/portable/foosn/fomu/dcd_fomu.c index 07efcce7a..12add4c85 100644 --- a/src/portable/foosn/fomu/dcd_fomu.c +++ b/src/portable/foosn/fomu/dcd_fomu.c @@ -24,11 +24,11 @@ * This file is part of the TinyUSB stack. */ -#include "tusb_option.h" - #ifndef DEBUG -#define DEBUG 0 +#define DEBUG 1 #endif +#include "tusb_option.h" + // #if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_FOMU_EPTRI) #if 1 @@ -41,6 +41,8 @@ void fomu_error(uint32_t line); void mputs(const char *str); void mputln(const char *str); +static uint8_t last_address; + //--------------------------------------------------------------------+ // SIE Command //--------------------------------------------------------------------+ @@ -58,11 +60,11 @@ uint8_t volatile * tx_buffer[16]; volatile uint16_t tx_buffer_max[16]; volatile uint8_t reset_count; -#ifdef DEBUG +#if DEBUG __attribute__((used)) uint8_t volatile * last_tx_buffer; __attribute__((used)) volatile uint8_t last_tx_ep; -#endif uint8_t setup_packet_bfr[10]; +#endif //--------------------------------------------------------------------+ // PIPE HELPER //--------------------------------------------------------------------+ @@ -82,15 +84,15 @@ static bool advance_tx_ep(void) { static void tx_more_data(void) { // Send more data uint8_t added_bytes; - for (added_bytes = 0; (added_bytes < EP_SIZE) && (added_bytes + tx_buffer_offset[tx_ep] < tx_buffer_max[tx_ep]); added_bytes++) { - usb_in_data_write(tx_buffer[tx_ep][added_bytes + tx_buffer_offset[tx_ep]]); + for (added_bytes = 0; (added_bytes < EP_SIZE) && (tx_buffer_offset[tx_ep] < tx_buffer_max[tx_ep]); added_bytes++) { + usb_in_data_write(tx_buffer[tx_ep][tx_buffer_offset[tx_ep]++]); } // Updating the epno queues the data usb_in_ctrl_write(tx_ep & 0xf); } -static void process_tx(bool in_isr) { +static void process_tx(void) { #if DEBUG // If the system isn't idle, then something is very wrong. uint8_t in_status = usb_in_status_read(); @@ -107,18 +109,20 @@ static void process_tx(bool in_isr) { return; } - tx_buffer_offset[tx_ep] += EP_SIZE; - if (tx_buffer_offset[tx_ep] >= tx_buffer_max[tx_ep]) { #if DEBUG last_tx_buffer = tx_buffer[tx_ep]; last_tx_ep = tx_ep; #endif tx_buffer[tx_ep] = NULL; + uint16_t xferred_bytes = tx_buffer_max[tx_ep]; + uint8_t xferred_ep = tx_ep; if (!advance_tx_ep()) tx_active = false; - dcd_event_xfer_complete(0, tu_edpt_addr(tx_ep, TUSB_DIR_IN), tx_buffer_max[tx_ep], XFER_RESULT_SUCCESS, in_isr); + if ((xferred_bytes == 13) && (tx_buffer_max[tx_ep] == 512)) + fomu_error(__LINE__); + dcd_event_xfer_complete(0, tu_edpt_addr(xferred_ep, TUSB_DIR_IN), xferred_bytes, XFER_RESULT_SUCCESS, true); if (!tx_active) return; } @@ -127,7 +131,7 @@ static void process_tx(bool in_isr) { return; } -static void process_rx(bool in_isr) { +static void process_rx(void) { uint8_t out_status = usb_out_status_read(); #if DEBUG // If the OUT handler is still waiting to send, don't do anything. @@ -190,15 +194,15 @@ static void process_rx(bool in_isr) { int i; for (i = 0; i < 16; i++) { if ((!!(ep_en_mask & (1 << i))) ^ (!!(rx_buffer[i]))) { - uint8_t new_status = usb_out_status_read(); - // Another IRQ came in while we were processing, so ignore this endpoint. - if ((new_status & 0x20) && ((new_status & 0xf) == i)) - continue; + // uint8_t new_status = usb_out_status_read(); + // // Another IRQ came in while we were processing, so ignore this endpoint. + // if ((new_status & 0x20) && ((new_status & 0xf) == i)) + // continue; fomu_error(__LINE__); } } #endif - dcd_event_xfer_complete(0, tu_edpt_addr(rx_ep, TUSB_DIR_OUT), len, XFER_RESULT_SUCCESS, in_isr); + dcd_event_xfer_complete(0, tu_edpt_addr(rx_ep, TUSB_DIR_OUT), len, XFER_RESULT_SUCCESS, true); } else { // If there's more data, re-enable data reception on this endpoint @@ -220,6 +224,8 @@ static void dcd_reset(void) usb_in_ev_enable_write(0); usb_out_ev_enable_write(0); + if (last_address) + asm("ebreak"); usb_address_write(0); // Reset all three FIFO handlers @@ -294,6 +300,7 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr) // Activate the new address usb_address_write(dev_addr); + last_address = dev_addr; } // Called when the device received SET_CONFIG request, you can leave this @@ -387,11 +394,13 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t t while (tx_buffer[ep_num] != NULL) ; + dcd_int_disable(0); + if (total_bytes == 499) + asm("ebreak"); // If a reset happens while we're waiting, abort the transfer if (previous_reset_count != reset_count) return true; - dcd_int_disable(0); TU_ASSERT(tx_buffer[ep_num] == NULL); tx_buffer_offset[ep_num] = 0; tx_buffer_max[ep_num] = total_bytes; @@ -419,18 +428,19 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t t rx_buffer_offset[ep_num] = 0; rx_buffer_max[ep_num] = total_bytes; + // Enable receiving on this particular endpoint usb_out_ctrl_write((1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | ep_num); -#if DEBUG - uint16_t ep_en_mask = usb_out_enable_status_read(); - int i; - for (i = 0; i < 16; i++) { - if ((!!(ep_en_mask & (1 << i))) ^ (!!(rx_buffer[i]))) { - if (rx_buffer[i] && usb_out_ev_pending_read() && (usb_out_status_read() & 0xf) == i) - continue; - fomu_error(__LINE__); - } - } -#endif +// #if DEBUG +// uint16_t ep_en_mask = usb_out_enable_status_read(); +// int i; +// for (i = 0; i < 16; i++) { +// if ((!!(ep_en_mask & (1 << i))) ^ (!!(rx_buffer[i]))) { +// if (rx_buffer[i] && usb_out_ev_pending_read() && (usb_out_status_read() & 0xf) == i) +// continue; +// fomu_error(__LINE__); +// } +// } +// #endif dcd_int_enable(0); } return true; @@ -442,9 +452,12 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t t void hal_dcd_isr(uint8_t rhport) { - uint8_t setup_pending = usb_setup_ev_pending_read(); - uint8_t in_pending = usb_in_ev_pending_read(); - uint8_t out_pending = usb_out_ev_pending_read(); + uint8_t setup_pending = usb_setup_ev_pending_read() & usb_setup_ev_enable_read(); + uint8_t in_pending = usb_in_ev_pending_read() & usb_in_ev_enable_read(); + uint8_t out_pending = usb_out_ev_pending_read() & usb_out_ev_enable_read(); +#if !DEBUG + uint8_t setup_packet_bfr[10]; +#endif usb_setup_ev_pending_write(setup_pending); // This event means a bus reset occurred. Reset everything, and @@ -454,17 +467,6 @@ void hal_dcd_isr(uint8_t rhport) return; } - // An "OUT" transaction just completed so we have new data. - // (But only if we can accept the data) - // if (out_pending) { - if (out_pending) { -#if DEBUG - if (!usb_out_ev_enable_read()) - fomu_error(__LINE__); -#endif - process_rx(true); - } - // An "IN" transaction just completed. // Note that due to the way tinyusb's callback system is implemented, // we must handle IN and OUT packets before we handle SETUP packets. @@ -478,9 +480,20 @@ void hal_dcd_isr(uint8_t rhport) fomu_error(__LINE__); #endif usb_in_ev_pending_write(in_pending); - process_tx(true); + process_tx(); } + // An "OUT" transaction just completed so we have new data. + // (But only if we can accept the data) + // if (out_pending) { + if (out_pending) { +#if DEBUG + if (!usb_out_ev_enable_read()) + fomu_error(__LINE__); +#endif + process_rx(); + } + // We got a SETUP packet. Copy it to the setup buffer and clear // the "pending" bit. if (setup_pending & 1) { -- cgit v1.3.1 From 729c8d073ce9df2bdf5d1c94a5ff63b2ffc2f48b Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Fri, 1 Nov 2019 12:31:17 +0800 Subject: fomu: dcd_fomu: add next_ev support Now that we have the `USB_NEXT_EV` register, take advantage of it to work around issue #207. Signed-off-by: Sean Cross --- src/portable/foosn/fomu/dcd_fomu.c | 206 ++++++++++++++++++++++++++----------- 1 file changed, 144 insertions(+), 62 deletions(-) (limited to 'src/portable') diff --git a/src/portable/foosn/fomu/dcd_fomu.c b/src/portable/foosn/fomu/dcd_fomu.c index 12add4c85..29bd7d485 100644 --- a/src/portable/foosn/fomu/dcd_fomu.c +++ b/src/portable/foosn/fomu/dcd_fomu.c @@ -29,7 +29,6 @@ #endif #include "tusb_option.h" - // #if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_FOMU_EPTRI) #if 1 @@ -41,6 +40,30 @@ void fomu_error(uint32_t line); void mputs(const char *str); void mputln(const char *str); +struct usb_log { + uint8_t ep_num; + uint8_t size; + uint8_t data[66]; +}; +__attribute__((used)) +struct usb_log usb_log[128]; +__attribute__((used)) +uint8_t usb_log_offset; + +struct xfer_log { + uint8_t ep_num; + uint16_t size; +}; +__attribute__((used)) +struct xfer_log xfer_log[64]; +__attribute__((used)) +uint8_t xfer_log_offset; + +__attribute__((used)) +struct xfer_log queue_log[64]; +__attribute__((used)) +uint8_t queue_log_offset; + static uint8_t last_address; //--------------------------------------------------------------------+ @@ -65,6 +88,7 @@ __attribute__((used)) uint8_t volatile * last_tx_buffer; __attribute__((used)) volatile uint8_t last_tx_ep; uint8_t setup_packet_bfr[10]; #endif + //--------------------------------------------------------------------+ // PIPE HELPER //--------------------------------------------------------------------+ @@ -81,13 +105,36 @@ static bool advance_tx_ep(void) { return true; } +void xfer_log_append(uint8_t ep_num, uint16_t sz) { + xfer_log[xfer_log_offset].ep_num = ep_num; + xfer_log[xfer_log_offset].size = sz; + xfer_log_offset++; + if (xfer_log_offset > sizeof(xfer_log)/sizeof(*xfer_log)) + xfer_log_offset = 0; +} + +void queue_log_append(uint8_t ep_num, uint16_t sz) { + queue_log[queue_log_offset].ep_num = ep_num; + queue_log[queue_log_offset].size = sz; + queue_log_offset++; + if (queue_log_offset > sizeof(queue_log)/sizeof(*queue_log)) + queue_log_offset = 0; +} + static void tx_more_data(void) { // Send more data uint8_t added_bytes; for (added_bytes = 0; (added_bytes < EP_SIZE) && (tx_buffer_offset[tx_ep] < tx_buffer_max[tx_ep]); added_bytes++) { + usb_log[usb_log_offset].data[added_bytes] = tx_buffer[tx_ep][tx_buffer_offset[tx_ep]]; usb_in_data_write(tx_buffer[tx_ep][tx_buffer_offset[tx_ep]++]); } + usb_log[usb_log_offset].ep_num = tu_edpt_addr(tx_ep, TUSB_DIR_IN); + usb_log[usb_log_offset].size = added_bytes; + usb_log_offset++; + if (usb_log_offset > sizeof(usb_log)/sizeof(*usb_log)) + usb_log_offset = 0; + // Updating the epno queues the data usb_in_ctrl_write(tx_ep & 0xf); } @@ -122,6 +169,7 @@ static void process_tx(void) { tx_active = false; if ((xferred_bytes == 13) && (tx_buffer_max[tx_ep] == 512)) fomu_error(__LINE__); + xfer_log_append(tu_edpt_addr(xferred_ep, TUSB_DIR_IN), xferred_bytes); dcd_event_xfer_complete(0, tu_edpt_addr(xferred_ep, TUSB_DIR_IN), xferred_bytes, XFER_RESULT_SUCCESS, true); if (!tx_active) return; @@ -158,12 +206,19 @@ static void process_rx(void) { if (current_offset > rx_buffer_max[rx_ep]) fomu_error(__LINE__); #endif + usb_log[usb_log_offset].ep_num = tu_edpt_addr(rx_ep, TUSB_DIR_OUT); + usb_log[usb_log_offset].size = 0; while (usb_out_status_read() & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET)) { uint8_t c = usb_out_data_read(); total_read++; - if ((rx_buffer_offset[rx_ep] + current_offset) < rx_buffer_max[rx_ep]) + if ((rx_buffer_offset[rx_ep] + current_offset) < rx_buffer_max[rx_ep]) { + usb_log[usb_log_offset].data[usb_log[usb_log_offset].size++] = c; rx_buffer[rx_ep][current_offset++] = c; + } } + usb_log_offset++; + if (usb_log_offset > sizeof(usb_log)/sizeof(*usb_log)) + usb_log_offset = 0; #if DEBUG if (total_read > 66) fomu_error(__LINE__); @@ -202,6 +257,7 @@ static void process_rx(void) { } } #endif + xfer_log_append(tu_edpt_addr(rx_ep, TUSB_DIR_OUT), len); dcd_event_xfer_complete(0, tu_edpt_addr(rx_ep, TUSB_DIR_OUT), len, XFER_RESULT_SUCCESS, true); } else { @@ -395,6 +451,7 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t t ; dcd_int_disable(0); + queue_log_append(ep_addr, total_bytes); if (total_bytes == 499) asm("ebreak"); // If a reset happens while we're waiting, abort the transfer @@ -422,8 +479,9 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t t while (rx_buffer[ep_num] != NULL) ; - dcd_int_disable(0); TU_ASSERT(rx_buffer[ep_num] == NULL); + dcd_int_disable(0); + queue_log_append(ep_addr, total_bytes); rx_buffer[ep_num] = buffer; rx_buffer_offset[ep_num] = 0; rx_buffer_max[ep_num] = total_bytes; @@ -450,83 +508,107 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t t // ISR //--------------------------------------------------------------------+ -void hal_dcd_isr(uint8_t rhport) +static void handle_out(void) { - uint8_t setup_pending = usb_setup_ev_pending_read() & usb_setup_ev_enable_read(); - uint8_t in_pending = usb_in_ev_pending_read() & usb_in_ev_enable_read(); - uint8_t out_pending = usb_out_ev_pending_read() & usb_out_ev_enable_read(); -#if !DEBUG - uint8_t setup_packet_bfr[10]; + // An "OUT" transaction just completed so we have new data. + // (But only if we can accept the data) +#if DEBUG + if (!usb_out_ev_pending_read()) + fomu_error(__LINE__); + if (!usb_out_ev_enable_read()) + fomu_error(__LINE__); +#endif + process_rx(); +} + +static void handle_in(void) +{ +#if DEBUG + if (!usb_in_ev_pending_read()) + fomu_error(__LINE__); + if (!usb_in_ev_enable_read()) + fomu_error(__LINE__); #endif - usb_setup_ev_pending_write(setup_pending); + usb_in_ev_pending_write(usb_in_ev_pending_read()); + process_tx(); +} + +static void handle_reset(void) +{ + uint8_t setup_pending = usb_setup_ev_pending_read() & usb_setup_ev_enable_read(); + if (!(setup_pending & 2)) + fomu_error(__LINE__); + usb_setup_ev_pending_write(2); // This event means a bus reset occurred. Reset everything, and // abandon any further processing. - if (setup_pending & 2) { - dcd_reset(); - return; - } + dcd_reset(); +} - // An "IN" transaction just completed. - // Note that due to the way tinyusb's callback system is implemented, - // we must handle IN and OUT packets before we handle SETUP packets. - // This ensures that any responses to SETUP packets aren't overwritten. - // For example, oftentimes a host will request part of a descriptor - // to begin with, then make a subsequent request. If we don't handle - // the IN packets first, then the second request will be truncated. - if (in_pending) { -#if DEBUG - if (!usb_in_ev_enable_read()) - fomu_error(__LINE__); +static void handle_setup(void) +{ +#if !DEBUG + uint8_t setup_packet_bfr[10]; #endif - usb_in_ev_pending_write(in_pending); - process_tx(); - } - // An "OUT" transaction just completed so we have new data. - // (But only if we can accept the data) - // if (out_pending) { - if (out_pending) { -#if DEBUG - if (!usb_out_ev_enable_read()) - fomu_error(__LINE__); -#endif - process_rx(); - } + uint8_t setup_pending = usb_setup_ev_pending_read() & usb_setup_ev_enable_read(); + if (!(setup_pending & 1)) + fomu_error(__LINE__); // We got a SETUP packet. Copy it to the setup buffer and clear // the "pending" bit. - if (setup_pending & 1) { - // Setup packets are always 8 bytes, plus two bytes of crc16. - uint32_t setup_length = 0; + // Setup packets are always 8 bytes, plus two bytes of crc16. + uint32_t setup_length = 0; #if DEBUG - if (!(usb_setup_status_read() & (1 << CSR_USB_SETUP_STATUS_HAVE_OFFSET))) - fomu_error(__LINE__); + if (!(usb_setup_status_read() & (1 << CSR_USB_SETUP_STATUS_HAVE_OFFSET))) + fomu_error(__LINE__); #endif - while (usb_setup_status_read() & (1 << CSR_USB_SETUP_STATUS_HAVE_OFFSET)) { - uint8_t c = usb_setup_data_read(); - if (setup_length < sizeof(setup_packet_bfr)) - setup_packet_bfr[setup_length] = c; - setup_length++; - } + while (usb_setup_status_read() & (1 << CSR_USB_SETUP_STATUS_HAVE_OFFSET)) { + uint8_t c = usb_setup_data_read(); + if (setup_length < sizeof(setup_packet_bfr)) + setup_packet_bfr[setup_length] = c; + setup_length++; + } - // If we have 10 bytes, that's a full SETUP packet plus CRC16. - // Otherwise, it was an RX error. - if (setup_length == 10) { - dcd_event_setup_received(rhport, setup_packet_bfr, true); - // Acknowledge the packet, so long as it isn't a SET_ADDRESS - // packet. If it is, leave it unacknowledged and we'll do this - // in the `dcd_set_address` function instead. - // if (!((setup_packet_bfr[0] == 0x00) && (setup_packet_bfr[1] == 0x05))) - usb_setup_ctrl_write(1 << CSR_USB_SETUP_CTRL_ACK_OFFSET); - } + // If we have 10 bytes, that's a full SETUP packet plus CRC16. + // Otherwise, it was an RX error. + if (setup_length == 10) { + dcd_event_setup_received(0, setup_packet_bfr, true); + // Acknowledge the packet, so long as it isn't a SET_ADDRESS + // packet. If it is, leave it unacknowledged and we'll do this + // in the `dcd_set_address` function instead. + // if (!((setup_packet_bfr[0] == 0x00) && (setup_packet_bfr[1] == 0x05))) + usb_setup_ctrl_write(1 << CSR_USB_SETUP_CTRL_ACK_OFFSET); + } #if DEBUG - else { - fomu_error(__LINE__); - } + else { + fomu_error(__LINE__); + } #endif + + usb_setup_ev_pending_write(1); +} +void hal_dcd_isr(uint8_t rhport) +{ + (void)rhport; + uint8_t next_ev; + while ((next_ev = usb_next_ev_read())) { + switch (next_ev) { + case 1 << CSR_USB_NEXT_EV_IN_OFFSET: + handle_in(); + break; + case 1 << CSR_USB_NEXT_EV_OUT_OFFSET: + handle_out(); + break; + case 1 << CSR_USB_NEXT_EV_SETUP_OFFSET: + handle_setup(); + break; + case 1 << CSR_USB_NEXT_EV_RESET_OFFSET: + handle_reset(); + break; + } } } -- cgit v1.3.1 From 1882a87212859e352502bddaccf973d2fd08ad09 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Fri, 1 Nov 2019 16:57:39 +0800 Subject: fomu: remove reference to SETUP_CTRL.ACK THis bit isn't used anymore, so remove it. Signed-off-by: Sean Cross --- src/portable/foosn/fomu/dcd_fomu.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'src/portable') diff --git a/src/portable/foosn/fomu/dcd_fomu.c b/src/portable/foosn/fomu/dcd_fomu.c index 29bd7d485..a77f548c5 100644 --- a/src/portable/foosn/fomu/dcd_fomu.c +++ b/src/portable/foosn/fomu/dcd_fomu.c @@ -576,11 +576,6 @@ static void handle_setup(void) // Otherwise, it was an RX error. if (setup_length == 10) { dcd_event_setup_received(0, setup_packet_bfr, true); - // Acknowledge the packet, so long as it isn't a SET_ADDRESS - // packet. If it is, leave it unacknowledged and we'll do this - // in the `dcd_set_address` function instead. - // if (!((setup_packet_bfr[0] == 0x00) && (setup_packet_bfr[1] == 0x05))) - usb_setup_ctrl_write(1 << CSR_USB_SETUP_CTRL_ACK_OFFSET); } #if DEBUG else { -- cgit v1.3.1 From 3292920933a35c98ecaf88062020cceb6a9bd5c2 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Tue, 12 Nov 2019 20:22:00 -0800 Subject: fomu: first stable working commit This appears to be stable, and works well. Signed-off-by: Sean Cross --- src/portable/foosn/fomu/dcd_fomu.c | 50 ++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 23 deletions(-) (limited to 'src/portable') diff --git a/src/portable/foosn/fomu/dcd_fomu.c b/src/portable/foosn/fomu/dcd_fomu.c index a77f548c5..49ad7d4b5 100644 --- a/src/portable/foosn/fomu/dcd_fomu.c +++ b/src/portable/foosn/fomu/dcd_fomu.c @@ -109,7 +109,7 @@ void xfer_log_append(uint8_t ep_num, uint16_t sz) { xfer_log[xfer_log_offset].ep_num = ep_num; xfer_log[xfer_log_offset].size = sz; xfer_log_offset++; - if (xfer_log_offset > sizeof(xfer_log)/sizeof(*xfer_log)) + if (xfer_log_offset >= sizeof(xfer_log)/sizeof(*xfer_log)) xfer_log_offset = 0; } @@ -117,7 +117,7 @@ void queue_log_append(uint8_t ep_num, uint16_t sz) { queue_log[queue_log_offset].ep_num = ep_num; queue_log[queue_log_offset].size = sz; queue_log_offset++; - if (queue_log_offset > sizeof(queue_log)/sizeof(*queue_log)) + if (queue_log_offset >= sizeof(queue_log)/sizeof(*queue_log)) queue_log_offset = 0; } @@ -132,7 +132,7 @@ static void tx_more_data(void) { usb_log[usb_log_offset].ep_num = tu_edpt_addr(tx_ep, TUSB_DIR_IN); usb_log[usb_log_offset].size = added_bytes; usb_log_offset++; - if (usb_log_offset > sizeof(usb_log)/sizeof(*usb_log)) + if (usb_log_offset >= sizeof(usb_log)/sizeof(*usb_log)) usb_log_offset = 0; // Updating the epno queues the data @@ -167,8 +167,6 @@ static void process_tx(void) { if (!advance_tx_ep()) tx_active = false; - if ((xferred_bytes == 13) && (tx_buffer_max[tx_ep] == 512)) - fomu_error(__LINE__); xfer_log_append(tu_edpt_addr(xferred_ep, TUSB_DIR_IN), xferred_bytes); dcd_event_xfer_complete(0, tu_edpt_addr(xferred_ep, TUSB_DIR_IN), xferred_bytes, XFER_RESULT_SUCCESS, true); if (!tx_active) @@ -213,17 +211,19 @@ static void process_rx(void) { total_read++; if ((rx_buffer_offset[rx_ep] + current_offset) < rx_buffer_max[rx_ep]) { usb_log[usb_log_offset].data[usb_log[usb_log_offset].size++] = c; - rx_buffer[rx_ep][current_offset++] = c; + if (rx_buffer[rx_ep] != (volatile uint8_t *)0xffffffff) + rx_buffer[rx_ep][current_offset++] = c; } } usb_log_offset++; - if (usb_log_offset > sizeof(usb_log)/sizeof(*usb_log)) + if (usb_log_offset >= sizeof(usb_log)/sizeof(*usb_log)) usb_log_offset = 0; #if DEBUG if (total_read > 66) fomu_error(__LINE__); if (total_read < 2) - fomu_error(__LINE__); + total_read = 2; + // fomu_error(__LINE__); #endif // Strip off the CRC16 @@ -232,7 +232,11 @@ static void process_rx(void) { rx_buffer_offset[rx_ep] = rx_buffer_max[rx_ep]; // If there's no more data, complete the transfer to tinyusb - if (rx_buffer_max[rx_ep] == rx_buffer_offset[rx_ep]) { + if ((rx_buffer_max[rx_ep] == rx_buffer_offset[rx_ep]) + // ZLP with less than the total amount of data + || ((total_read == 2) && ((rx_buffer_offset[rx_ep] & 63) == 0)) + // Short read, but not a full packet + || (((rx_buffer_offset[rx_ep] & 63) != 0) && (total_read < 66))) { #if DEBUG if (rx_buffer[rx_ep] == NULL) fomu_error(__LINE__); @@ -249,10 +253,10 @@ static void process_rx(void) { int i; for (i = 0; i < 16; i++) { if ((!!(ep_en_mask & (1 << i))) ^ (!!(rx_buffer[i]))) { - // uint8_t new_status = usb_out_status_read(); - // // Another IRQ came in while we were processing, so ignore this endpoint. - // if ((new_status & 0x20) && ((new_status & 0xf) == i)) - // continue; + uint8_t new_status = usb_out_status_read(); + // Another IRQ came in while we were processing, so ignore this endpoint. + if ((new_status & 0x20) && ((new_status & 0xf) == i)) + continue; fomu_error(__LINE__); } } @@ -280,8 +284,8 @@ static void dcd_reset(void) usb_in_ev_enable_write(0); usb_out_ev_enable_write(0); - if (last_address) - asm("ebreak"); + // if (last_address) + // asm("ebreak"); usb_address_write(0); // Reset all three FIFO handlers @@ -300,9 +304,9 @@ static void dcd_reset(void) tx_active = false; // Enable all event handlers and clear their contents - usb_setup_ev_pending_write(-1); - usb_in_ev_pending_write(-1); - usb_out_ev_pending_write(-1); + usb_setup_ev_pending_write(0xff); + usb_in_ev_pending_write(0xff); + usb_out_ev_pending_write(0xff); usb_in_ev_enable_write(1); usb_out_ev_enable_write(1); usb_setup_ev_enable_write(3); @@ -535,9 +539,9 @@ static void handle_in(void) static void handle_reset(void) { - uint8_t setup_pending = usb_setup_ev_pending_read() & usb_setup_ev_enable_read(); - if (!(setup_pending & 2)) - fomu_error(__LINE__); + // uint8_t setup_pending = usb_setup_ev_pending_read() & usb_setup_ev_enable_read(); + // if (!(setup_pending & 2)) + // fomu_error(__LINE__); usb_setup_ev_pending_write(2); // This event means a bus reset occurred. Reset everything, and @@ -592,8 +596,8 @@ void hal_dcd_isr(uint8_t rhport) while ((next_ev = usb_next_ev_read())) { switch (next_ev) { case 1 << CSR_USB_NEXT_EV_IN_OFFSET: - handle_in(); - break; + handle_in(); + break; case 1 << CSR_USB_NEXT_EV_OUT_OFFSET: handle_out(); break; -- cgit v1.3.1 From e05e9801e46e19bc541c1ca7d6a2cc37902f6b2a Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Tue, 12 Nov 2019 20:55:48 -0800 Subject: fomu: gate debug/logging features This gates the majority of the debug and logging features behind testable macros. Signed-off-by: Sean Cross --- src/portable/foosn/fomu/dcd_fomu.c | 80 +++++++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 28 deletions(-) (limited to 'src/portable') diff --git a/src/portable/foosn/fomu/dcd_fomu.c b/src/portable/foosn/fomu/dcd_fomu.c index 49ad7d4b5..ee79366e4 100644 --- a/src/portable/foosn/fomu/dcd_fomu.c +++ b/src/portable/foosn/fomu/dcd_fomu.c @@ -25,21 +25,24 @@ */ #ifndef DEBUG -#define DEBUG 1 +#define DEBUG 0 #endif + +#ifndef LOG_USB +#define LOG_USB 0 +#endif + #include "tusb_option.h" -// #if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_FOMU_EPTRI) -#if 1 +#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_FOMU_EPTRI) #include "device/dcd.h" #include "dcd_fomu.h" #include "csr.h" #include "irq.h" void fomu_error(uint32_t line); -void mputs(const char *str); -void mputln(const char *str); +#if LOG_USB struct usb_log { uint8_t ep_num; uint8_t size; @@ -63,8 +66,7 @@ __attribute__((used)) struct xfer_log queue_log[64]; __attribute__((used)) uint8_t queue_log_offset; - -static uint8_t last_address; +#endif //--------------------------------------------------------------------+ // SIE Command @@ -105,6 +107,7 @@ static bool advance_tx_ep(void) { return true; } +#if LOG_USB void xfer_log_append(uint8_t ep_num, uint16_t sz) { xfer_log[xfer_log_offset].ep_num = ep_num; xfer_log[xfer_log_offset].size = sz; @@ -120,20 +123,25 @@ void queue_log_append(uint8_t ep_num, uint16_t sz) { if (queue_log_offset >= sizeof(queue_log)/sizeof(*queue_log)) queue_log_offset = 0; } +#endif static void tx_more_data(void) { // Send more data uint8_t added_bytes; for (added_bytes = 0; (added_bytes < EP_SIZE) && (tx_buffer_offset[tx_ep] < tx_buffer_max[tx_ep]); added_bytes++) { +#if LOG_USB usb_log[usb_log_offset].data[added_bytes] = tx_buffer[tx_ep][tx_buffer_offset[tx_ep]]; +#endif usb_in_data_write(tx_buffer[tx_ep][tx_buffer_offset[tx_ep]++]); } +#if LOG_USB usb_log[usb_log_offset].ep_num = tu_edpt_addr(tx_ep, TUSB_DIR_IN); usb_log[usb_log_offset].size = added_bytes; usb_log_offset++; if (usb_log_offset >= sizeof(usb_log)/sizeof(*usb_log)) usb_log_offset = 0; +#endif // Updating the epno queues the data usb_in_ctrl_write(tx_ep & 0xf); @@ -167,7 +175,9 @@ static void process_tx(void) { if (!advance_tx_ep()) tx_active = false; +#if LOG_USB xfer_log_append(tu_edpt_addr(xferred_ep, TUSB_DIR_IN), xferred_bytes); +#endif dcd_event_xfer_complete(0, tu_edpt_addr(xferred_ep, TUSB_DIR_IN), xferred_bytes, XFER_RESULT_SUCCESS, true); if (!tx_active) return; @@ -201,23 +211,34 @@ static void process_rx(void) { uint32_t total_read = 0; uint32_t current_offset = rx_buffer_offset[rx_ep]; #if DEBUG + uint8_t test_buffer[256]; + memset(test_buffer, 0, sizeof(test_buffer)); if (current_offset > rx_buffer_max[rx_ep]) fomu_error(__LINE__); #endif +#if LOG_USB usb_log[usb_log_offset].ep_num = tu_edpt_addr(rx_ep, TUSB_DIR_OUT); usb_log[usb_log_offset].size = 0; +#endif while (usb_out_status_read() & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET)) { uint8_t c = usb_out_data_read(); +#if DEBUG + test_buffer[total_read] = c; +#endif total_read++; if ((rx_buffer_offset[rx_ep] + current_offset) < rx_buffer_max[rx_ep]) { +#if LOG_USB usb_log[usb_log_offset].data[usb_log[usb_log_offset].size++] = c; +#endif if (rx_buffer[rx_ep] != (volatile uint8_t *)0xffffffff) rx_buffer[rx_ep][current_offset++] = c; } } +#if LOG_USB usb_log_offset++; if (usb_log_offset >= sizeof(usb_log)/sizeof(*usb_log)) usb_log_offset = 0; +#endif #if DEBUG if (total_read > 66) fomu_error(__LINE__); @@ -261,7 +282,9 @@ static void process_rx(void) { } } #endif +#if LOG_USB xfer_log_append(tu_edpt_addr(rx_ep, TUSB_DIR_OUT), len); +#endif dcd_event_xfer_complete(0, tu_edpt_addr(rx_ep, TUSB_DIR_OUT), len, XFER_RESULT_SUCCESS, true); } else { @@ -284,8 +307,6 @@ static void dcd_reset(void) usb_in_ev_enable_write(0); usb_out_ev_enable_write(0); - // if (last_address) - // asm("ebreak"); usb_address_write(0); // Reset all three FIFO handlers @@ -360,7 +381,6 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr) // Activate the new address usb_address_write(dev_addr); - last_address = dev_addr; } // Called when the device received SET_CONFIG request, you can leave this @@ -407,8 +427,6 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { (void) rhport; - if (tu_edpt_number(ep_addr) == 2) - fomu_error(__LINE__); if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) { uint8_t enable = 0; @@ -455,9 +473,9 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t t ; dcd_int_disable(0); +#if LOG_USB queue_log_append(ep_addr, total_bytes); - if (total_bytes == 499) - asm("ebreak"); +#endif // If a reset happens while we're waiting, abort the transfer if (previous_reset_count != reset_count) return true; @@ -485,24 +503,26 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t t TU_ASSERT(rx_buffer[ep_num] == NULL); dcd_int_disable(0); +#if LOG_USB queue_log_append(ep_addr, total_bytes); +#endif rx_buffer[ep_num] = buffer; rx_buffer_offset[ep_num] = 0; rx_buffer_max[ep_num] = total_bytes; // Enable receiving on this particular endpoint usb_out_ctrl_write((1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | ep_num); -// #if DEBUG -// uint16_t ep_en_mask = usb_out_enable_status_read(); -// int i; -// for (i = 0; i < 16; i++) { -// if ((!!(ep_en_mask & (1 << i))) ^ (!!(rx_buffer[i]))) { -// if (rx_buffer[i] && usb_out_ev_pending_read() && (usb_out_status_read() & 0xf) == i) -// continue; -// fomu_error(__LINE__); -// } -// } -// #endif +#if DEBUG + uint16_t ep_en_mask = usb_out_enable_status_read(); + int i; + for (i = 0; i < 16; i++) { + if ((!!(ep_en_mask & (1 << i))) ^ (!!(rx_buffer[i]))) { + if (rx_buffer[i] && usb_out_ev_pending_read() && (usb_out_status_read() & 0xf) == i) + continue; + fomu_error(__LINE__); + } + } +#endif dcd_int_enable(0); } return true; @@ -539,9 +559,11 @@ static void handle_in(void) static void handle_reset(void) { - // uint8_t setup_pending = usb_setup_ev_pending_read() & usb_setup_ev_enable_read(); - // if (!(setup_pending & 2)) - // fomu_error(__LINE__); +#if DEBUG + uint8_t setup_pending = usb_setup_ev_pending_read() & usb_setup_ev_enable_read(); + if (!(setup_pending & 2)) + fomu_error(__LINE__); +#endif usb_setup_ev_pending_write(2); // This event means a bus reset occurred. Reset everything, and @@ -555,9 +577,11 @@ static void handle_setup(void) uint8_t setup_packet_bfr[10]; #endif +#if DEBUG uint8_t setup_pending = usb_setup_ev_pending_read() & usb_setup_ev_enable_read(); if (!(setup_pending & 1)) fomu_error(__LINE__); +#endif // We got a SETUP packet. Copy it to the setup buffer and clear // the "pending" bit. -- cgit v1.3.1 From 8c5f02960b7d41f663b0c6063a0cc268ef8d2270 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Tue, 12 Nov 2019 21:11:46 -0800 Subject: valentyusb: rename from `foosn` While Fomu is produced by Foosn, the actual name of the hardware block is `valentyusb`. Rename the module to match that. Signed-off-by: Sean Cross --- hw/bsp/fomu/board.mk | 6 +- src/portable/foosn/fomu/dcd_fomu.c | 638 ------------------------------ src/portable/foosn/fomu/dcd_fomu.h | 151 ------- src/portable/foosn/fomu/hal_fomu.c | 33 -- src/portable/valentyusb/eptri/dcd_eptri.c | 638 ++++++++++++++++++++++++++++++ src/portable/valentyusb/eptri/dcd_eptri.h | 39 ++ src/portable/valentyusb/eptri/hal_eptri.c | 33 ++ 7 files changed, 713 insertions(+), 825 deletions(-) delete mode 100644 src/portable/foosn/fomu/dcd_fomu.c delete mode 100644 src/portable/foosn/fomu/dcd_fomu.h delete mode 100644 src/portable/foosn/fomu/hal_fomu.c create mode 100644 src/portable/valentyusb/eptri/dcd_eptri.c create mode 100644 src/portable/valentyusb/eptri/dcd_eptri.h create mode 100644 src/portable/valentyusb/eptri/hal_eptri.c (limited to 'src/portable') diff --git a/hw/bsp/fomu/board.mk b/hw/bsp/fomu/board.mk index 86f8df23f..cdcd63b28 100644 --- a/hw/bsp/fomu/board.mk +++ b/hw/bsp/fomu/board.mk @@ -2,7 +2,7 @@ CFLAGS += \ -march=rv32i \ -mabi=ilp32 \ -nostdlib \ - -DCFG_TUSB_MCU=OPT_MCU_FOMU_EPTRI + -DCFG_TUSB_MCU=OPT_MCU_VALENTYUSB_EPTRI MCU_DIR = hw/mcu/fomu BSP_DIR = hw/bsp/fomu @@ -21,8 +21,8 @@ INC += \ $(TOP)/$(BSP_DIR)/include # For TinyUSB port source -VENDOR = foosn -CHIP_FAMILY = fomu +VENDOR = valentyusb +CHIP_FAMILY = eptri # flash using dfu-util flash: $(BUILD)/$(BOARD)-firmware.dfu diff --git a/src/portable/foosn/fomu/dcd_fomu.c b/src/portable/foosn/fomu/dcd_fomu.c deleted file mode 100644 index ee79366e4..000000000 --- a/src/portable/foosn/fomu/dcd_fomu.c +++ /dev/null @@ -1,638 +0,0 @@ -/* - * 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. - */ - -#ifndef DEBUG -#define DEBUG 0 -#endif - -#ifndef LOG_USB -#define LOG_USB 0 -#endif - -#include "tusb_option.h" - -#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_FOMU_EPTRI) - -#include "device/dcd.h" -#include "dcd_fomu.h" -#include "csr.h" -#include "irq.h" -void fomu_error(uint32_t line); - -#if LOG_USB -struct usb_log { - uint8_t ep_num; - uint8_t size; - uint8_t data[66]; -}; -__attribute__((used)) -struct usb_log usb_log[128]; -__attribute__((used)) -uint8_t usb_log_offset; - -struct xfer_log { - uint8_t ep_num; - uint16_t size; -}; -__attribute__((used)) -struct xfer_log xfer_log[64]; -__attribute__((used)) -uint8_t xfer_log_offset; - -__attribute__((used)) -struct xfer_log queue_log[64]; -__attribute__((used)) -uint8_t queue_log_offset; -#endif - -//--------------------------------------------------------------------+ -// SIE Command -//--------------------------------------------------------------------+ - -#define EP_SIZE 64 - -uint16_t volatile rx_buffer_offset[16]; -uint8_t volatile * rx_buffer[16]; -uint16_t volatile rx_buffer_max[16]; - -volatile uint8_t tx_ep; -volatile bool tx_active; -volatile uint16_t tx_buffer_offset[16]; -uint8_t volatile * tx_buffer[16]; -volatile uint16_t tx_buffer_max[16]; -volatile uint8_t reset_count; - -#if DEBUG -__attribute__((used)) uint8_t volatile * last_tx_buffer; -__attribute__((used)) volatile uint8_t last_tx_ep; -uint8_t setup_packet_bfr[10]; -#endif - -//--------------------------------------------------------------------+ -// PIPE HELPER -//--------------------------------------------------------------------+ - -static bool advance_tx_ep(void) { - // Move on to the next transmit buffer in a round-robin manner - uint8_t prev_tx_ep = tx_ep; - for (tx_ep = (tx_ep + 1) & 0xf; tx_ep != prev_tx_ep; tx_ep = ((tx_ep + 1) & 0xf)) { - if (tx_buffer[tx_ep]) - return true; - } - if (!tx_buffer[tx_ep]) - return false; - return true; -} - -#if LOG_USB -void xfer_log_append(uint8_t ep_num, uint16_t sz) { - xfer_log[xfer_log_offset].ep_num = ep_num; - xfer_log[xfer_log_offset].size = sz; - xfer_log_offset++; - if (xfer_log_offset >= sizeof(xfer_log)/sizeof(*xfer_log)) - xfer_log_offset = 0; -} - -void queue_log_append(uint8_t ep_num, uint16_t sz) { - queue_log[queue_log_offset].ep_num = ep_num; - queue_log[queue_log_offset].size = sz; - queue_log_offset++; - if (queue_log_offset >= sizeof(queue_log)/sizeof(*queue_log)) - queue_log_offset = 0; -} -#endif - -static void tx_more_data(void) { - // Send more data - uint8_t added_bytes; - for (added_bytes = 0; (added_bytes < EP_SIZE) && (tx_buffer_offset[tx_ep] < tx_buffer_max[tx_ep]); added_bytes++) { -#if LOG_USB - usb_log[usb_log_offset].data[added_bytes] = tx_buffer[tx_ep][tx_buffer_offset[tx_ep]]; -#endif - usb_in_data_write(tx_buffer[tx_ep][tx_buffer_offset[tx_ep]++]); - } - -#if LOG_USB - usb_log[usb_log_offset].ep_num = tu_edpt_addr(tx_ep, TUSB_DIR_IN); - usb_log[usb_log_offset].size = added_bytes; - usb_log_offset++; - if (usb_log_offset >= sizeof(usb_log)/sizeof(*usb_log)) - usb_log_offset = 0; -#endif - - // Updating the epno queues the data - usb_in_ctrl_write(tx_ep & 0xf); -} - -static void process_tx(void) { -#if DEBUG - // If the system isn't idle, then something is very wrong. - uint8_t in_status = usb_in_status_read(); - if (!(in_status & (1 << CSR_USB_IN_STATUS_IDLE_OFFSET))) - fomu_error(__LINE__); -#endif - - // If the buffer is now empty, search for the next buffer to fill. - if (!tx_buffer[tx_ep]) { - if (advance_tx_ep()) - tx_more_data(); - else - tx_active = false; - return; - } - - if (tx_buffer_offset[tx_ep] >= tx_buffer_max[tx_ep]) { -#if DEBUG - last_tx_buffer = tx_buffer[tx_ep]; - last_tx_ep = tx_ep; -#endif - tx_buffer[tx_ep] = NULL; - uint16_t xferred_bytes = tx_buffer_max[tx_ep]; - uint8_t xferred_ep = tx_ep; - - if (!advance_tx_ep()) - tx_active = false; -#if LOG_USB - xfer_log_append(tu_edpt_addr(xferred_ep, TUSB_DIR_IN), xferred_bytes); -#endif - dcd_event_xfer_complete(0, tu_edpt_addr(xferred_ep, TUSB_DIR_IN), xferred_bytes, XFER_RESULT_SUCCESS, true); - if (!tx_active) - return; - } - - tx_more_data(); - return; -} - -static void process_rx(void) { - uint8_t out_status = usb_out_status_read(); -#if DEBUG - // If the OUT handler is still waiting to send, don't do anything. - if (!(out_status & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET))) - fomu_error(__LINE__); - // return; -#endif - uint8_t rx_ep = (out_status >> CSR_USB_OUT_STATUS_EPNO_OFFSET) & 0xf; - - // If the destination buffer doesn't exist, don't drain the hardware - // fifo. Note that this can cause deadlocks if the host is waiting - // on some other endpoint's data! -#if DEBUG - if (rx_buffer[rx_ep] == NULL) { - fomu_error(__LINE__); - return; - } -#endif - - // Drain the FIFO into the destination buffer - uint32_t total_read = 0; - uint32_t current_offset = rx_buffer_offset[rx_ep]; -#if DEBUG - uint8_t test_buffer[256]; - memset(test_buffer, 0, sizeof(test_buffer)); - if (current_offset > rx_buffer_max[rx_ep]) - fomu_error(__LINE__); -#endif -#if LOG_USB - usb_log[usb_log_offset].ep_num = tu_edpt_addr(rx_ep, TUSB_DIR_OUT); - usb_log[usb_log_offset].size = 0; -#endif - while (usb_out_status_read() & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET)) { - uint8_t c = usb_out_data_read(); -#if DEBUG - test_buffer[total_read] = c; -#endif - total_read++; - if ((rx_buffer_offset[rx_ep] + current_offset) < rx_buffer_max[rx_ep]) { -#if LOG_USB - usb_log[usb_log_offset].data[usb_log[usb_log_offset].size++] = c; -#endif - if (rx_buffer[rx_ep] != (volatile uint8_t *)0xffffffff) - rx_buffer[rx_ep][current_offset++] = c; - } - } -#if LOG_USB - usb_log_offset++; - if (usb_log_offset >= sizeof(usb_log)/sizeof(*usb_log)) - usb_log_offset = 0; -#endif -#if DEBUG - if (total_read > 66) - fomu_error(__LINE__); - if (total_read < 2) - total_read = 2; - // fomu_error(__LINE__); -#endif - - // Strip off the CRC16 - rx_buffer_offset[rx_ep] += (total_read - 2); - if (rx_buffer_offset[rx_ep] > rx_buffer_max[rx_ep]) - rx_buffer_offset[rx_ep] = rx_buffer_max[rx_ep]; - - // If there's no more data, complete the transfer to tinyusb - if ((rx_buffer_max[rx_ep] == rx_buffer_offset[rx_ep]) - // ZLP with less than the total amount of data - || ((total_read == 2) && ((rx_buffer_offset[rx_ep] & 63) == 0)) - // Short read, but not a full packet - || (((rx_buffer_offset[rx_ep] & 63) != 0) && (total_read < 66))) { -#if DEBUG - if (rx_buffer[rx_ep] == NULL) - fomu_error(__LINE__); -#endif - - // Free up this buffer. - rx_buffer[rx_ep] = NULL; - uint16_t len = rx_buffer_offset[rx_ep]; - -#if DEBUG - // Validate that all enabled endpoints have buffers, - // and no disabled endpoints have buffers. - uint16_t ep_en_mask = usb_out_enable_status_read(); - int i; - for (i = 0; i < 16; i++) { - if ((!!(ep_en_mask & (1 << i))) ^ (!!(rx_buffer[i]))) { - uint8_t new_status = usb_out_status_read(); - // Another IRQ came in while we were processing, so ignore this endpoint. - if ((new_status & 0x20) && ((new_status & 0xf) == i)) - continue; - fomu_error(__LINE__); - } - } -#endif -#if LOG_USB - xfer_log_append(tu_edpt_addr(rx_ep, TUSB_DIR_OUT), len); -#endif - dcd_event_xfer_complete(0, tu_edpt_addr(rx_ep, TUSB_DIR_OUT), len, XFER_RESULT_SUCCESS, true); - } - else { - // If there's more data, re-enable data reception on this endpoint - usb_out_ctrl_write((1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | rx_ep); - } - - // Now that the buffer is drained, clear the pending IRQ. - usb_out_ev_pending_write(usb_out_ev_pending_read()); -} - -//--------------------------------------------------------------------+ -// CONTROLLER API -//--------------------------------------------------------------------+ - -static void dcd_reset(void) -{ - reset_count++; - usb_setup_ev_enable_write(0); - usb_in_ev_enable_write(0); - usb_out_ev_enable_write(0); - - usb_address_write(0); - - // Reset all three FIFO handlers - usb_setup_ctrl_write(1 << CSR_USB_SETUP_CTRL_RESET_OFFSET); - usb_in_ctrl_write(1 << CSR_USB_IN_CTRL_RESET_OFFSET); - usb_out_ctrl_write(1 << CSR_USB_OUT_CTRL_RESET_OFFSET); - - memset((void *)rx_buffer, 0, sizeof(rx_buffer)); - memset((void *)rx_buffer_max, 0, sizeof(rx_buffer_max)); - memset((void *)rx_buffer_offset, 0, sizeof(rx_buffer_offset)); - - memset((void *)tx_buffer, 0, sizeof(tx_buffer)); - memset((void *)tx_buffer_max, 0, sizeof(tx_buffer_max)); - memset((void *)tx_buffer_offset, 0, sizeof(tx_buffer_offset)); - tx_ep = 0; - tx_active = false; - - // Enable all event handlers and clear their contents - usb_setup_ev_pending_write(0xff); - usb_in_ev_pending_write(0xff); - usb_out_ev_pending_write(0xff); - usb_in_ev_enable_write(1); - usb_out_ev_enable_write(1); - usb_setup_ev_enable_write(3); - - dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true); -} - -// Initializes the USB peripheral for device mode and enables it. -void dcd_init(uint8_t rhport) -{ - (void) rhport; - - usb_pullup_out_write(0); - - // Enable all event handlers and clear their contents - usb_setup_ev_pending_write(usb_setup_ev_pending_read()); - usb_in_ev_pending_write(usb_in_ev_pending_read()); - usb_out_ev_pending_write(usb_out_ev_pending_read()); - usb_in_ev_enable_write(1); - usb_out_ev_enable_write(1); - usb_setup_ev_enable_write(3); - - // Turn on the external pullup - usb_pullup_out_write(1); -} - -// Enables or disables the USB device interrupt(s). May be used to -// prevent concurrency issues when mutating data structures shared -// between main code and the interrupt handler. -void dcd_int_enable(uint8_t rhport) -{ - (void) rhport; - irq_setmask(irq_getmask() | (1 << USB_INTERRUPT)); -} - -void dcd_int_disable(uint8_t rhport) -{ - (void) rhport; - irq_setmask(irq_getmask() & ~(1 << USB_INTERRUPT)); -} - -// Called when the device is given a new bus address. -void dcd_set_address(uint8_t rhport, uint8_t dev_addr) -{ - // Respond with ACK status first before changing device address - dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); - - // Wait for the response packet to get sent - while (tx_active) - ; - - // Activate the new address - usb_address_write(dev_addr); -} - -// Called when the device received SET_CONFIG request, you can leave this -// empty if your peripheral does not require any specific action. -void dcd_set_config(uint8_t rhport, uint8_t config_num) -{ - (void) rhport; - (void) config_num; -} - -// Called to remote wake up host when suspended (e.g hid keyboard) -void dcd_remote_wakeup(uint8_t rhport) -{ - (void) rhport; -} - -//--------------------------------------------------------------------+ -// DCD Endpoint Port -//--------------------------------------------------------------------+ -bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) -{ - (void) rhport; - uint8_t ep_num = tu_edpt_number(p_endpoint_desc->bEndpointAddress); - uint8_t ep_dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); - - if (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) - return false; // Not supported - - if (ep_dir == TUSB_DIR_OUT) { - rx_buffer_offset[ep_num] = 0; - rx_buffer_max[ep_num] = 0; - rx_buffer[ep_num] = NULL; - } - - else if (ep_dir == TUSB_DIR_OUT) { - tx_buffer_offset[ep_num] = 0; - tx_buffer_max[ep_num] = 0; - tx_buffer[ep_num] = NULL; - } - - return true; -} - -void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) -{ - (void) rhport; - - if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) { - uint8_t enable = 0; - if (rx_buffer[ep_addr]) - enable = 1; - usb_out_ctrl_write((1 << CSR_USB_OUT_CTRL_STALL_OFFSET) | (enable << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | tu_edpt_number(ep_addr)); - } - else - usb_in_ctrl_write((1 << CSR_USB_IN_CTRL_STALL_OFFSET) | tu_edpt_number(ep_addr)); -} - -void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) -{ - (void) rhport; - if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) { - uint8_t enable = 0; - if (rx_buffer[ep_addr]) - enable = 1; - usb_out_ctrl_write((0 << CSR_USB_OUT_CTRL_STALL_OFFSET) | (enable << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | tu_edpt_number(ep_addr)); - } - // IN endpoints will get unstalled when more data is written. -} - -bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) -{ - (void)rhport; - uint8_t ep_num = tu_edpt_number(ep_addr); - uint8_t ep_dir = tu_edpt_dir(ep_addr); - TU_ASSERT(ep_num < 16); - - // Give a nonzero buffer when we transmit 0 bytes, so that the - // system doesn't think the endpoint is idle. - if ((buffer == NULL) && (total_bytes == 0)) { - buffer = (uint8_t *)0xffffffff; - } - - TU_ASSERT(buffer != NULL); - - if (ep_dir == TUSB_DIR_IN) { - // Wait for the tx pipe to free up - uint8_t previous_reset_count = reset_count; - // Continue until the buffer is empty, the system is idle, and the fifo is empty. - while (tx_buffer[ep_num] != NULL) - ; - - dcd_int_disable(0); -#if LOG_USB - queue_log_append(ep_addr, total_bytes); -#endif - // If a reset happens while we're waiting, abort the transfer - if (previous_reset_count != reset_count) - return true; - - TU_ASSERT(tx_buffer[ep_num] == NULL); - tx_buffer_offset[ep_num] = 0; - tx_buffer_max[ep_num] = total_bytes; - tx_buffer[ep_num] = buffer; - - // If the current buffer is NULL, then that means the tx logic is idle. - // Update the tx_ep to point to our endpoint number and queue the data. - // Otherwise, let it be and it'll get picked up after the next transfer - // finishes. - if (!tx_active) { - tx_ep = ep_num; - tx_active = true; - tx_more_data(); - } - dcd_int_enable(0); - } - - else if (ep_dir == TUSB_DIR_OUT) { - while (rx_buffer[ep_num] != NULL) - ; - - TU_ASSERT(rx_buffer[ep_num] == NULL); - dcd_int_disable(0); -#if LOG_USB - queue_log_append(ep_addr, total_bytes); -#endif - rx_buffer[ep_num] = buffer; - rx_buffer_offset[ep_num] = 0; - rx_buffer_max[ep_num] = total_bytes; - - // Enable receiving on this particular endpoint - usb_out_ctrl_write((1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | ep_num); -#if DEBUG - uint16_t ep_en_mask = usb_out_enable_status_read(); - int i; - for (i = 0; i < 16; i++) { - if ((!!(ep_en_mask & (1 << i))) ^ (!!(rx_buffer[i]))) { - if (rx_buffer[i] && usb_out_ev_pending_read() && (usb_out_status_read() & 0xf) == i) - continue; - fomu_error(__LINE__); - } - } -#endif - dcd_int_enable(0); - } - return true; -} - -//--------------------------------------------------------------------+ -// ISR -//--------------------------------------------------------------------+ - -static void handle_out(void) -{ - // An "OUT" transaction just completed so we have new data. - // (But only if we can accept the data) -#if DEBUG - if (!usb_out_ev_pending_read()) - fomu_error(__LINE__); - if (!usb_out_ev_enable_read()) - fomu_error(__LINE__); -#endif - process_rx(); -} - -static void handle_in(void) -{ -#if DEBUG - if (!usb_in_ev_pending_read()) - fomu_error(__LINE__); - if (!usb_in_ev_enable_read()) - fomu_error(__LINE__); -#endif - usb_in_ev_pending_write(usb_in_ev_pending_read()); - process_tx(); -} - -static void handle_reset(void) -{ -#if DEBUG - uint8_t setup_pending = usb_setup_ev_pending_read() & usb_setup_ev_enable_read(); - if (!(setup_pending & 2)) - fomu_error(__LINE__); -#endif - usb_setup_ev_pending_write(2); - - // This event means a bus reset occurred. Reset everything, and - // abandon any further processing. - dcd_reset(); -} - -static void handle_setup(void) -{ -#if !DEBUG - uint8_t setup_packet_bfr[10]; -#endif - -#if DEBUG - uint8_t setup_pending = usb_setup_ev_pending_read() & usb_setup_ev_enable_read(); - if (!(setup_pending & 1)) - fomu_error(__LINE__); -#endif - - // We got a SETUP packet. Copy it to the setup buffer and clear - // the "pending" bit. - // Setup packets are always 8 bytes, plus two bytes of crc16. - uint32_t setup_length = 0; - -#if DEBUG - if (!(usb_setup_status_read() & (1 << CSR_USB_SETUP_STATUS_HAVE_OFFSET))) - fomu_error(__LINE__); -#endif - - while (usb_setup_status_read() & (1 << CSR_USB_SETUP_STATUS_HAVE_OFFSET)) { - uint8_t c = usb_setup_data_read(); - if (setup_length < sizeof(setup_packet_bfr)) - setup_packet_bfr[setup_length] = c; - setup_length++; - } - - // If we have 10 bytes, that's a full SETUP packet plus CRC16. - // Otherwise, it was an RX error. - if (setup_length == 10) { - dcd_event_setup_received(0, setup_packet_bfr, true); - } -#if DEBUG - else { - fomu_error(__LINE__); - } -#endif - - usb_setup_ev_pending_write(1); -} -void hal_dcd_isr(uint8_t rhport) -{ - (void)rhport; - uint8_t next_ev; - while ((next_ev = usb_next_ev_read())) { - switch (next_ev) { - case 1 << CSR_USB_NEXT_EV_IN_OFFSET: - handle_in(); - break; - case 1 << CSR_USB_NEXT_EV_OUT_OFFSET: - handle_out(); - break; - case 1 << CSR_USB_NEXT_EV_SETUP_OFFSET: - handle_setup(); - break; - case 1 << CSR_USB_NEXT_EV_RESET_OFFSET: - handle_reset(); - break; - } - } -} - -#endif diff --git a/src/portable/foosn/fomu/dcd_fomu.h b/src/portable/foosn/fomu/dcd_fomu.h deleted file mode 100644 index 60bc1240f..000000000 --- a/src/portable/foosn/fomu/dcd_fomu.h +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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. - */ - -#ifndef _TUSB_DCD_FOMU_H_ -#define _TUSB_DCD_FOMU_H_ - -#include "common/tusb_common.h" -#ifdef __cplusplus - extern "C" { -#endif - -// //--------------------------------------------------------------------+ -// // Register Interface -// //--------------------------------------------------------------------+ - -// //------------- USB Interrupt USBIntSt -------------// -// //enum { -// // DCD_USB_REQ_LOW_PRIO_MASK = TU_BIT(0), -// // DCD_USB_REQ_HIGH_PRIO_MASK = TU_BIT(1), -// // DCD_USB_REQ_DMA_MASK = TU_BIT(2), -// // DCD_USB_REQ_NEED_CLOCK_MASK = TU_BIT(8), -// // DCD_USB_REQ_ENABLE_MASK = TU_BIT(31) -// //}; - -// //------------- Device Interrupt USBDevInt -------------// -// enum { -// DEV_INT_FRAME_MASK = TU_BIT(0), -// DEV_INT_ENDPOINT_FAST_MASK = TU_BIT(1), -// DEV_INT_ENDPOINT_SLOW_MASK = TU_BIT(2), -// DEV_INT_DEVICE_STATUS_MASK = TU_BIT(3), -// DEV_INT_COMMAND_CODE_EMPTY_MASK = TU_BIT(4), -// DEV_INT_COMMAND_DATA_FULL_MASK = TU_BIT(5), -// DEV_INT_RX_ENDPOINT_PACKET_MASK = TU_BIT(6), -// DEV_INT_TX_ENDPOINT_PACKET_MASK = TU_BIT(7), -// DEV_INT_ENDPOINT_REALIZED_MASK = TU_BIT(8), -// DEV_INT_ERROR_MASK = TU_BIT(9) -// }; - -// //------------- DMA Interrupt USBDMAInt-------------// -// enum { -// DMA_INT_END_OF_XFER_MASK = TU_BIT(0), -// DMA_INT_NEW_DD_REQUEST_MASK = TU_BIT(1), -// DMA_INT_ERROR_MASK = TU_BIT(2) -// }; - -// //------------- USBCtrl -------------// -// enum { -// USBCTRL_READ_ENABLE_MASK = TU_BIT(0), -// USBCTRL_WRITE_ENABLE_MASK = TU_BIT(1), -// }; - -// //------------- USBRxPLen -------------// -// enum { -// USBRXPLEN_PACKET_LENGTH_MASK = (TU_BIT(10)-1), -// USBRXPLEN_DATA_VALID_MASK = TU_BIT(10), -// USBRXPLEN_PACKET_READY_MASK = TU_BIT(11), -// }; - -// //------------- SIE Command Code -------------// -// typedef enum -// { -// SIE_CMDPHASE_WRITE = 1, -// SIE_CMDPHASE_READ = 2, -// SIE_CMDPHASE_COMMAND = 5 -// } sie_cmdphase_t; - -// enum { -// // device commands -// SIE_CMDCODE_SET_ADDRESS = 0xd0, -// SIE_CMDCODE_CONFIGURE_DEVICE = 0xd8, -// SIE_CMDCODE_SET_MODE = 0xf3, -// SIE_CMDCODE_READ_FRAME_NUMBER = 0xf5, -// SIE_CMDCODE_READ_TEST_REGISTER = 0xfd, -// SIE_CMDCODE_DEVICE_STATUS = 0xfe, -// SIE_CMDCODE_GET_ERROR = 0xff, -// SIE_CMDCODE_READ_ERROR_STATUS = 0xfb, - -// // endpoint commands -// SIE_CMDCODE_ENDPOINT_SELECT = 0x00, // + endpoint index -// SIE_CMDCODE_ENDPOINT_SELECT_CLEAR_INTERRUPT = 0x40, // + endpoint index, should use USBEpIntClr instead -// SIE_CMDCODE_ENDPOINT_SET_STATUS = 0x40, // + endpoint index -// SIE_CMDCODE_BUFFER_CLEAR = 0xf2, -// SIE_CMDCODE_BUFFER_VALIDATE = 0xfa -// }; - -// //------------- SIE Device Status (get/set from SIE_CMDCODE_DEVICE_STATUS) -------------// -// enum { -// SIE_DEV_STATUS_CONNECT_STATUS_MASK = TU_BIT(0), -// SIE_DEV_STATUS_CONNECT_CHANGE_MASK = TU_BIT(1), -// SIE_DEV_STATUS_SUSPEND_MASK = TU_BIT(2), -// SIE_DEV_STATUS_SUSPEND_CHANGE_MASK = TU_BIT(3), -// SIE_DEV_STATUS_RESET_MASK = TU_BIT(4) -// }; - -// //------------- SIE Select Endpoint Command -------------// -// enum { -// SIE_SELECT_ENDPOINT_FULL_EMPTY_MASK = TU_BIT(0), // 0: empty, 1 full. IN endpoint checks empty, OUT endpoint check full -// SIE_SELECT_ENDPOINT_STALL_MASK = TU_BIT(1), -// SIE_SELECT_ENDPOINT_SETUP_RECEIVED_MASK = TU_BIT(2), // clear by SIE_CMDCODE_ENDPOINT_SELECT_CLEAR_INTERRUPT -// SIE_SELECT_ENDPOINT_PACKET_OVERWRITTEN_MASK = TU_BIT(3), // previous packet is overwritten by a SETUP packet -// SIE_SELECT_ENDPOINT_NAK_MASK = TU_BIT(4), // last packet response is NAK (auto clear by an ACK) -// SIE_SELECT_ENDPOINT_BUFFER1_FULL_MASK = TU_BIT(5), -// SIE_SELECT_ENDPOINT_BUFFER2_FULL_MASK = TU_BIT(6) -// }; - -// typedef enum -// { -// SIE_SET_ENDPOINT_STALLED_MASK = TU_BIT(0), -// SIE_SET_ENDPOINT_DISABLED_MASK = TU_BIT(5), -// SIE_SET_ENDPOINT_RATE_FEEDBACK_MASK = TU_BIT(6), -// SIE_SET_ENDPOINT_CONDITION_STALLED_MASK = TU_BIT(7), -// }sie_endpoint_set_status_mask_t; - -// //------------- DMA Descriptor Status -------------// -// enum { -// DD_STATUS_NOT_SERVICED = 0, -// DD_STATUS_BEING_SERVICED, -// DD_STATUS_NORMAL, -// DD_STATUS_DATA_UNDERUN, // short packet -// DD_STATUS_DATA_OVERRUN, -// DD_STATUS_SYSTEM_ERROR -// }; - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_DCD_FOMU_H_ */ diff --git a/src/portable/foosn/fomu/hal_fomu.c b/src/portable/foosn/fomu/hal_fomu.c deleted file mode 100644 index ebc0a1d93..000000000 --- a/src/portable/foosn/fomu/hal_fomu.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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 "common/tusb_common.h" - -#if (CFG_TUSB_MCU == OPT_MCU_FOMU_EPTRI) - -// No HAL-specific stuff here! - -#endif diff --git a/src/portable/valentyusb/eptri/dcd_eptri.c b/src/portable/valentyusb/eptri/dcd_eptri.c new file mode 100644 index 000000000..d7e1b8a9a --- /dev/null +++ b/src/portable/valentyusb/eptri/dcd_eptri.c @@ -0,0 +1,638 @@ +/* + * 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. + */ + +#ifndef DEBUG +#define DEBUG 0 +#endif + +#ifndef LOG_USB +#define LOG_USB 0 +#endif + +#include "tusb_option.h" + +#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_VALENTYUSB_EPTRI) + +#include "device/dcd.h" +#include "dcd_eptri.h" +#include "csr.h" +#include "irq.h" +void fomu_error(uint32_t line); + +#if LOG_USB +struct usb_log { + uint8_t ep_num; + uint8_t size; + uint8_t data[66]; +}; +__attribute__((used)) +struct usb_log usb_log[128]; +__attribute__((used)) +uint8_t usb_log_offset; + +struct xfer_log { + uint8_t ep_num; + uint16_t size; +}; +__attribute__((used)) +struct xfer_log xfer_log[64]; +__attribute__((used)) +uint8_t xfer_log_offset; + +__attribute__((used)) +struct xfer_log queue_log[64]; +__attribute__((used)) +uint8_t queue_log_offset; +#endif + +//--------------------------------------------------------------------+ +// SIE Command +//--------------------------------------------------------------------+ + +#define EP_SIZE 64 + +uint16_t volatile rx_buffer_offset[16]; +uint8_t volatile * rx_buffer[16]; +uint16_t volatile rx_buffer_max[16]; + +volatile uint8_t tx_ep; +volatile bool tx_active; +volatile uint16_t tx_buffer_offset[16]; +uint8_t volatile * tx_buffer[16]; +volatile uint16_t tx_buffer_max[16]; +volatile uint8_t reset_count; + +#if DEBUG +__attribute__((used)) uint8_t volatile * last_tx_buffer; +__attribute__((used)) volatile uint8_t last_tx_ep; +uint8_t setup_packet_bfr[10]; +#endif + +//--------------------------------------------------------------------+ +// PIPE HELPER +//--------------------------------------------------------------------+ + +static bool advance_tx_ep(void) { + // Move on to the next transmit buffer in a round-robin manner + uint8_t prev_tx_ep = tx_ep; + for (tx_ep = (tx_ep + 1) & 0xf; tx_ep != prev_tx_ep; tx_ep = ((tx_ep + 1) & 0xf)) { + if (tx_buffer[tx_ep]) + return true; + } + if (!tx_buffer[tx_ep]) + return false; + return true; +} + +#if LOG_USB +void xfer_log_append(uint8_t ep_num, uint16_t sz) { + xfer_log[xfer_log_offset].ep_num = ep_num; + xfer_log[xfer_log_offset].size = sz; + xfer_log_offset++; + if (xfer_log_offset >= sizeof(xfer_log)/sizeof(*xfer_log)) + xfer_log_offset = 0; +} + +void queue_log_append(uint8_t ep_num, uint16_t sz) { + queue_log[queue_log_offset].ep_num = ep_num; + queue_log[queue_log_offset].size = sz; + queue_log_offset++; + if (queue_log_offset >= sizeof(queue_log)/sizeof(*queue_log)) + queue_log_offset = 0; +} +#endif + +static void tx_more_data(void) { + // Send more data + uint8_t added_bytes; + for (added_bytes = 0; (added_bytes < EP_SIZE) && (tx_buffer_offset[tx_ep] < tx_buffer_max[tx_ep]); added_bytes++) { +#if LOG_USB + usb_log[usb_log_offset].data[added_bytes] = tx_buffer[tx_ep][tx_buffer_offset[tx_ep]]; +#endif + usb_in_data_write(tx_buffer[tx_ep][tx_buffer_offset[tx_ep]++]); + } + +#if LOG_USB + usb_log[usb_log_offset].ep_num = tu_edpt_addr(tx_ep, TUSB_DIR_IN); + usb_log[usb_log_offset].size = added_bytes; + usb_log_offset++; + if (usb_log_offset >= sizeof(usb_log)/sizeof(*usb_log)) + usb_log_offset = 0; +#endif + + // Updating the epno queues the data + usb_in_ctrl_write(tx_ep & 0xf); +} + +static void process_tx(void) { +#if DEBUG + // If the system isn't idle, then something is very wrong. + uint8_t in_status = usb_in_status_read(); + if (!(in_status & (1 << CSR_USB_IN_STATUS_IDLE_OFFSET))) + fomu_error(__LINE__); +#endif + + // If the buffer is now empty, search for the next buffer to fill. + if (!tx_buffer[tx_ep]) { + if (advance_tx_ep()) + tx_more_data(); + else + tx_active = false; + return; + } + + if (tx_buffer_offset[tx_ep] >= tx_buffer_max[tx_ep]) { +#if DEBUG + last_tx_buffer = tx_buffer[tx_ep]; + last_tx_ep = tx_ep; +#endif + tx_buffer[tx_ep] = NULL; + uint16_t xferred_bytes = tx_buffer_max[tx_ep]; + uint8_t xferred_ep = tx_ep; + + if (!advance_tx_ep()) + tx_active = false; +#if LOG_USB + xfer_log_append(tu_edpt_addr(xferred_ep, TUSB_DIR_IN), xferred_bytes); +#endif + dcd_event_xfer_complete(0, tu_edpt_addr(xferred_ep, TUSB_DIR_IN), xferred_bytes, XFER_RESULT_SUCCESS, true); + if (!tx_active) + return; + } + + tx_more_data(); + return; +} + +static void process_rx(void) { + uint8_t out_status = usb_out_status_read(); +#if DEBUG + // If the OUT handler is still waiting to send, don't do anything. + if (!(out_status & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET))) + fomu_error(__LINE__); + // return; +#endif + uint8_t rx_ep = (out_status >> CSR_USB_OUT_STATUS_EPNO_OFFSET) & 0xf; + + // If the destination buffer doesn't exist, don't drain the hardware + // fifo. Note that this can cause deadlocks if the host is waiting + // on some other endpoint's data! +#if DEBUG + if (rx_buffer[rx_ep] == NULL) { + fomu_error(__LINE__); + return; + } +#endif + + // Drain the FIFO into the destination buffer + uint32_t total_read = 0; + uint32_t current_offset = rx_buffer_offset[rx_ep]; +#if DEBUG + uint8_t test_buffer[256]; + memset(test_buffer, 0, sizeof(test_buffer)); + if (current_offset > rx_buffer_max[rx_ep]) + fomu_error(__LINE__); +#endif +#if LOG_USB + usb_log[usb_log_offset].ep_num = tu_edpt_addr(rx_ep, TUSB_DIR_OUT); + usb_log[usb_log_offset].size = 0; +#endif + while (usb_out_status_read() & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET)) { + uint8_t c = usb_out_data_read(); +#if DEBUG + test_buffer[total_read] = c; +#endif + total_read++; + if ((rx_buffer_offset[rx_ep] + current_offset) < rx_buffer_max[rx_ep]) { +#if LOG_USB + usb_log[usb_log_offset].data[usb_log[usb_log_offset].size++] = c; +#endif + if (rx_buffer[rx_ep] != (volatile uint8_t *)0xffffffff) + rx_buffer[rx_ep][current_offset++] = c; + } + } +#if LOG_USB + usb_log_offset++; + if (usb_log_offset >= sizeof(usb_log)/sizeof(*usb_log)) + usb_log_offset = 0; +#endif +#if DEBUG + if (total_read > 66) + fomu_error(__LINE__); + if (total_read < 2) + total_read = 2; + // fomu_error(__LINE__); +#endif + + // Strip off the CRC16 + rx_buffer_offset[rx_ep] += (total_read - 2); + if (rx_buffer_offset[rx_ep] > rx_buffer_max[rx_ep]) + rx_buffer_offset[rx_ep] = rx_buffer_max[rx_ep]; + + // If there's no more data, complete the transfer to tinyusb + if ((rx_buffer_max[rx_ep] == rx_buffer_offset[rx_ep]) + // ZLP with less than the total amount of data + || ((total_read == 2) && ((rx_buffer_offset[rx_ep] & 63) == 0)) + // Short read, but not a full packet + || (((rx_buffer_offset[rx_ep] & 63) != 0) && (total_read < 66))) { +#if DEBUG + if (rx_buffer[rx_ep] == NULL) + fomu_error(__LINE__); +#endif + + // Free up this buffer. + rx_buffer[rx_ep] = NULL; + uint16_t len = rx_buffer_offset[rx_ep]; + +#if DEBUG + // Validate that all enabled endpoints have buffers, + // and no disabled endpoints have buffers. + uint16_t ep_en_mask = usb_out_enable_status_read(); + int i; + for (i = 0; i < 16; i++) { + if ((!!(ep_en_mask & (1 << i))) ^ (!!(rx_buffer[i]))) { + uint8_t new_status = usb_out_status_read(); + // Another IRQ came in while we were processing, so ignore this endpoint. + if ((new_status & 0x20) && ((new_status & 0xf) == i)) + continue; + fomu_error(__LINE__); + } + } +#endif +#if LOG_USB + xfer_log_append(tu_edpt_addr(rx_ep, TUSB_DIR_OUT), len); +#endif + dcd_event_xfer_complete(0, tu_edpt_addr(rx_ep, TUSB_DIR_OUT), len, XFER_RESULT_SUCCESS, true); + } + else { + // If there's more data, re-enable data reception on this endpoint + usb_out_ctrl_write((1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | rx_ep); + } + + // Now that the buffer is drained, clear the pending IRQ. + usb_out_ev_pending_write(usb_out_ev_pending_read()); +} + +//--------------------------------------------------------------------+ +// CONTROLLER API +//--------------------------------------------------------------------+ + +static void dcd_reset(void) +{ + reset_count++; + usb_setup_ev_enable_write(0); + usb_in_ev_enable_write(0); + usb_out_ev_enable_write(0); + + usb_address_write(0); + + // Reset all three FIFO handlers + usb_setup_ctrl_write(1 << CSR_USB_SETUP_CTRL_RESET_OFFSET); + usb_in_ctrl_write(1 << CSR_USB_IN_CTRL_RESET_OFFSET); + usb_out_ctrl_write(1 << CSR_USB_OUT_CTRL_RESET_OFFSET); + + memset((void *)rx_buffer, 0, sizeof(rx_buffer)); + memset((void *)rx_buffer_max, 0, sizeof(rx_buffer_max)); + memset((void *)rx_buffer_offset, 0, sizeof(rx_buffer_offset)); + + memset((void *)tx_buffer, 0, sizeof(tx_buffer)); + memset((void *)tx_buffer_max, 0, sizeof(tx_buffer_max)); + memset((void *)tx_buffer_offset, 0, sizeof(tx_buffer_offset)); + tx_ep = 0; + tx_active = false; + + // Enable all event handlers and clear their contents + usb_setup_ev_pending_write(0xff); + usb_in_ev_pending_write(0xff); + usb_out_ev_pending_write(0xff); + usb_in_ev_enable_write(1); + usb_out_ev_enable_write(1); + usb_setup_ev_enable_write(3); + + dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true); +} + +// Initializes the USB peripheral for device mode and enables it. +void dcd_init(uint8_t rhport) +{ + (void) rhport; + + usb_pullup_out_write(0); + + // Enable all event handlers and clear their contents + usb_setup_ev_pending_write(usb_setup_ev_pending_read()); + usb_in_ev_pending_write(usb_in_ev_pending_read()); + usb_out_ev_pending_write(usb_out_ev_pending_read()); + usb_in_ev_enable_write(1); + usb_out_ev_enable_write(1); + usb_setup_ev_enable_write(3); + + // Turn on the external pullup + usb_pullup_out_write(1); +} + +// Enables or disables the USB device interrupt(s). May be used to +// prevent concurrency issues when mutating data structures shared +// between main code and the interrupt handler. +void dcd_int_enable(uint8_t rhport) +{ + (void) rhport; + irq_setmask(irq_getmask() | (1 << USB_INTERRUPT)); +} + +void dcd_int_disable(uint8_t rhport) +{ + (void) rhport; + irq_setmask(irq_getmask() & ~(1 << USB_INTERRUPT)); +} + +// Called when the device is given a new bus address. +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) +{ + // Respond with ACK status first before changing device address + dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); + + // Wait for the response packet to get sent + while (tx_active) + ; + + // Activate the new address + usb_address_write(dev_addr); +} + +// Called when the device received SET_CONFIG request, you can leave this +// empty if your peripheral does not require any specific action. +void dcd_set_config(uint8_t rhport, uint8_t config_num) +{ + (void) rhport; + (void) config_num; +} + +// Called to remote wake up host when suspended (e.g hid keyboard) +void dcd_remote_wakeup(uint8_t rhport) +{ + (void) rhport; +} + +//--------------------------------------------------------------------+ +// DCD Endpoint Port +//--------------------------------------------------------------------+ +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) +{ + (void) rhport; + uint8_t ep_num = tu_edpt_number(p_endpoint_desc->bEndpointAddress); + uint8_t ep_dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); + + if (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) + return false; // Not supported + + if (ep_dir == TUSB_DIR_OUT) { + rx_buffer_offset[ep_num] = 0; + rx_buffer_max[ep_num] = 0; + rx_buffer[ep_num] = NULL; + } + + else if (ep_dir == TUSB_DIR_OUT) { + tx_buffer_offset[ep_num] = 0; + tx_buffer_max[ep_num] = 0; + tx_buffer[ep_num] = NULL; + } + + return true; +} + +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + + if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) { + uint8_t enable = 0; + if (rx_buffer[ep_addr]) + enable = 1; + usb_out_ctrl_write((1 << CSR_USB_OUT_CTRL_STALL_OFFSET) | (enable << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | tu_edpt_number(ep_addr)); + } + else + usb_in_ctrl_write((1 << CSR_USB_IN_CTRL_STALL_OFFSET) | tu_edpt_number(ep_addr)); +} + +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) { + uint8_t enable = 0; + if (rx_buffer[ep_addr]) + enable = 1; + usb_out_ctrl_write((0 << CSR_USB_OUT_CTRL_STALL_OFFSET) | (enable << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | tu_edpt_number(ep_addr)); + } + // IN endpoints will get unstalled when more data is written. +} + +bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) +{ + (void)rhport; + uint8_t ep_num = tu_edpt_number(ep_addr); + uint8_t ep_dir = tu_edpt_dir(ep_addr); + TU_ASSERT(ep_num < 16); + + // Give a nonzero buffer when we transmit 0 bytes, so that the + // system doesn't think the endpoint is idle. + if ((buffer == NULL) && (total_bytes == 0)) { + buffer = (uint8_t *)0xffffffff; + } + + TU_ASSERT(buffer != NULL); + + if (ep_dir == TUSB_DIR_IN) { + // Wait for the tx pipe to free up + uint8_t previous_reset_count = reset_count; + // Continue until the buffer is empty, the system is idle, and the fifo is empty. + while (tx_buffer[ep_num] != NULL) + ; + + dcd_int_disable(0); +#if LOG_USB + queue_log_append(ep_addr, total_bytes); +#endif + // If a reset happens while we're waiting, abort the transfer + if (previous_reset_count != reset_count) + return true; + + TU_ASSERT(tx_buffer[ep_num] == NULL); + tx_buffer_offset[ep_num] = 0; + tx_buffer_max[ep_num] = total_bytes; + tx_buffer[ep_num] = buffer; + + // If the current buffer is NULL, then that means the tx logic is idle. + // Update the tx_ep to point to our endpoint number and queue the data. + // Otherwise, let it be and it'll get picked up after the next transfer + // finishes. + if (!tx_active) { + tx_ep = ep_num; + tx_active = true; + tx_more_data(); + } + dcd_int_enable(0); + } + + else if (ep_dir == TUSB_DIR_OUT) { + while (rx_buffer[ep_num] != NULL) + ; + + TU_ASSERT(rx_buffer[ep_num] == NULL); + dcd_int_disable(0); +#if LOG_USB + queue_log_append(ep_addr, total_bytes); +#endif + rx_buffer[ep_num] = buffer; + rx_buffer_offset[ep_num] = 0; + rx_buffer_max[ep_num] = total_bytes; + + // Enable receiving on this particular endpoint + usb_out_ctrl_write((1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | ep_num); +#if DEBUG + uint16_t ep_en_mask = usb_out_enable_status_read(); + int i; + for (i = 0; i < 16; i++) { + if ((!!(ep_en_mask & (1 << i))) ^ (!!(rx_buffer[i]))) { + if (rx_buffer[i] && usb_out_ev_pending_read() && (usb_out_status_read() & 0xf) == i) + continue; + fomu_error(__LINE__); + } + } +#endif + dcd_int_enable(0); + } + return true; +} + +//--------------------------------------------------------------------+ +// ISR +//--------------------------------------------------------------------+ + +static void handle_out(void) +{ + // An "OUT" transaction just completed so we have new data. + // (But only if we can accept the data) +#if DEBUG + if (!usb_out_ev_pending_read()) + fomu_error(__LINE__); + if (!usb_out_ev_enable_read()) + fomu_error(__LINE__); +#endif + process_rx(); +} + +static void handle_in(void) +{ +#if DEBUG + if (!usb_in_ev_pending_read()) + fomu_error(__LINE__); + if (!usb_in_ev_enable_read()) + fomu_error(__LINE__); +#endif + usb_in_ev_pending_write(usb_in_ev_pending_read()); + process_tx(); +} + +static void handle_reset(void) +{ +#if DEBUG + uint8_t setup_pending = usb_setup_ev_pending_read() & usb_setup_ev_enable_read(); + if (!(setup_pending & 2)) + fomu_error(__LINE__); +#endif + usb_setup_ev_pending_write(2); + + // This event means a bus reset occurred. Reset everything, and + // abandon any further processing. + dcd_reset(); +} + +static void handle_setup(void) +{ +#if !DEBUG + uint8_t setup_packet_bfr[10]; +#endif + +#if DEBUG + uint8_t setup_pending = usb_setup_ev_pending_read() & usb_setup_ev_enable_read(); + if (!(setup_pending & 1)) + fomu_error(__LINE__); +#endif + + // We got a SETUP packet. Copy it to the setup buffer and clear + // the "pending" bit. + // Setup packets are always 8 bytes, plus two bytes of crc16. + uint32_t setup_length = 0; + +#if DEBUG + if (!(usb_setup_status_read() & (1 << CSR_USB_SETUP_STATUS_HAVE_OFFSET))) + fomu_error(__LINE__); +#endif + + while (usb_setup_status_read() & (1 << CSR_USB_SETUP_STATUS_HAVE_OFFSET)) { + uint8_t c = usb_setup_data_read(); + if (setup_length < sizeof(setup_packet_bfr)) + setup_packet_bfr[setup_length] = c; + setup_length++; + } + + // If we have 10 bytes, that's a full SETUP packet plus CRC16. + // Otherwise, it was an RX error. + if (setup_length == 10) { + dcd_event_setup_received(0, setup_packet_bfr, true); + } +#if DEBUG + else { + fomu_error(__LINE__); + } +#endif + + usb_setup_ev_pending_write(1); +} +void hal_dcd_isr(uint8_t rhport) +{ + (void)rhport; + uint8_t next_ev; + while ((next_ev = usb_next_ev_read())) { + switch (next_ev) { + case 1 << CSR_USB_NEXT_EV_IN_OFFSET: + handle_in(); + break; + case 1 << CSR_USB_NEXT_EV_OUT_OFFSET: + handle_out(); + break; + case 1 << CSR_USB_NEXT_EV_SETUP_OFFSET: + handle_setup(); + break; + case 1 << CSR_USB_NEXT_EV_RESET_OFFSET: + handle_reset(); + break; + } + } +} + +#endif diff --git a/src/portable/valentyusb/eptri/dcd_eptri.h b/src/portable/valentyusb/eptri/dcd_eptri.h new file mode 100644 index 000000000..0fa6ecc64 --- /dev/null +++ b/src/portable/valentyusb/eptri/dcd_eptri.h @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#ifndef _TUSB_DCD_VALENTYUSB_EPTRI_H_ +#define _TUSB_DCD_VALENTYUSB_EPTRI_H_ + +#include "common/tusb_common.h" +#ifdef __cplusplus + extern "C" { +#endif + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_DCD_VALENTYUSB_EPTRI_H_ */ diff --git a/src/portable/valentyusb/eptri/hal_eptri.c b/src/portable/valentyusb/eptri/hal_eptri.c new file mode 100644 index 000000000..72453affa --- /dev/null +++ b/src/portable/valentyusb/eptri/hal_eptri.c @@ -0,0 +1,33 @@ +/* + * 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 "common/tusb_common.h" + +#if (CFG_TUSB_MCU == OPT_MCU_VALENTYUSB_EPTRI) + +// No HAL-specific stuff here! + +#endif -- cgit v1.3.1 From a9282eab51e7342cb464fcf995d8612ee606357c Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Wed, 20 Nov 2019 17:45:21 +0800 Subject: eptri: clear proper endpoint when opening IN port When opening a USB port, we ensure the buffer is NULL and has a length of 0. Due to a mistake in specifying the endpoint type, we never actually cleared the value when opening an IN endpoint. This patch fixes the comparison when opening an IN endpoint. This fixes issue #218. Signed-off-by: Sean Cross --- src/portable/valentyusb/eptri/dcd_eptri.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/portable') diff --git a/src/portable/valentyusb/eptri/dcd_eptri.c b/src/portable/valentyusb/eptri/dcd_eptri.c index d7e1b8a9a..e555fb0b1 100644 --- a/src/portable/valentyusb/eptri/dcd_eptri.c +++ b/src/portable/valentyusb/eptri/dcd_eptri.c @@ -415,7 +415,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) rx_buffer[ep_num] = NULL; } - else if (ep_dir == TUSB_DIR_OUT) { + else if (ep_dir == TUSB_DIR_IN) { tx_buffer_offset[ep_num] = 0; tx_buffer_max[ep_num] = 0; tx_buffer[ep_num] = NULL; -- cgit v1.3.1 From 043697ab9589955f831f4f716bb37b5470ba11f6 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 21 Nov 2019 16:08:08 +0700 Subject: rename lpc 17/18 irq to dcd_isr/hcd_isr --- hw/bsp/mimxrt1064_evk/mimxrt1064_evk.c | 68 ++++++++++---------------------- src/host/ehci/ehci.c | 2 +- src/host/ohci/ohci.c | 2 +- src/portable/nxp/lpc17_40/dcd_lpc17_40.c | 2 +- src/portable/nxp/lpc17_40/hal_lpc17_40.c | 8 ++-- src/portable/nxp/lpc18_43/dcd_lpc18_43.c | 10 +++-- src/portable/nxp/lpc18_43/hal_lpc18_43.c | 22 +++++++---- 7 files changed, 50 insertions(+), 64 deletions(-) (limited to 'src/portable') diff --git a/hw/bsp/mimxrt1064_evk/mimxrt1064_evk.c b/hw/bsp/mimxrt1064_evk/mimxrt1064_evk.c index 1673c01c9..62117e64e 100644 --- a/hw/bsp/mimxrt1064_evk/mimxrt1064_evk.c +++ b/hw/bsp/mimxrt1064_evk/mimxrt1064_evk.c @@ -94,54 +94,28 @@ void board_init(void) uart_config.enableRx = true; LPUART_Init(UART_PORT, &uart_config, (CLOCK_GetPllFreq(kCLOCK_PllUsb1) / 6U) / (CLOCK_GetDiv(kCLOCK_UartDiv) + 1U)); -#if 0 - // USB VBUS - const uint32_t port0_pin22_config = ( - IOCON_PIO_FUNC7 | /* Pin is configured as USB0_VBUS */ - IOCON_PIO_MODE_INACT | /* No addition pin function */ - IOCON_PIO_SLEW_STANDARD | /* Standard mode, output slew rate control is enabled */ - IOCON_PIO_INV_DI | /* Input function is not inverted */ - IOCON_PIO_DIGITAL_EN | /* Enables digital function */ - IOCON_PIO_OPENDRAIN_DI /* Open drain is disabled */ - ); - /* PORT0 PIN22 (coords: 78) is configured as USB0_VBUS */ - IOCON_PinMuxSet(IOCON, 0U, 22U, port0_pin22_config); - - // USB Controller - POWER_DisablePD(kPDRUNCFG_PD_USB0_PHY); /*Turn on USB0 Phy */ - POWER_DisablePD(kPDRUNCFG_PD_USB1_PHY); /*< Turn on USB1 Phy */ - - /* reset the IP to make sure it's in reset state. */ - RESET_PeripheralReset(kUSB0D_RST_SHIFT_RSTn); - RESET_PeripheralReset(kUSB0HSL_RST_SHIFT_RSTn); - RESET_PeripheralReset(kUSB0HMR_RST_SHIFT_RSTn); - RESET_PeripheralReset(kUSB1H_RST_SHIFT_RSTn); - RESET_PeripheralReset(kUSB1D_RST_SHIFT_RSTn); - RESET_PeripheralReset(kUSB1_RST_SHIFT_RSTn); - RESET_PeripheralReset(kUSB1RAM_RST_SHIFT_RSTn); - -#if (defined USB_DEVICE_CONFIG_LPCIP3511HS) && (USB_DEVICE_CONFIG_LPCIP3511HS) - CLOCK_EnableClock(kCLOCK_Usbh1); - /* Put PHY powerdown under software control */ - *((uint32_t *)(USBHSH_BASE + 0x50)) = USBHSH_PORTMODE_SW_PDCOM_MASK; - /* According to reference mannual, device mode setting has to be set by access usb host register */ - *((uint32_t *)(USBHSH_BASE + 0x50)) |= USBHSH_PORTMODE_DEV_ENABLE_MASK; - /* enable usb1 host clock */ - CLOCK_DisableClock(kCLOCK_Usbh1); -#endif + //------------- USB0 -------------// + // Clock + CLOCK_EnableUsbhs0PhyPllClock(kCLOCK_Usbphy480M, 480000000U); + CLOCK_EnableUsbhs0Clock(kCLOCK_Usb480M, 480000000U); -#if 1 || (defined USB_DEVICE_CONFIG_LPCIP3511FS) && (USB_DEVICE_CONFIG_LPCIP3511FS) - CLOCK_SetClkDiv(kCLOCK_DivUsb0Clk, 1, false); - CLOCK_AttachClk(kFRO_HF_to_USB0_CLK); - /* enable usb0 host clock */ - CLOCK_EnableClock(kCLOCK_Usbhsl0); - /*According to reference mannual, device mode setting has to be set by access usb host register */ - *((uint32_t *)(USBFSH_BASE + 0x5C)) |= USBFSH_PORTMODE_DEV_ENABLE_MASK; - /* disable usb0 host clock */ - CLOCK_DisableClock(kCLOCK_Usbhsl0); - CLOCK_EnableUsbfs0DeviceClock(kCLOCK_UsbfsSrcFro, CLOCK_GetFreq(kCLOCK_FroHf)); /* enable USB Device clock */ -#endif -#endif + USBPHY_Type* usb_phy = USBPHY1; + + // Enable PHY support for Low speed device + LS via FS Hub + usb_phy->CTRL |= USBPHY_CTRL_SET_ENUTMILEVEL2_MASK | USBPHY_CTRL_SET_ENUTMILEVEL3_MASK; + + // Enable all power for normal operation + usb_phy->PWD = 0; + + // TX Timing + uint32_t phytx = usb_phy->TX; + phytx &= ~(USBPHY_TX_D_CAL_MASK | USBPHY_TX_TXCAL45DM_MASK | USBPHY_TX_TXCAL45DP_MASK); + phytx |= USBPHY_TX_D_CAL(0x0C) | USBPHY_TX_TXCAL45DP(0x06) | USBPHY_TX_TXCAL45DM(0x06); + usb_phy->TX = phytx; + + // USB1 +// CLOCK_EnableUsbhs1PhyPllClock(kCLOCK_Usbphy480M, 480000000U); +// CLOCK_EnableUsbhs1Clock(kCLOCK_Usb480M, 480000000U); } //--------------------------------------------------------------------+ diff --git a/src/host/ehci/ehci.c b/src/host/ehci/ehci.c index 63e87dd54..04ee5c5ce 100644 --- a/src/host/ehci/ehci.c +++ b/src/host/ehci/ehci.c @@ -623,7 +623,7 @@ static void xfer_error_isr(uint8_t hostid) } //------------- Host Controller Driver's Interrupt Handler -------------// -void hal_hcd_isr(uint8_t rhport) +void hcd_isr(uint8_t rhport) { ehci_registers_t* regs = ehci_data.regs; diff --git a/src/host/ohci/ohci.c b/src/host/ohci/ohci.c index ea553baa2..9fa861ac9 100644 --- a/src/host/ohci/ohci.c +++ b/src/host/ohci/ohci.c @@ -599,7 +599,7 @@ static void done_queue_isr(uint8_t hostid) } } -void hal_hcd_isr(uint8_t hostid) +void hcd_isr(uint8_t hostid) { uint32_t const int_en = OHCI_REG->interrupt_enable; uint32_t const int_status = OHCI_REG->interrupt_status & int_en; diff --git a/src/portable/nxp/lpc17_40/dcd_lpc17_40.c b/src/portable/nxp/lpc17_40/dcd_lpc17_40.c index 8c439616a..89a4ba3f8 100644 --- a/src/portable/nxp/lpc17_40/dcd_lpc17_40.c +++ b/src/portable/nxp/lpc17_40/dcd_lpc17_40.c @@ -495,7 +495,7 @@ static void dd_complete_isr(uint8_t rhport, uint8_t ep_id) } // main USB IRQ handler -void hal_dcd_isr(uint8_t rhport) +void dcd_isr(uint8_t rhport) { uint32_t const dev_int_status = LPC_USB->DevIntSt & LPC_USB->DevIntEn; LPC_USB->DevIntClr = dev_int_status;// Acknowledge handled interrupt diff --git a/src/portable/nxp/lpc17_40/hal_lpc17_40.c b/src/portable/nxp/lpc17_40/hal_lpc17_40.c index e83e3e9a4..bc2477a90 100644 --- a/src/portable/nxp/lpc17_40/hal_lpc17_40.c +++ b/src/portable/nxp/lpc17_40/hal_lpc17_40.c @@ -30,17 +30,17 @@ #include "chip.h" -extern void hal_hcd_isr(uint8_t hostid); -extern void hal_dcd_isr(uint8_t rhport); +extern void hcd_isr(uint8_t hostid); +extern void dcd_isr(uint8_t rhport); void USB_IRQHandler(void) { #if TUSB_OPT_HOST_ENABLED - hal_hcd_isr(0); + hcd_isr(0); #endif #if TUSB_OPT_DEVICE_ENABLED - hal_dcd_isr(0); + dcd_isr(0); #endif } diff --git a/src/portable/nxp/lpc18_43/dcd_lpc18_43.c b/src/portable/nxp/lpc18_43/dcd_lpc18_43.c index 3619a7c2b..30cab820f 100644 --- a/src/portable/nxp/lpc18_43/dcd_lpc18_43.c +++ b/src/portable/nxp/lpc18_43/dcd_lpc18_43.c @@ -26,7 +26,7 @@ #include "tusb_option.h" -#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX) +#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_RT10XX) //--------------------------------------------------------------------+ // INCLUDE @@ -35,7 +35,11 @@ #include "device/dcd.h" #include "dcd_lpc18_43.h" -#include "chip.h" +#if CFG_TUSB_MCU == OPT_MCU_RT10XX + #include "fsl_device_registers.h" +#else + #include "chip.h" +#endif //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF @@ -270,7 +274,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t //--------------------------------------------------------------------+ // ISR //--------------------------------------------------------------------+ -void hal_dcd_isr(uint8_t rhport) +void dcd_isr(uint8_t rhport) { LPC_USBHS_T* const lpc_usb = LPC_USB[rhport]; diff --git a/src/portable/nxp/lpc18_43/hal_lpc18_43.c b/src/portable/nxp/lpc18_43/hal_lpc18_43.c index 3f4e9c23d..5710b9ffd 100644 --- a/src/portable/nxp/lpc18_43/hal_lpc18_43.c +++ b/src/portable/nxp/lpc18_43/hal_lpc18_43.c @@ -26,35 +26,43 @@ #include "tusb.h" -#if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX) +#if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_RT10XX) #include "chip.h" -extern void hal_dcd_isr(uint8_t rhport); -extern void hal_hcd_isr(uint8_t hostid); +extern void dcd_isr(uint8_t rhport); +extern void hcd_isr(uint8_t hostid); #if CFG_TUSB_RHPORT0_MODE +#if CFG_TUSB_MCU == OPT_MCU_RT10XX +void USB_OTG1_IRQHandler(void) +#else void USB0_IRQHandler(void) +#endif { #if TUSB_OPT_HOST_ENABLED - hal_hcd_isr(0); + hcd_isr(0); #endif #if TUSB_OPT_DEVICE_ENABLED - hal_dcd_isr(0); + dcd_isr(0); #endif } #endif #if CFG_TUSB_RHPORT1_MODE +#if CFG_TUSB_MCU == OPT_MCU_RT10XX +void USB_OTG2_IRQHandler(void) +#else void USB1_IRQHandler(void) +#endif { #if TUSB_OPT_HOST_ENABLED - hal_hcd_isr(1); + hcd_isr(1); #endif #if TUSB_OPT_DEVICE_ENABLED - hal_dcd_isr(1); + dcd_isr(1); #endif } #endif -- cgit v1.3.1 From f5d737aa7ec6abde5738d29510c8416d5e8b17e7 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 21 Nov 2019 23:19:38 +0700 Subject: moving irq to bsp for lpc17/40 --- hw/bsp/ea4088qs/board.mk | 3 -- hw/bsp/ea4088qs/ea4088qs.c | 15 +++++++ hw/bsp/ea4357/ea4357.c | 8 ++-- hw/bsp/lpcxpresso1769/board.mk | 3 -- hw/bsp/lpcxpresso1769/lpcxpresso1769.c | 14 +++++++ hw/bsp/mbed1768/board.mk | 9 ++-- hw/bsp/mbed1768/mbed1768.c | 14 +++++++ hw/bsp/mcb1800/mcb1800.c | 9 ++-- hw/bsp/mimxrt1064_evk/mimxrt1064_evk.c | 25 ++++++++++++ hw/bsp/ngx4330/ngx4330.c | 8 ++-- src/device/dcd.h | 3 ++ src/portable/nxp/lpc17_40/hal_lpc17_40.c | 61 ---------------------------- src/portable/nxp/lpc17_40/hcd_lpc17_40.c | 46 +++++++++++++++++++++ src/portable/nxp/lpc18_43/hal_lpc18_43.c | 70 -------------------------------- test/test/device/usbd/test_usbd.c | 2 +- 15 files changed, 134 insertions(+), 156 deletions(-) delete mode 100644 src/portable/nxp/lpc17_40/hal_lpc17_40.c create mode 100644 src/portable/nxp/lpc17_40/hcd_lpc17_40.c delete mode 100644 src/portable/nxp/lpc18_43/hal_lpc18_43.c (limited to 'src/portable') diff --git a/hw/bsp/ea4088qs/board.mk b/hw/bsp/ea4088qs/board.mk index 7869ea8d0..a88976d3a 100644 --- a/hw/bsp/ea4088qs/board.mk +++ b/hw/bsp/ea4088qs/board.mk @@ -17,9 +17,6 @@ MCU_DIR = hw/mcu/nxp/lpcopen/lpc40xx/lpc_chip_40xx # All source paths should be relative to the top level. LD_FILE = hw/bsp/ea4088qs/lpc4088.ld -# TODO remove later -SRC_C += src/portable/$(VENDOR)/$(CHIP_FAMILY)/hal_$(CHIP_FAMILY).c - SRC_C += \ $(MCU_DIR)/../gcc/cr_startup_lpc40xx.c \ $(MCU_DIR)/src/chip_17xx_40xx.c \ diff --git a/hw/bsp/ea4088qs/ea4088qs.c b/hw/bsp/ea4088qs/ea4088qs.c index 8adb61fda..83b2d6b1e 100644 --- a/hw/bsp/ea4088qs/ea4088qs.c +++ b/hw/bsp/ea4088qs/ea4088qs.c @@ -113,6 +113,21 @@ void board_init(void) LPC_USB->StCtrl = 0x3; } +//--------------------------------------------------------------------+ +// USB Interrupt Handler +//--------------------------------------------------------------------+ +void USB_IRQHandler(void) +{ + #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST + tuh_isr(0); + #endif + + #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE + tud_isr(0); + #endif +} + + //--------------------------------------------------------------------+ // Board porting API //--------------------------------------------------------------------+ diff --git a/hw/bsp/ea4357/ea4357.c b/hw/bsp/ea4357/ea4357.c index e8252f210..f21d3259d 100644 --- a/hw/bsp/ea4357/ea4357.c +++ b/hw/bsp/ea4357/ea4357.c @@ -235,22 +235,22 @@ void board_init(void) //--------------------------------------------------------------------+ void USB0_IRQHandler(void) { - #if TUSB_OPT_HOST_ENABLED + #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST tuh_isr(0); #endif - #if TUSB_OPT_DEVICE_ENABLED + #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE tud_isr(0); #endif } void USB1_IRQHandler(void) { - #if TUSB_OPT_HOST_ENABLED + #if CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST tuh_isr(1); #endif - #if TUSB_OPT_DEVICE_ENABLED + #if CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE tud_isr(1); #endif } diff --git a/hw/bsp/lpcxpresso1769/board.mk b/hw/bsp/lpcxpresso1769/board.mk index 8d1f14c42..b87d6afc9 100644 --- a/hw/bsp/lpcxpresso1769/board.mk +++ b/hw/bsp/lpcxpresso1769/board.mk @@ -17,9 +17,6 @@ MCU_DIR = hw/mcu/nxp/lpcopen/lpc175x_6x/lpc_chip_175x_6x # All source paths should be relative to the top level. LD_FILE = hw/bsp/lpcxpresso1769/lpc1769.ld -# TODO remove later -SRC_C += src/portable/$(VENDOR)/$(CHIP_FAMILY)/hal_$(CHIP_FAMILY).c - SRC_C += \ $(MCU_DIR)/../gcc/cr_startup_lpc175x_6x.c \ $(MCU_DIR)/src/chip_17xx_40xx.c \ diff --git a/hw/bsp/lpcxpresso1769/lpcxpresso1769.c b/hw/bsp/lpcxpresso1769/lpcxpresso1769.c index e2d5f6cf2..76755e89f 100644 --- a/hw/bsp/lpcxpresso1769/lpcxpresso1769.c +++ b/hw/bsp/lpcxpresso1769/lpcxpresso1769.c @@ -143,6 +143,20 @@ void board_init(void) #endif } +//--------------------------------------------------------------------+ +// USB Interrupt Handler +//--------------------------------------------------------------------+ +void USB_IRQHandler(void) +{ + #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST + tuh_isr(0); + #endif + + #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE + tud_isr(0); + #endif +} + //--------------------------------------------------------------------+ // Board porting API //--------------------------------------------------------------------+ diff --git a/hw/bsp/mbed1768/board.mk b/hw/bsp/mbed1768/board.mk index 03176b543..60428bcb4 100644 --- a/hw/bsp/mbed1768/board.mk +++ b/hw/bsp/mbed1768/board.mk @@ -17,9 +17,6 @@ MCU_DIR = hw/mcu/nxp/lpcopen/lpc175x_6x/lpc_chip_175x_6x # All source paths should be relative to the top level. LD_FILE = hw/bsp/mbed1768/lpc1768.ld -# TODO remove later -SRC_C += src/portable/$(VENDOR)/$(CHIP_FAMILY)/hal_$(CHIP_FAMILY).c - SRC_C += \ $(MCU_DIR)/../gcc/cr_startup_lpc175x_6x.c \ $(MCU_DIR)/src/chip_17xx_40xx.c \ @@ -44,5 +41,7 @@ FREERTOS_PORT = ARM_CM3 JLINK_DEVICE = LPC1768 JLINK_IF = swd -# flash using jlink -flash: flash-jlink +# flash using pyocd +flash: $(BUILD)/$(BOARD)-firmware.hex + pyocd flash -t lpc1768 $< + diff --git a/hw/bsp/mbed1768/mbed1768.c b/hw/bsp/mbed1768/mbed1768.c index 9f1ed12ef..66cf44a0e 100644 --- a/hw/bsp/mbed1768/mbed1768.c +++ b/hw/bsp/mbed1768/mbed1768.c @@ -135,6 +135,20 @@ void board_init(void) #endif } +//--------------------------------------------------------------------+ +// USB Interrupt Handler +//--------------------------------------------------------------------+ +void USB_IRQHandler(void) +{ + #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST + tuh_isr(0); + #endif + + #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE + tud_isr(0); + #endif +} + //--------------------------------------------------------------------+ // Board porting API //--------------------------------------------------------------------+ diff --git a/hw/bsp/mcb1800/mcb1800.c b/hw/bsp/mcb1800/mcb1800.c index 4e75c10dd..4fe874762 100644 --- a/hw/bsp/mcb1800/mcb1800.c +++ b/hw/bsp/mcb1800/mcb1800.c @@ -177,26 +177,25 @@ void board_init(void) //--------------------------------------------------------------------+ void USB0_IRQHandler(void) { - #if TUSB_OPT_HOST_ENABLED + #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST tuh_isr(0); #endif - #if TUSB_OPT_DEVICE_ENABLED + #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE tud_isr(0); #endif } void USB1_IRQHandler(void) { - #if TUSB_OPT_HOST_ENABLED + #if CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST tuh_isr(1); #endif - #if TUSB_OPT_DEVICE_ENABLED + #if CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE tud_isr(1); #endif } - //--------------------------------------------------------------------+ // Board porting API //--------------------------------------------------------------------+ diff --git a/hw/bsp/mimxrt1064_evk/mimxrt1064_evk.c b/hw/bsp/mimxrt1064_evk/mimxrt1064_evk.c index 62117e64e..8793a759c 100644 --- a/hw/bsp/mimxrt1064_evk/mimxrt1064_evk.c +++ b/hw/bsp/mimxrt1064_evk/mimxrt1064_evk.c @@ -118,6 +118,31 @@ void board_init(void) // CLOCK_EnableUsbhs1Clock(kCLOCK_Usb480M, 480000000U); } +//--------------------------------------------------------------------+ +// USB Interrupt Handler +//--------------------------------------------------------------------+ +void USB0_IRQHandler(void) +{ + #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST + tuh_isr(0); + #endif + + #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE + tud_isr(0); + #endif +} + +void USB1_IRQHandler(void) +{ + #if CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST + tuh_isr(1); + #endif + + #if CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE + tud_isr(1); + #endif +} + //--------------------------------------------------------------------+ // Board porting API //--------------------------------------------------------------------+ diff --git a/hw/bsp/ngx4330/ngx4330.c b/hw/bsp/ngx4330/ngx4330.c index e95f6b059..fd6f0119b 100644 --- a/hw/bsp/ngx4330/ngx4330.c +++ b/hw/bsp/ngx4330/ngx4330.c @@ -224,22 +224,22 @@ void board_init(void) //--------------------------------------------------------------------+ void USB0_IRQHandler(void) { - #if TUSB_OPT_HOST_ENABLED + #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST tuh_isr(0); #endif - #if TUSB_OPT_DEVICE_ENABLED + #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE tud_isr(0); #endif } void USB1_IRQHandler(void) { - #if TUSB_OPT_HOST_ENABLED + #if CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST tuh_isr(1); #endif - #if TUSB_OPT_DEVICE_ENABLED + #if CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE tud_isr(1); #endif } diff --git a/src/device/dcd.h b/src/device/dcd.h index 6e43ab3e9..dca289e78 100644 --- a/src/device/dcd.h +++ b/src/device/dcd.h @@ -88,6 +88,9 @@ typedef struct TU_ATTR_ALIGNED(4) // 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); diff --git a/src/portable/nxp/lpc17_40/hal_lpc17_40.c b/src/portable/nxp/lpc17_40/hal_lpc17_40.c deleted file mode 100644 index bc2477a90..000000000 --- a/src/portable/nxp/lpc17_40/hal_lpc17_40.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 "common/tusb_common.h" - -#if (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC40XX) - -#include "chip.h" - -extern void hcd_isr(uint8_t hostid); -extern void dcd_isr(uint8_t rhport); - -void USB_IRQHandler(void) -{ - #if TUSB_OPT_HOST_ENABLED - hcd_isr(0); - #endif - - #if TUSB_OPT_DEVICE_ENABLED - dcd_isr(0); - #endif -} - -//FIXME move later -void hcd_int_enable(uint8_t rhport) -{ - (void) rhport; - NVIC_EnableIRQ(USB_IRQn); -} - -void hcd_int_disable(uint8_t rhport) -{ - (void) rhport; - NVIC_DisableIRQ(USB_IRQn); -} - - -#endif diff --git a/src/portable/nxp/lpc17_40/hcd_lpc17_40.c b/src/portable/nxp/lpc17_40/hcd_lpc17_40.c new file mode 100644 index 000000000..537b3daba --- /dev/null +++ b/src/portable/nxp/lpc17_40/hcd_lpc17_40.c @@ -0,0 +1,46 @@ +/* + * 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 (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC40XX) + +#include "chip.h" + +void hcd_int_enable(uint8_t rhport) +{ + (void) rhport; + NVIC_EnableIRQ(USB_IRQn); +} + +void hcd_int_disable(uint8_t rhport) +{ + (void) rhport; + NVIC_DisableIRQ(USB_IRQn); +} + +#endif + diff --git a/src/portable/nxp/lpc18_43/hal_lpc18_43.c b/src/portable/nxp/lpc18_43/hal_lpc18_43.c deleted file mode 100644 index 5710b9ffd..000000000 --- a/src/portable/nxp/lpc18_43/hal_lpc18_43.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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.h" - -#if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_RT10XX) - -#include "chip.h" - -extern void dcd_isr(uint8_t rhport); -extern void hcd_isr(uint8_t hostid); - -#if CFG_TUSB_RHPORT0_MODE -#if CFG_TUSB_MCU == OPT_MCU_RT10XX -void USB_OTG1_IRQHandler(void) -#else -void USB0_IRQHandler(void) -#endif -{ - #if TUSB_OPT_HOST_ENABLED - hcd_isr(0); - #endif - - #if TUSB_OPT_DEVICE_ENABLED - dcd_isr(0); - #endif -} -#endif - -#if CFG_TUSB_RHPORT1_MODE -#if CFG_TUSB_MCU == OPT_MCU_RT10XX -void USB_OTG2_IRQHandler(void) -#else -void USB1_IRQHandler(void) -#endif -{ - #if TUSB_OPT_HOST_ENABLED - hcd_isr(1); - #endif - - #if TUSB_OPT_DEVICE_ENABLED - dcd_isr(1); - #endif -} -#endif - -#endif diff --git a/test/test/device/usbd/test_usbd.c b/test/test/device/usbd/test_usbd.c index fcd739b40..c59d94c2c 100644 --- a/test/test/device/usbd/test_usbd.c +++ b/test/test/device/usbd/test_usbd.c @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2019 hathach for Adafruit Industries + * 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 -- cgit v1.3.1 From 1f52273d99a340128d535d8c3d146b8522cad0ec Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 22 Nov 2019 00:38:22 +0700 Subject: move dcd_lpc18_43.h into .c file --- src/portable/nxp/lpc18_43/dcd_lpc18_43.c | 98 ++++++++++++++++++++- src/portable/nxp/lpc18_43/dcd_lpc18_43.h | 144 ------------------------------- 2 files changed, 97 insertions(+), 145 deletions(-) delete mode 100644 src/portable/nxp/lpc18_43/dcd_lpc18_43.h (limited to 'src/portable') diff --git a/src/portable/nxp/lpc18_43/dcd_lpc18_43.c b/src/portable/nxp/lpc18_43/dcd_lpc18_43.c index 30cab820f..75f9c8d5d 100644 --- a/src/portable/nxp/lpc18_43/dcd_lpc18_43.c +++ b/src/portable/nxp/lpc18_43/dcd_lpc18_43.c @@ -33,7 +33,6 @@ //--------------------------------------------------------------------+ #include "common/tusb_common.h" #include "device/dcd.h" -#include "dcd_lpc18_43.h" #if CFG_TUSB_MCU == OPT_MCU_RT10XX #include "fsl_device_registers.h" @@ -45,6 +44,103 @@ // MACRO CONSTANT TYPEDEF //--------------------------------------------------------------------+ +/*---------- ENDPTCTRL ----------*/ +enum { + ENDPTCTRL_MASK_STALL = TU_BIT(0), + ENDPTCTRL_MASK_TOGGLE_INHIBIT = TU_BIT(5), ///< used for test only + ENDPTCTRL_MASK_TOGGLE_RESET = TU_BIT(6), + ENDPTCTRL_MASK_ENABLE = TU_BIT(7) +}; + +/*---------- USBCMD ----------*/ +enum { + USBCMD_MASK_RUN_STOP = TU_BIT(0), + USBCMD_MASK_RESET = TU_BIT(1), + USBCMD_MASK_SETUP_TRIPWIRE = TU_BIT(13), + USBCMD_MASK_ADD_QTD_TRIPWIRE = TU_BIT(14) ///< This bit is used as a semaphore to ensure the to proper addition of a new dTD to an active (primed) endpoint’s linked list. This bit is set and cleared by software during the process of adding a new dTD +}; +// Interrupt Threshold bit 23:16 + +/*---------- USBSTS, USBINTR ----------*/ +enum { + INT_MASK_USB = TU_BIT(0), + INT_MASK_ERROR = TU_BIT(1), + INT_MASK_PORT_CHANGE = TU_BIT(2), + INT_MASK_RESET = TU_BIT(6), + INT_MASK_SOF = TU_BIT(7), + INT_MASK_SUSPEND = TU_BIT(8), + INT_MASK_NAK = TU_BIT(16) +}; + +//------------- PORTSC -------------// +enum { + PORTSC_CURRENT_CONNECT_STATUS_MASK = TU_BIT(0), + PORTSC_FORCE_PORT_RESUME_MASK = TU_BIT(6), + PORTSC_SUSPEND_MASK = TU_BIT(7) +}; + +typedef struct +{ + // Word 0: Next QTD Pointer + uint32_t next; ///< Next link pointer This field contains the physical memory address of the next dTD to be processed + + // Word 1: qTQ Token + uint32_t : 3 ; + volatile uint32_t xact_err : 1 ; + uint32_t : 1 ; + volatile uint32_t buffer_err : 1 ; + volatile uint32_t halted : 1 ; + volatile uint32_t active : 1 ; + uint32_t : 2 ; + uint32_t iso_mult_override : 2 ; ///< This field can be used for transmit ISOs to override the MULT field in the dQH. This field must be zero for all packet types that are not transmit-ISO. + uint32_t : 3 ; + uint32_t int_on_complete : 1 ; + volatile uint32_t total_bytes : 15 ; + uint32_t : 0 ; + + // Word 2-6: Buffer Page Pointer List, Each element in the list is a 4K page aligned, physical memory address. The lower 12 bits in each pointer are reserved (except for the first one) as each memory pointer must reference the start of a 4K page + uint32_t buffer[5]; ///< buffer1 has frame_n for TODO Isochronous + + //------------- DCD Area -------------// + uint16_t expected_bytes; + uint8_t reserved[2]; +} dcd_qtd_t; + +TU_VERIFY_STATIC( sizeof(dcd_qtd_t) == 32, "size is not correct"); + +typedef struct +{ + // Word 0: Capabilities and Characteristics + uint32_t : 15 ; ///< Number of packets executed per transaction descriptor 00 - Execute N transactions as demonstrated by the USB variable length protocol where N is computed using Max_packet_length and the Total_bytes field in the dTD. 01 - Execute one transaction 10 - Execute two transactions 11 - Execute three transactions Remark: Non-isochronous endpoints must set MULT = 00. Remark: Isochronous endpoints must set MULT = 01, 10, or 11 as needed. + uint32_t int_on_setup : 1 ; ///< Interrupt on setup This bit is used on control type endpoints to indicate if USBINT is set in response to a setup being received. + uint32_t max_package_size : 11 ; ///< This directly corresponds to the maximum packet size of the associated endpoint (wMaxPacketSize) + uint32_t : 2 ; + uint32_t zero_length_termination : 1 ; ///< This bit is used for non-isochronous endpoints to indicate when a zero-length packet is received to terminate transfers in case the total transfer length is “multiple”. 0 - Enable zero-length packet to terminate transfers equal to a multiple of Max_packet_length (default). 1 - Disable zero-length packet on transfers that are equal in length to a multiple Max_packet_length. + uint32_t iso_mult : 2 ; ///< + uint32_t : 0 ; + + // Word 1: Current qTD Pointer + volatile uint32_t qtd_addr; + + // Word 2-9: Transfer Overlay + volatile dcd_qtd_t qtd_overlay; + + // Word 10-11: Setup request (control OUT only) + volatile tusb_control_request_t setup_request; + + //--------------------------------------------------------------------+ + /// Due to the fact QHD is 64 bytes aligned but occupies only 48 bytes + /// thus there are 16 bytes padding free that we can make use of. + //--------------------------------------------------------------------+ + uint8_t reserved[16]; +} dcd_qhd_t; + +TU_VERIFY_STATIC( sizeof(dcd_qhd_t) == 64, "size is not correct"); + +//--------------------------------------------------------------------+ +// Variables +//--------------------------------------------------------------------+ + #define QHD_MAX 12 #define QTD_NEXT_INVALID 0x01 diff --git a/src/portable/nxp/lpc18_43/dcd_lpc18_43.h b/src/portable/nxp/lpc18_43/dcd_lpc18_43.h deleted file mode 100644 index 00f4854b1..000000000 --- a/src/portable/nxp/lpc18_43/dcd_lpc18_43.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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_dcd - * \defgroup group_dcd_lpc143xx LPC43xx - * @{ */ - -#ifndef _TUSB_DCD_LPC43XX_H_ -#define _TUSB_DCD_LPC43XX_H_ - -#include "common/tusb_common.h" - -#ifdef __cplusplus - extern "C" { -#endif - -//--------------------------------------------------------------------+ -// MACRO CONSTANT TYPEDEF -//--------------------------------------------------------------------+ - -/*---------- ENDPTCTRL ----------*/ -enum { - ENDPTCTRL_MASK_STALL = TU_BIT(0), - ENDPTCTRL_MASK_TOGGLE_INHIBIT = TU_BIT(5), ///< used for test only - ENDPTCTRL_MASK_TOGGLE_RESET = TU_BIT(6), - ENDPTCTRL_MASK_ENABLE = TU_BIT(7) -}; - -/*---------- USBCMD ----------*/ -enum { - USBCMD_MASK_RUN_STOP = TU_BIT(0), - USBCMD_MASK_RESET = TU_BIT(1), - USBCMD_MASK_SETUP_TRIPWIRE = TU_BIT(13), - USBCMD_MASK_ADD_QTD_TRIPWIRE = TU_BIT(14) ///< This bit is used as a semaphore to ensure the to proper addition of a new dTD to an active (primed) endpoint’s linked list. This bit is set and cleared by software during the process of adding a new dTD -}; -// Interrupt Threshold bit 23:16 - -/*---------- USBSTS, USBINTR ----------*/ -enum { - INT_MASK_USB = TU_BIT(0), - INT_MASK_ERROR = TU_BIT(1), - INT_MASK_PORT_CHANGE = TU_BIT(2), - INT_MASK_RESET = TU_BIT(6), - INT_MASK_SOF = TU_BIT(7), - INT_MASK_SUSPEND = TU_BIT(8), - INT_MASK_NAK = TU_BIT(16) -}; - -//------------- PORTSC -------------// -enum { - PORTSC_CURRENT_CONNECT_STATUS_MASK = TU_BIT(0), - PORTSC_FORCE_PORT_RESUME_MASK = TU_BIT(6), - PORTSC_SUSPEND_MASK = TU_BIT(7) -}; - -typedef struct -{ - // Word 0: Next QTD Pointer - uint32_t next; ///< Next link pointer This field contains the physical memory address of the next dTD to be processed - - // Word 1: qTQ Token - uint32_t : 3 ; - volatile uint32_t xact_err : 1 ; - uint32_t : 1 ; - volatile uint32_t buffer_err : 1 ; - volatile uint32_t halted : 1 ; - volatile uint32_t active : 1 ; - uint32_t : 2 ; - uint32_t iso_mult_override : 2 ; ///< This field can be used for transmit ISOs to override the MULT field in the dQH. This field must be zero for all packet types that are not transmit-ISO. - uint32_t : 3 ; - uint32_t int_on_complete : 1 ; - volatile uint32_t total_bytes : 15 ; - uint32_t : 0 ; - - // Word 2-6: Buffer Page Pointer List, Each element in the list is a 4K page aligned, physical memory address. The lower 12 bits in each pointer are reserved (except for the first one) as each memory pointer must reference the start of a 4K page - uint32_t buffer[5]; ///< buffer1 has frame_n for TODO Isochronous - - //------------- DCD Area -------------// - uint16_t expected_bytes; - uint8_t reserved[2]; -} dcd_qtd_t; - -TU_VERIFY_STATIC( sizeof(dcd_qtd_t) == 32, "size is not correct"); - -typedef struct -{ - // Word 0: Capabilities and Characteristics - uint32_t : 15 ; ///< Number of packets executed per transaction descriptor 00 - Execute N transactions as demonstrated by the USB variable length protocol where N is computed using Max_packet_length and the Total_bytes field in the dTD. 01 - Execute one transaction 10 - Execute two transactions 11 - Execute three transactions Remark: Non-isochronous endpoints must set MULT = 00. Remark: Isochronous endpoints must set MULT = 01, 10, or 11 as needed. - uint32_t int_on_setup : 1 ; ///< Interrupt on setup This bit is used on control type endpoints to indicate if USBINT is set in response to a setup being received. - uint32_t max_package_size : 11 ; ///< This directly corresponds to the maximum packet size of the associated endpoint (wMaxPacketSize) - uint32_t : 2 ; - uint32_t zero_length_termination : 1 ; ///< This bit is used for non-isochronous endpoints to indicate when a zero-length packet is received to terminate transfers in case the total transfer length is “multiple”. 0 - Enable zero-length packet to terminate transfers equal to a multiple of Max_packet_length (default). 1 - Disable zero-length packet on transfers that are equal in length to a multiple Max_packet_length. - uint32_t iso_mult : 2 ; ///< - uint32_t : 0 ; - - // Word 1: Current qTD Pointer - volatile uint32_t qtd_addr; - - // Word 2-9: Transfer Overlay - volatile dcd_qtd_t qtd_overlay; - - // Word 10-11: Setup request (control OUT only) - volatile tusb_control_request_t setup_request; - - //--------------------------------------------------------------------+ - /// Due to the fact QHD is 64 bytes aligned but occupies only 48 bytes - /// thus there are 16 bytes padding free that we can make use of. - //--------------------------------------------------------------------+ - uint8_t reserved[16]; -} dcd_qhd_t; - -TU_VERIFY_STATIC( sizeof(dcd_qhd_t) == 64, "size is not correct"); - - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_DCD_LPC43XX_H_ */ - -/** @} */ -- cgit v1.3.1 From 623b16af2eb9ead9d4267464eb8f178a037ad5fc Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 22 Nov 2019 00:58:18 +0700 Subject: clean up dcd lpc18_43 drop supporting both device mode on both ports. --- src/portable/nxp/lpc18_43/dcd_lpc18_43.c | 56 +++++++++----------------------- 1 file changed, 16 insertions(+), 40 deletions(-) (limited to 'src/portable') diff --git a/src/portable/nxp/lpc18_43/dcd_lpc18_43.c b/src/portable/nxp/lpc18_43/dcd_lpc18_43.c index 75f9c8d5d..7613f6711 100644 --- a/src/portable/nxp/lpc18_43/dcd_lpc18_43.c +++ b/src/portable/nxp/lpc18_43/dcd_lpc18_43.c @@ -26,7 +26,9 @@ #include "tusb_option.h" -#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_RT10XX) +#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC18XX || \ + CFG_TUSB_MCU == OPT_MCU_LPC43XX || \ + CFG_TUSB_MCU == OPT_MCU_RT10XX) //--------------------------------------------------------------------+ // INCLUDE @@ -150,31 +152,9 @@ typedef struct { dcd_qtd_t qtd[QHD_MAX] TU_ATTR_ALIGNED(32); }dcd_data_t; -#if (CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE) -CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(2048) static dcd_data_t dcd_data0; -#endif - -#if (CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE) -CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(2048) static dcd_data_t dcd_data1; -#endif - +static dcd_data_t _dcd_data CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(2048); static LPC_USBHS_T * const LPC_USB[2] = { LPC_USB0, LPC_USB1 }; -static dcd_data_t* const dcd_data_ptr[2] = -{ -#if (CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE) - &dcd_data0, -#else - NULL, -#endif - -#if (CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE) - &dcd_data1 -#else - NULL -#endif -}; - //--------------------------------------------------------------------+ // CONTROLLER API //--------------------------------------------------------------------+ @@ -210,25 +190,23 @@ static void bus_reset(uint8_t rhport) // read reset bit in portsc //------------- Queue Head & Queue TD -------------// - dcd_data_t* p_dcd = dcd_data_ptr[rhport]; - tu_memclr(p_dcd, sizeof(dcd_data_t)); + tu_memclr(&_dcd_data, sizeof(dcd_data_t)); //------------- Set up Control Endpoints (0 OUT, 1 IN) -------------// - p_dcd->qhd[0].zero_length_termination = p_dcd->qhd[1].zero_length_termination = 1; - p_dcd->qhd[0].max_package_size = p_dcd->qhd[1].max_package_size = CFG_TUD_ENDPOINT0_SIZE; - p_dcd->qhd[0].qtd_overlay.next = p_dcd->qhd[1].qtd_overlay.next = QTD_NEXT_INVALID; + _dcd_data.qhd[0].zero_length_termination = _dcd_data.qhd[1].zero_length_termination = 1; + _dcd_data.qhd[0].max_package_size = _dcd_data.qhd[1].max_package_size = CFG_TUD_ENDPOINT0_SIZE; + _dcd_data.qhd[0].qtd_overlay.next = _dcd_data.qhd[1].qtd_overlay.next = QTD_NEXT_INVALID; - p_dcd->qhd[0].int_on_setup = 1; // OUT only + _dcd_data.qhd[0].int_on_setup = 1; // OUT only } void dcd_init(uint8_t rhport) { LPC_USBHS_T* const lpc_usb = LPC_USB[rhport]; - dcd_data_t* p_dcd = dcd_data_ptr[rhport]; - tu_memclr(p_dcd, sizeof(dcd_data_t)); + tu_memclr(&_dcd_data, sizeof(dcd_data_t)); - lpc_usb->ENDPOINTLISTADDR = (uint32_t) p_dcd->qhd; // Endpoint List Address has to be 2K alignment + lpc_usb->ENDPOINTLISTADDR = (uint32_t) _dcd_data.qhd; // Endpoint List Address has to be 2K alignment lpc_usb->USBSTS_D = lpc_usb->USBSTS_D; lpc_usb->USBINTR_D = INT_MASK_USB | INT_MASK_ERROR | INT_MASK_PORT_CHANGE | INT_MASK_RESET | INT_MASK_SUSPEND | INT_MASK_SOF; @@ -327,7 +305,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) TU_ASSERT( epnum <= (rhport ? 3 : 5) ); //------------- Prepare Queue Head -------------// - dcd_qhd_t * p_qhd = &dcd_data_ptr[rhport]->qhd[ep_idx]; + dcd_qhd_t * p_qhd = &_dcd_data.qhd[ep_idx]; tu_memclr(p_qhd, sizeof(dcd_qhd_t)); p_qhd->zero_length_termination = 1; @@ -353,8 +331,8 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t while(LPC_USB[rhport]->ENDPTSETUPSTAT & TU_BIT(0)) {} } - dcd_qhd_t * p_qhd = &dcd_data_ptr[rhport]->qhd[ep_idx]; - dcd_qtd_t * p_qtd = &dcd_data_ptr[rhport]->qtd[ep_idx]; + dcd_qhd_t * p_qhd = &_dcd_data.qhd[ep_idx]; + dcd_qtd_t * p_qtd = &_dcd_data.qtd[ep_idx]; //------------- Prepare qtd -------------// qtd_init(p_qtd, buffer, total_bytes); @@ -414,15 +392,13 @@ void dcd_isr(uint8_t rhport) uint32_t const edpt_complete = lpc_usb->ENDPTCOMPLETE; lpc_usb->ENDPTCOMPLETE = edpt_complete; // acknowledge - dcd_data_t* const p_dcd = dcd_data_ptr[rhport]; - if (lpc_usb->ENDPTSETUPSTAT) { //------------- Set up Received -------------// // 23.10.10.2 Operational model for setup transfers lpc_usb->ENDPTSETUPSTAT = lpc_usb->ENDPTSETUPSTAT;// acknowledge - dcd_event_setup_received(rhport, (uint8_t*) &p_dcd->qhd[0].setup_request, true); + dcd_event_setup_received(rhport, (uint8_t*) &_dcd_data.qhd[0].setup_request, true); } if ( edpt_complete ) @@ -432,7 +408,7 @@ void dcd_isr(uint8_t rhport) if ( tu_bit_test(edpt_complete, ep_idx2bit(ep_idx)) ) { // 23.10.12.3 Failed QTD also get ENDPTCOMPLETE set - dcd_qtd_t * p_qtd = &dcd_data_ptr[rhport]->qtd[ep_idx]; + dcd_qtd_t * p_qtd = &_dcd_data.qtd[ep_idx]; uint8_t result = p_qtd->halted ? XFER_RESULT_STALLED : ( p_qtd->xact_err ||p_qtd->buffer_err ) ? XFER_RESULT_FAILED : XFER_RESULT_SUCCESS; -- cgit v1.3.1 From 8aacd1eacdc706e2fc54fa069b9ccf966aee6713 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 22 Nov 2019 12:11:13 +0700 Subject: refactor dcd_lpc18_43, making it capatible with rt10xx --- src/portable/nxp/lpc18_43/dcd_lpc18_43.c | 94 ++++++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 22 deletions(-) (limited to 'src/portable') diff --git a/src/portable/nxp/lpc18_43/dcd_lpc18_43.c b/src/portable/nxp/lpc18_43/dcd_lpc18_43.c index 7613f6711..75dd7b850 100644 --- a/src/portable/nxp/lpc18_43/dcd_lpc18_43.c +++ b/src/portable/nxp/lpc18_43/dcd_lpc18_43.c @@ -40,12 +40,61 @@ #include "fsl_device_registers.h" #else #include "chip.h" + + // Register base to CAPLENGTH + #define DCD_REGS_BASE { (dcd_registers_t*) (LPC_USB0_BASE + 0x100), (dcd_registers_t*) (LPC_USB1_BASE + 0x100) } #endif //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF //--------------------------------------------------------------------+ +// Device Register starting with CAPLENGTH offset +typedef struct +{ + //------------- Capability Registers-------------// + __I uint8_t CAPLENGTH; ///< Capability Registers Length + __I uint8_t TU_RESERVED[1]; + __I uint16_t HCIVERSION; ///< Host Controller Interface Version + + __I uint32_t HCSPARAMS; ///< Host Controller Structural Parameters + __I uint32_t HCCPARAMS; ///< Host Controller Capability Parameters + __I uint32_t TU_RESERVED[5]; + + __I uint16_t DCIVERSION; ///< Device Controller Interface Version + __I uint8_t TU_RESERVED[2]; + + __I uint32_t DCCPARAMS; ///< Device Controller Capability Parameters + __I uint32_t TU_RESERVED[6]; + + //------------- Operational Registers -------------// + __IO uint32_t USBCMD; ///< USB Command Register + __IO uint32_t USBSTS; ///< USB Status Register + __IO uint32_t USBINTR; ///< Interrupt Enable Register + __IO uint32_t FRINDEX; ///< USB Frame Index + __I uint32_t TU_RESERVED; + __IO uint32_t DEVICEADDR; ///< Device Address + __IO uint32_t ENDPTLISTADDR; ///< Endpoint List Address + __I uint32_t TU_RESERVED; + __IO uint32_t BURSTSIZE; ///< Programmable Burst Size + __IO uint32_t TXFILLTUNING; ///< TX FIFO Fill Tuning + uint32_t TU_RESERVED[4]; + __IO uint32_t ENDPTNAK; ///< Endpoint NAK + __IO uint32_t ENDPTNAKEN; ///< Endpoint NAK Enable + __I uint32_t TU_RESERVED; + __IO uint32_t PORTSC1; ///< Port Status & Control + __I uint32_t TU_RESERVED[7]; + __IO uint32_t OTGSC; ///< On-The-Go Status & control + __IO uint32_t USBMODE; ///< USB Device Mode + __IO uint32_t ENDPTSETUPSTAT; ///< Endpoint Setup Status + __IO uint32_t ENDPTPRIME; ///< Endpoint Prime + __IO uint32_t ENDPTFLUSH; ///< Endpoint Flush + __I uint32_t ENDPTSTAT; ///< Endpoint Status + __IO uint32_t ENDPTCOMPLETE; ///< Endpoint Complete + __IO uint32_t ENDPTCTRL[8]; ///< Endpoint Control 0 - 7 +} dcd_registers_t; + + /*---------- ENDPTCTRL ----------*/ enum { ENDPTCTRL_MASK_STALL = TU_BIT(0), @@ -153,7 +202,8 @@ typedef struct { }dcd_data_t; static dcd_data_t _dcd_data CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(2048); -static LPC_USBHS_T * const LPC_USB[2] = { LPC_USB0, LPC_USB1 }; +//static LPC_USBHS_T * const LPC_USB[2] = { LPC_USB0, LPC_USB1 }; +static dcd_registers_t* dcd_regs[] = DCD_REGS_BASE; //--------------------------------------------------------------------+ // CONTROLLER API @@ -162,7 +212,7 @@ static LPC_USBHS_T * const LPC_USB[2] = { LPC_USB0, LPC_USB1 }; /// follows LPC43xx User Manual 23.10.3 static void bus_reset(uint8_t rhport) { - LPC_USBHS_T* lpc_usb = LPC_USB[rhport]; + dcd_registers_t* lpc_usb = dcd_regs[rhport]; // The reset value for all endpoint types is the control endpoint. If one endpoint // direction is enabled and the paired endpoint of opposite direction is disabled, then the @@ -179,7 +229,7 @@ static void bus_reset(uint8_t rhport) //------------- Clear All Registers -------------// lpc_usb->ENDPTNAK = lpc_usb->ENDPTNAK; lpc_usb->ENDPTNAKEN = 0; - lpc_usb->USBSTS_D = lpc_usb->USBSTS_D; + lpc_usb->USBSTS = lpc_usb->USBSTS; lpc_usb->ENDPTSETUPSTAT = lpc_usb->ENDPTSETUPSTAT; lpc_usb->ENDPTCOMPLETE = lpc_usb->ENDPTCOMPLETE; @@ -202,16 +252,16 @@ static void bus_reset(uint8_t rhport) void dcd_init(uint8_t rhport) { - LPC_USBHS_T* const lpc_usb = LPC_USB[rhport]; + dcd_registers_t* const lpc_usb = dcd_regs[rhport]; tu_memclr(&_dcd_data, sizeof(dcd_data_t)); - lpc_usb->ENDPOINTLISTADDR = (uint32_t) _dcd_data.qhd; // Endpoint List Address has to be 2K alignment - lpc_usb->USBSTS_D = lpc_usb->USBSTS_D; - lpc_usb->USBINTR_D = INT_MASK_USB | INT_MASK_ERROR | INT_MASK_PORT_CHANGE | INT_MASK_RESET | INT_MASK_SUSPEND | INT_MASK_SOF; + lpc_usb->ENDPTLISTADDR = (uint32_t) _dcd_data.qhd; // Endpoint List Address has to be 2K alignment + lpc_usb->USBSTS = lpc_usb->USBSTS; + lpc_usb->USBINTR = INT_MASK_USB | INT_MASK_ERROR | INT_MASK_PORT_CHANGE | INT_MASK_RESET | INT_MASK_SUSPEND | INT_MASK_SOF; - lpc_usb->USBCMD_D &= ~0x00FF0000; // Interrupt Threshold Interval = 0 - lpc_usb->USBCMD_D |= TU_BIT(0); // connect + lpc_usb->USBCMD &= ~0x00FF0000; // Interrupt Threshold Interval = 0 + lpc_usb->USBCMD |= TU_BIT(0); // connect } void dcd_int_enable(uint8_t rhport) @@ -229,7 +279,7 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr) // Response with status first before changing device address dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); - LPC_USB[rhport]->DEVICEADDR = (dev_addr << 25) | TU_BIT(24); + dcd_regs[rhport]->DEVICEADDR = (dev_addr << 25) | TU_BIT(24); } void dcd_set_config(uint8_t rhport, uint8_t config_num) @@ -279,7 +329,7 @@ void dcd_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); - LPC_USB[rhport]->ENDPTCTRL[epnum] |= ENDPTCTRL_MASK_STALL << (dir ? 16 : 0); + dcd_regs[rhport]->ENDPTCTRL[epnum] |= ENDPTCTRL_MASK_STALL << (dir ? 16 : 0); } void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) @@ -288,8 +338,8 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) uint8_t const dir = tu_edpt_dir(ep_addr); // data toggle also need to be reset - LPC_USB[rhport]->ENDPTCTRL[epnum] |= ENDPTCTRL_MASK_TOGGLE_RESET << ( dir ? 16 : 0 ); - LPC_USB[rhport]->ENDPTCTRL[epnum] &= ~(ENDPTCTRL_MASK_STALL << ( dir ? 16 : 0)); + dcd_regs[rhport]->ENDPTCTRL[epnum] |= ENDPTCTRL_MASK_TOGGLE_RESET << ( dir ? 16 : 0 ); + dcd_regs[rhport]->ENDPTCTRL[epnum] &= ~(ENDPTCTRL_MASK_STALL << ( dir ? 16 : 0)); } bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) @@ -313,7 +363,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) p_qhd->qtd_overlay.next = QTD_NEXT_INVALID; // Enable EP Control - LPC_USB[rhport]->ENDPTCTRL[epnum] |= ((p_endpoint_desc->bmAttributes.xfer << 2) | ENDPTCTRL_MASK_ENABLE | ENDPTCTRL_MASK_TOGGLE_RESET) << (dir ? 16 : 0); + dcd_regs[rhport]->ENDPTCTRL[epnum] |= ((p_endpoint_desc->bmAttributes.xfer << 2) | ENDPTCTRL_MASK_ENABLE | ENDPTCTRL_MASK_TOGGLE_RESET) << (dir ? 16 : 0); return true; } @@ -328,7 +378,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t { // follows UM 24.10.8.1.1 Setup packet handling using setup lockout mechanism // wait until ENDPTSETUPSTAT before priming data/status in response TODO add time out - while(LPC_USB[rhport]->ENDPTSETUPSTAT & TU_BIT(0)) {} + while(dcd_regs[rhport]->ENDPTSETUPSTAT & TU_BIT(0)) {} } dcd_qhd_t * p_qhd = &_dcd_data.qhd[ep_idx]; @@ -340,7 +390,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // link qtd to qhd // start transfer - LPC_USB[rhport]->ENDPTPRIME = TU_BIT( ep_idx2bit(ep_idx) ) ; + dcd_regs[rhport]->ENDPTPRIME = TU_BIT( ep_idx2bit(ep_idx) ) ; return true; } @@ -350,11 +400,11 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t //--------------------------------------------------------------------+ void dcd_isr(uint8_t rhport) { - LPC_USBHS_T* const lpc_usb = LPC_USB[rhport]; + dcd_registers_t* const lpc_usb = dcd_regs[rhport]; - uint32_t const int_enable = lpc_usb->USBINTR_D; - uint32_t const int_status = lpc_usb->USBSTS_D & int_enable; - lpc_usb->USBSTS_D = int_status; // Acknowledge handled interrupt + uint32_t const int_enable = lpc_usb->USBINTR; + uint32_t const int_status = lpc_usb->USBSTS & int_enable; + lpc_usb->USBSTS = int_status; // Acknowledge handled interrupt if (int_status == 0) return;// disabled interrupt sources @@ -367,7 +417,7 @@ void dcd_isr(uint8_t rhport) if (int_status & INT_MASK_SUSPEND) { - if (lpc_usb->PORTSC1_D & PORTSC_SUSPEND_MASK) + if (lpc_usb->PORTSC1 & PORTSC_SUSPEND_MASK) { // Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration. if ((lpc_usb->DEVICEADDR >> 25) & 0x0f) @@ -380,7 +430,7 @@ void dcd_isr(uint8_t rhport) // TODO disconnection does not generate interrupt !!!!!! // if (int_status & INT_MASK_PORT_CHANGE) // { -// if ( !(lpc_usb->PORTSC1_D & PORTSC_CURRENT_CONNECT_STATUS_MASK) ) +// if ( !(lpc_usb->PORTSC1 & PORTSC_CURRENT_CONNECT_STATUS_MASK) ) // { // dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_UNPLUGGED }; // dcd_event_handler(&event, true); -- cgit v1.3.1 From 7e16a9a1dbbb5ad87d351a7332adb361b1b6b2d3 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 22 Nov 2019 12:14:55 +0700 Subject: more constant rename clean up --- src/portable/nxp/lpc18_43/dcd_lpc18_43.c | 100 ++++++++++++++++--------------- 1 file changed, 51 insertions(+), 49 deletions(-) (limited to 'src/portable') diff --git a/src/portable/nxp/lpc18_43/dcd_lpc18_43.c b/src/portable/nxp/lpc18_43/dcd_lpc18_43.c index 75dd7b850..1e710c9c6 100644 --- a/src/portable/nxp/lpc18_43/dcd_lpc18_43.c +++ b/src/portable/nxp/lpc18_43/dcd_lpc18_43.c @@ -49,6 +49,41 @@ // MACRO CONSTANT TYPEDEF //--------------------------------------------------------------------+ +// ENDPTCTRL +enum { + ENDPTCTRL_STALL = TU_BIT(0), + ENDPTCTRL_TOGGLE_INHIBIT = TU_BIT(5), ///< used for test only + ENDPTCTRL_TOGGLE_RESET = TU_BIT(6), + ENDPTCTRL_ENABLE = TU_BIT(7) +}; + +// USBCMD +enum { + USBCMD_RUN_STOP = TU_BIT(0), + USBCMD_RESET = TU_BIT(1), + USBCMD_SETUP_TRIPWIRE = TU_BIT(13), + USBCMD_ADD_QTD_TRIPWIRE = TU_BIT(14) ///< This bit is used as a semaphore to ensure the to proper addition of a new dTD to an active (primed) endpoint’s linked list. This bit is set and cleared by software during the process of adding a new dTD +}; +// Interrupt Threshold bit 23:16 + +// USBSTS, USBINTR +enum { + INTR_USB = TU_BIT(0), + INTR_ERROR = TU_BIT(1), + INTR_PORT_CHANGE = TU_BIT(2), + INTR_RESET = TU_BIT(6), + INTR_SOF = TU_BIT(7), + INTR_SUSPEND = TU_BIT(8), + INTR_NAK = TU_BIT(16) +}; + +// PORTSC +enum { + PORTSC_CURRENT_CONNECT_STATUS = TU_BIT(0), + PORTSC_FORCE_PORT_RESUME = TU_BIT(6), + PORTSC_SUSPEND = TU_BIT(7) +}; + // Device Register starting with CAPLENGTH offset typedef struct { @@ -95,41 +130,7 @@ typedef struct } dcd_registers_t; -/*---------- ENDPTCTRL ----------*/ -enum { - ENDPTCTRL_MASK_STALL = TU_BIT(0), - ENDPTCTRL_MASK_TOGGLE_INHIBIT = TU_BIT(5), ///< used for test only - ENDPTCTRL_MASK_TOGGLE_RESET = TU_BIT(6), - ENDPTCTRL_MASK_ENABLE = TU_BIT(7) -}; - -/*---------- USBCMD ----------*/ -enum { - USBCMD_MASK_RUN_STOP = TU_BIT(0), - USBCMD_MASK_RESET = TU_BIT(1), - USBCMD_MASK_SETUP_TRIPWIRE = TU_BIT(13), - USBCMD_MASK_ADD_QTD_TRIPWIRE = TU_BIT(14) ///< This bit is used as a semaphore to ensure the to proper addition of a new dTD to an active (primed) endpoint’s linked list. This bit is set and cleared by software during the process of adding a new dTD -}; -// Interrupt Threshold bit 23:16 - -/*---------- USBSTS, USBINTR ----------*/ -enum { - INT_MASK_USB = TU_BIT(0), - INT_MASK_ERROR = TU_BIT(1), - INT_MASK_PORT_CHANGE = TU_BIT(2), - INT_MASK_RESET = TU_BIT(6), - INT_MASK_SOF = TU_BIT(7), - INT_MASK_SUSPEND = TU_BIT(8), - INT_MASK_NAK = TU_BIT(16) -}; - -//------------- PORTSC -------------// -enum { - PORTSC_CURRENT_CONNECT_STATUS_MASK = TU_BIT(0), - PORTSC_FORCE_PORT_RESUME_MASK = TU_BIT(6), - PORTSC_SUSPEND_MASK = TU_BIT(7) -}; - +// Queue Transfer Descriptor typedef struct { // Word 0: Next QTD Pointer @@ -159,6 +160,7 @@ typedef struct TU_VERIFY_STATIC( sizeof(dcd_qtd_t) == 32, "size is not correct"); +// Queue Head typedef struct { // Word 0: Capabilities and Characteristics @@ -258,7 +260,7 @@ void dcd_init(uint8_t rhport) lpc_usb->ENDPTLISTADDR = (uint32_t) _dcd_data.qhd; // Endpoint List Address has to be 2K alignment lpc_usb->USBSTS = lpc_usb->USBSTS; - lpc_usb->USBINTR = INT_MASK_USB | INT_MASK_ERROR | INT_MASK_PORT_CHANGE | INT_MASK_RESET | INT_MASK_SUSPEND | INT_MASK_SOF; + lpc_usb->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_RESET | INTR_SUSPEND | INTR_SOF; lpc_usb->USBCMD &= ~0x00FF0000; // Interrupt Threshold Interval = 0 lpc_usb->USBCMD |= TU_BIT(0); // connect @@ -329,7 +331,7 @@ void dcd_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_regs[rhport]->ENDPTCTRL[epnum] |= ENDPTCTRL_MASK_STALL << (dir ? 16 : 0); + dcd_regs[rhport]->ENDPTCTRL[epnum] |= ENDPTCTRL_STALL << (dir ? 16 : 0); } void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) @@ -338,8 +340,8 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) uint8_t const dir = tu_edpt_dir(ep_addr); // data toggle also need to be reset - dcd_regs[rhport]->ENDPTCTRL[epnum] |= ENDPTCTRL_MASK_TOGGLE_RESET << ( dir ? 16 : 0 ); - dcd_regs[rhport]->ENDPTCTRL[epnum] &= ~(ENDPTCTRL_MASK_STALL << ( dir ? 16 : 0)); + dcd_regs[rhport]->ENDPTCTRL[epnum] |= ENDPTCTRL_TOGGLE_RESET << ( dir ? 16 : 0 ); + dcd_regs[rhport]->ENDPTCTRL[epnum] &= ~(ENDPTCTRL_STALL << ( dir ? 16 : 0)); } bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) @@ -363,7 +365,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) p_qhd->qtd_overlay.next = QTD_NEXT_INVALID; // Enable EP Control - dcd_regs[rhport]->ENDPTCTRL[epnum] |= ((p_endpoint_desc->bmAttributes.xfer << 2) | ENDPTCTRL_MASK_ENABLE | ENDPTCTRL_MASK_TOGGLE_RESET) << (dir ? 16 : 0); + dcd_regs[rhport]->ENDPTCTRL[epnum] |= ((p_endpoint_desc->bmAttributes.xfer << 2) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET) << (dir ? 16 : 0); return true; } @@ -409,15 +411,15 @@ void dcd_isr(uint8_t rhport) if (int_status == 0) return;// disabled interrupt sources - if (int_status & INT_MASK_RESET) + if (int_status & INTR_RESET) { bus_reset(rhport); dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, true); } - if (int_status & INT_MASK_SUSPEND) + if (int_status & INTR_SUSPEND) { - if (lpc_usb->PORTSC1 & PORTSC_SUSPEND_MASK) + if (lpc_usb->PORTSC1 & PORTSC_SUSPEND) { // Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration. if ((lpc_usb->DEVICEADDR >> 25) & 0x0f) @@ -428,16 +430,16 @@ void dcd_isr(uint8_t rhport) } // TODO disconnection does not generate interrupt !!!!!! -// if (int_status & INT_MASK_PORT_CHANGE) +// if (int_status & INTR_PORT_CHANGE) // { -// if ( !(lpc_usb->PORTSC1 & PORTSC_CURRENT_CONNECT_STATUS_MASK) ) +// if ( !(lpc_usb->PORTSC1 & PORTSC_CURRENT_CONNECT_STATUS) ) // { // dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_UNPLUGGED }; // dcd_event_handler(&event, true); // } // } - if (int_status & INT_MASK_USB) + if (int_status & INTR_USB) { uint32_t const edpt_complete = lpc_usb->ENDPTCOMPLETE; lpc_usb->ENDPTCOMPLETE = edpt_complete; // acknowledge @@ -470,13 +472,13 @@ void dcd_isr(uint8_t rhport) } } - if (int_status & INT_MASK_SOF) + if (int_status & INTR_SOF) { dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true); } - if (int_status & INT_MASK_NAK) {} - if (int_status & INT_MASK_ERROR) TU_ASSERT(false, ); + if (int_status & INTR_NAK) {} + if (int_status & INTR_ERROR) TU_ASSERT(false, ); } #endif -- cgit v1.3.1 From ccb09db3b782163e93f2208ff5612c789d465058 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 22 Nov 2019 12:16:47 +0700 Subject: more clean up --- src/portable/nxp/lpc18_43/dcd_lpc18_43.c | 74 ++++++++++++++++---------------- 1 file changed, 37 insertions(+), 37 deletions(-) (limited to 'src/portable') diff --git a/src/portable/nxp/lpc18_43/dcd_lpc18_43.c b/src/portable/nxp/lpc18_43/dcd_lpc18_43.c index 1e710c9c6..2f3d8ab25 100644 --- a/src/portable/nxp/lpc18_43/dcd_lpc18_43.c +++ b/src/portable/nxp/lpc18_43/dcd_lpc18_43.c @@ -205,7 +205,7 @@ typedef struct { static dcd_data_t _dcd_data CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(2048); //static LPC_USBHS_T * const LPC_USB[2] = { LPC_USB0, LPC_USB1 }; -static dcd_registers_t* dcd_regs[] = DCD_REGS_BASE; +static dcd_registers_t* DCD_REGS[] = DCD_REGS_BASE; //--------------------------------------------------------------------+ // CONTROLLER API @@ -214,7 +214,7 @@ static dcd_registers_t* dcd_regs[] = DCD_REGS_BASE; /// follows LPC43xx User Manual 23.10.3 static void bus_reset(uint8_t rhport) { - dcd_registers_t* lpc_usb = dcd_regs[rhport]; + dcd_registers_t* dcd_reg = DCD_REGS[rhport]; // The reset value for all endpoint types is the control endpoint. If one endpoint // direction is enabled and the paired endpoint of opposite direction is disabled, then the @@ -225,19 +225,19 @@ static void bus_reset(uint8_t rhport) // USB0 has 5 but USB1 only has 3 non-control endpoints for( int i=1; i < (rhport ? 6 : 4); i++) { - lpc_usb->ENDPTCTRL[i] = (TUSB_XFER_BULK << 2) | (TUSB_XFER_BULK << 18); + dcd_reg->ENDPTCTRL[i] = (TUSB_XFER_BULK << 2) | (TUSB_XFER_BULK << 18); } //------------- Clear All Registers -------------// - lpc_usb->ENDPTNAK = lpc_usb->ENDPTNAK; - lpc_usb->ENDPTNAKEN = 0; - lpc_usb->USBSTS = lpc_usb->USBSTS; - lpc_usb->ENDPTSETUPSTAT = lpc_usb->ENDPTSETUPSTAT; - lpc_usb->ENDPTCOMPLETE = lpc_usb->ENDPTCOMPLETE; + dcd_reg->ENDPTNAK = dcd_reg->ENDPTNAK; + dcd_reg->ENDPTNAKEN = 0; + dcd_reg->USBSTS = dcd_reg->USBSTS; + dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT; + dcd_reg->ENDPTCOMPLETE = dcd_reg->ENDPTCOMPLETE; - while (lpc_usb->ENDPTPRIME); - lpc_usb->ENDPTFLUSH = 0xFFFFFFFF; - while (lpc_usb->ENDPTFLUSH); + while (dcd_reg->ENDPTPRIME); + dcd_reg->ENDPTFLUSH = 0xFFFFFFFF; + while (dcd_reg->ENDPTFLUSH); // read reset bit in portsc @@ -254,16 +254,16 @@ static void bus_reset(uint8_t rhport) void dcd_init(uint8_t rhport) { - dcd_registers_t* const lpc_usb = dcd_regs[rhport]; + dcd_registers_t* const dcd_reg = DCD_REGS[rhport]; tu_memclr(&_dcd_data, sizeof(dcd_data_t)); - lpc_usb->ENDPTLISTADDR = (uint32_t) _dcd_data.qhd; // Endpoint List Address has to be 2K alignment - lpc_usb->USBSTS = lpc_usb->USBSTS; - lpc_usb->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_RESET | INTR_SUSPEND | INTR_SOF; + dcd_reg->ENDPTLISTADDR = (uint32_t) _dcd_data.qhd; // Endpoint List Address has to be 2K alignment + dcd_reg->USBSTS = dcd_reg->USBSTS; + dcd_reg->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_RESET | INTR_SUSPEND | INTR_SOF; - lpc_usb->USBCMD &= ~0x00FF0000; // Interrupt Threshold Interval = 0 - lpc_usb->USBCMD |= TU_BIT(0); // connect + dcd_reg->USBCMD &= ~0x00FF0000; // Interrupt Threshold Interval = 0 + dcd_reg->USBCMD |= TU_BIT(0); // connect } void dcd_int_enable(uint8_t rhport) @@ -281,7 +281,7 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr) // Response with status first before changing device address dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); - dcd_regs[rhport]->DEVICEADDR = (dev_addr << 25) | TU_BIT(24); + DCD_REGS[rhport]->DEVICEADDR = (dev_addr << 25) | TU_BIT(24); } void dcd_set_config(uint8_t rhport, uint8_t config_num) @@ -331,7 +331,7 @@ void dcd_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_regs[rhport]->ENDPTCTRL[epnum] |= ENDPTCTRL_STALL << (dir ? 16 : 0); + DCD_REGS[rhport]->ENDPTCTRL[epnum] |= ENDPTCTRL_STALL << (dir ? 16 : 0); } void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) @@ -340,8 +340,8 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) uint8_t const dir = tu_edpt_dir(ep_addr); // data toggle also need to be reset - dcd_regs[rhport]->ENDPTCTRL[epnum] |= ENDPTCTRL_TOGGLE_RESET << ( dir ? 16 : 0 ); - dcd_regs[rhport]->ENDPTCTRL[epnum] &= ~(ENDPTCTRL_STALL << ( dir ? 16 : 0)); + DCD_REGS[rhport]->ENDPTCTRL[epnum] |= ENDPTCTRL_TOGGLE_RESET << ( dir ? 16 : 0 ); + DCD_REGS[rhport]->ENDPTCTRL[epnum] &= ~(ENDPTCTRL_STALL << ( dir ? 16 : 0)); } bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) @@ -365,7 +365,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) p_qhd->qtd_overlay.next = QTD_NEXT_INVALID; // Enable EP Control - dcd_regs[rhport]->ENDPTCTRL[epnum] |= ((p_endpoint_desc->bmAttributes.xfer << 2) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET) << (dir ? 16 : 0); + DCD_REGS[rhport]->ENDPTCTRL[epnum] |= ((p_endpoint_desc->bmAttributes.xfer << 2) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET) << (dir ? 16 : 0); return true; } @@ -380,7 +380,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t { // follows UM 24.10.8.1.1 Setup packet handling using setup lockout mechanism // wait until ENDPTSETUPSTAT before priming data/status in response TODO add time out - while(dcd_regs[rhport]->ENDPTSETUPSTAT & TU_BIT(0)) {} + while(DCD_REGS[rhport]->ENDPTSETUPSTAT & TU_BIT(0)) {} } dcd_qhd_t * p_qhd = &_dcd_data.qhd[ep_idx]; @@ -392,7 +392,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // link qtd to qhd // start transfer - dcd_regs[rhport]->ENDPTPRIME = TU_BIT( ep_idx2bit(ep_idx) ) ; + DCD_REGS[rhport]->ENDPTPRIME = TU_BIT( ep_idx2bit(ep_idx) ) ; return true; } @@ -402,14 +402,14 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t //--------------------------------------------------------------------+ void dcd_isr(uint8_t rhport) { - dcd_registers_t* const lpc_usb = dcd_regs[rhport]; + dcd_registers_t* const dcd_reg = DCD_REGS[rhport]; - uint32_t const int_enable = lpc_usb->USBINTR; - uint32_t const int_status = lpc_usb->USBSTS & int_enable; - lpc_usb->USBSTS = int_status; // Acknowledge handled interrupt - - if (int_status == 0) return;// disabled interrupt sources + uint32_t const int_enable = dcd_reg->USBINTR; + uint32_t const int_status = dcd_reg->USBSTS & int_enable; + dcd_reg->USBSTS = int_status; // Acknowledge handled interrupt + // disabled interrupt sources + if (int_status == 0) return; if (int_status & INTR_RESET) { @@ -419,10 +419,10 @@ void dcd_isr(uint8_t rhport) if (int_status & INTR_SUSPEND) { - if (lpc_usb->PORTSC1 & PORTSC_SUSPEND) + if (dcd_reg->PORTSC1 & PORTSC_SUSPEND) { // Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration. - if ((lpc_usb->DEVICEADDR >> 25) & 0x0f) + if ((dcd_reg->DEVICEADDR >> 25) & 0x0f) { dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); } @@ -432,7 +432,7 @@ void dcd_isr(uint8_t rhport) // TODO disconnection does not generate interrupt !!!!!! // if (int_status & INTR_PORT_CHANGE) // { -// if ( !(lpc_usb->PORTSC1 & PORTSC_CURRENT_CONNECT_STATUS) ) +// if ( !(dcd_reg->PORTSC1 & PORTSC_CURRENT_CONNECT_STATUS) ) // { // dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_UNPLUGGED }; // dcd_event_handler(&event, true); @@ -441,14 +441,14 @@ void dcd_isr(uint8_t rhport) if (int_status & INTR_USB) { - uint32_t const edpt_complete = lpc_usb->ENDPTCOMPLETE; - lpc_usb->ENDPTCOMPLETE = edpt_complete; // acknowledge + uint32_t const edpt_complete = dcd_reg->ENDPTCOMPLETE; + dcd_reg->ENDPTCOMPLETE = edpt_complete; // acknowledge - if (lpc_usb->ENDPTSETUPSTAT) + if (dcd_reg->ENDPTSETUPSTAT) { //------------- Set up Received -------------// // 23.10.10.2 Operational model for setup transfers - lpc_usb->ENDPTSETUPSTAT = lpc_usb->ENDPTSETUPSTAT;// acknowledge + dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT;// acknowledge dcd_event_setup_received(rhport, (uint8_t*) &_dcd_data.qhd[0].setup_request, true); } -- cgit v1.3.1 From 2ead26a12d8ec1a3c229d2afdb3f36fdde444162 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 22 Nov 2019 12:26:40 +0700 Subject: more clean up --- src/portable/nxp/lpc18_43/dcd_lpc18_43.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src/portable') diff --git a/src/portable/nxp/lpc18_43/dcd_lpc18_43.c b/src/portable/nxp/lpc18_43/dcd_lpc18_43.c index 2f3d8ab25..6e3e993d2 100644 --- a/src/portable/nxp/lpc18_43/dcd_lpc18_43.c +++ b/src/portable/nxp/lpc18_43/dcd_lpc18_43.c @@ -38,11 +38,11 @@ #if CFG_TUSB_MCU == OPT_MCU_RT10XX #include "fsl_device_registers.h" + #define DCD_REGS_BASE { (dcd_registers_t*) USB1_BASE, (dcd_registers_t*) USB2_BASE } + #else #include "chip.h" - - // Register base to CAPLENGTH - #define DCD_REGS_BASE { (dcd_registers_t*) (LPC_USB0_BASE + 0x100), (dcd_registers_t*) (LPC_USB1_BASE + 0x100) } + #define DCD_REGS_BASE { (dcd_registers_t*) LPC_USB0_BASE, (dcd_registers_t*) LPC_USB1_BASE } #endif //--------------------------------------------------------------------+ @@ -84,9 +84,12 @@ enum { PORTSC_SUSPEND = TU_BIT(7) }; -// Device Register starting with CAPLENGTH offset +// Device Registers typedef struct { + //------------- ID + HW Parameter Registers-------------// + __I uint32_t TU_RESERVED[64]; ///< For iMX RT10xx, but not used by LPC18XX/LPC43XX + //------------- Capability Registers-------------// __I uint8_t CAPLENGTH; ///< Capability Registers Length __I uint8_t TU_RESERVED[1]; @@ -204,7 +207,6 @@ typedef struct { }dcd_data_t; static dcd_data_t _dcd_data CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(2048); -//static LPC_USBHS_T * const LPC_USB[2] = { LPC_USB0, LPC_USB1 }; static dcd_registers_t* DCD_REGS[] = DCD_REGS_BASE; //--------------------------------------------------------------------+ -- cgit v1.3.1 From fca4653b95038d1ed1905d5a4731f758a1023eaa Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 22 Nov 2019 12:34:41 +0700 Subject: able to compile dcd 18/43 with rt1064 --- src/portable/nxp/lpc18_43/dcd_lpc18_43.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/portable') diff --git a/src/portable/nxp/lpc18_43/dcd_lpc18_43.c b/src/portable/nxp/lpc18_43/dcd_lpc18_43.c index 6e3e993d2..4972446b6 100644 --- a/src/portable/nxp/lpc18_43/dcd_lpc18_43.c +++ b/src/portable/nxp/lpc18_43/dcd_lpc18_43.c @@ -38,11 +38,13 @@ #if CFG_TUSB_MCU == OPT_MCU_RT10XX #include "fsl_device_registers.h" - #define DCD_REGS_BASE { (dcd_registers_t*) USB1_BASE, (dcd_registers_t*) USB2_BASE } + #define DCD_REGS_BASE { (dcd_registers_t*) USB1_BASE, (dcd_registers_t*) USB2_BASE } + IRQn_Type DCD_IRQn[] = { USB_OTG1_IRQn, USB_OTG2_IRQn }; #else #include "chip.h" - #define DCD_REGS_BASE { (dcd_registers_t*) LPC_USB0_BASE, (dcd_registers_t*) LPC_USB1_BASE } + #define DCD_REGS_BASE { (dcd_registers_t*) LPC_USB0_BASE, (dcd_registers_t*) LPC_USB1_BASE } + IRQn_Type DCD_IRQn[] = { USB0_IRQn, USB1_IRQn }; #endif //--------------------------------------------------------------------+ @@ -270,12 +272,12 @@ void dcd_init(uint8_t rhport) void dcd_int_enable(uint8_t rhport) { - NVIC_EnableIRQ(rhport ? USB1_IRQn : USB0_IRQn); + NVIC_EnableIRQ(DCD_IRQn[rhport]); } void dcd_int_disable(uint8_t rhport) { - NVIC_DisableIRQ(rhport ? USB1_IRQn : USB0_IRQn); + NVIC_DisableIRQ(DCD_IRQn[rhport]); } void dcd_set_address(uint8_t rhport, uint8_t dev_addr) -- cgit v1.3.1 From a0b2561a2df93cccbf48002348daf4220262f8d8 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 22 Nov 2019 14:20:10 +0700 Subject: move nxp dcd ehci controller reset and modde into dcd rt1064 work with cdc msc example --- hw/bsp/ea4357/ea4357.c | 52 ++++++++++++++--------------- hw/bsp/mcb1800/mcb1800.c | 48 +++++++++++++-------------- hw/bsp/ngx4330/ngx4330.c | 52 ++++++++++++++--------------- src/portable/nxp/lpc18_43/dcd_lpc18_43.c | 56 ++++++++++++++++++++++++++++---- 4 files changed, 125 insertions(+), 83 deletions(-) (limited to 'src/portable') diff --git a/hw/bsp/ea4357/ea4357.c b/hw/bsp/ea4357/ea4357.c index f21d3259d..f5d2a199e 100644 --- a/hw/bsp/ea4357/ea4357.c +++ b/hw/bsp/ea4357/ea4357.c @@ -166,19 +166,19 @@ void board_init(void) #if CFG_TUSB_RHPORT0_MODE Chip_USB0_Init(); - // Reset controller - LPC_USB0->USBCMD_D |= 0x02; - while( LPC_USB0->USBCMD_D & 0x02 ) {} - - // Set mode - #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST - LPC_USB0->USBMODE_H = USBMODE_HOST | (USBMODE_VBUS_HIGH << 5); - - LPC_USB0->PORTSC1_D |= (1<<24); // FIXME force full speed for debugging - #else // TODO OTG - LPC_USB0->USBMODE_D = USBMODE_DEVICE; - LPC_USB0->OTGSC = (1<<3) | (1<<0) /*| (1<<16)| (1<<24)| (1<<25)| (1<<26)| (1<<27)| (1<<28)| (1<<29)| (1<<30)*/; - #endif +// // Reset controller +// LPC_USB0->USBCMD_D |= 0x02; +// while( LPC_USB0->USBCMD_D & 0x02 ) {} +// +// // Set mode +// #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST +// LPC_USB0->USBMODE_H = USBMODE_HOST | (USBMODE_VBUS_HIGH << 5); +// +// LPC_USB0->PORTSC1_D |= (1<<24); // FIXME force full speed for debugging +// #else // TODO OTG +// LPC_USB0->USBMODE_D = USBMODE_DEVICE; +// LPC_USB0->OTGSC = (1<<3) | (1<<0) /*| (1<<16)| (1<<24)| (1<<25)| (1<<26)| (1<<27)| (1<<28)| (1<<29)| (1<<30)*/; +// #endif #endif /* USB1 @@ -202,19 +202,19 @@ void board_init(void) #if CFG_TUSB_RHPORT1_MODE Chip_USB1_Init(); - // Reset controller - LPC_USB1->USBCMD_D |= 0x02; - while( LPC_USB1->USBCMD_D & 0x02 ) {} - - // Set mode - #if CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST - LPC_USB1->USBMODE_H = USBMODE_HOST | (USBMODE_VBUS_HIGH << 5); - #else // TODO OTG - LPC_USB1->USBMODE_D = USBMODE_DEVICE; - #endif - - // USB1 as fullspeed - LPC_USB1->PORTSC1_D |= (1<<24); +// // Reset controller +// LPC_USB1->USBCMD_D |= 0x02; +// while( LPC_USB1->USBCMD_D & 0x02 ) {} +// +// // Set mode +// #if CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST +// LPC_USB1->USBMODE_H = USBMODE_HOST | (USBMODE_VBUS_HIGH << 5); +// #else // TODO OTG +// LPC_USB1->USBMODE_D = USBMODE_DEVICE; +// #endif +// +// // USB1 as fullspeed +// LPC_USB1->PORTSC1_D |= (1<<24); #endif // USB0 Vbus Power: P2_3 on EA4357 channel B U20 GPIO26 active low (base board) diff --git a/hw/bsp/mcb1800/mcb1800.c b/hw/bsp/mcb1800/mcb1800.c index 4fe874762..6d5284869 100644 --- a/hw/bsp/mcb1800/mcb1800.c +++ b/hw/bsp/mcb1800/mcb1800.c @@ -139,36 +139,36 @@ void board_init(void) #if CFG_TUSB_RHPORT0_MODE Chip_USB0_Init(); - // Reset controller - LPC_USB0->USBCMD_D |= 0x02; - while( LPC_USB0->USBCMD_D & 0x02 ) {} - - // Set mode - #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST - LPC_USB0->USBMODE_H = USBMODE_HOST | (USBMODE_VBUS_HIGH << 5); - #else // TODO OTG - LPC_USB0->USBMODE_D = USBMODE_DEVICE; - LPC_USB0->OTGSC = (1<<3) | (1<<0) /*| (1<<16)| (1<<24)| (1<<25)| (1<<26)| (1<<27)| (1<<28)| (1<<29)| (1<<30)*/; - #endif +// // Reset controller +// LPC_USB0->USBCMD_D |= 0x02; +// while( LPC_USB0->USBCMD_D & 0x02 ) {} +// +// // Set mode +// #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST +// LPC_USB0->USBMODE_H = USBMODE_HOST | (USBMODE_VBUS_HIGH << 5); +// #else // TODO OTG +// LPC_USB0->USBMODE_D = USBMODE_DEVICE; +// LPC_USB0->OTGSC = (1<<3) | (1<<0) /*| (1<<16)| (1<<24)| (1<<25)| (1<<26)| (1<<27)| (1<<28)| (1<<29)| (1<<30)*/; +// #endif #endif // USB1 #if CFG_TUSB_RHPORT1_MODE Chip_USB1_Init(); - // Reset controller - LPC_USB1->USBCMD_D |= 0x02; - while( LPC_USB1->USBCMD_D & 0x02 ) {} - - // Set mode - #if CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST - LPC_USB1->USBMODE_H = USBMODE_HOST | (USBMODE_VBUS_HIGH << 5); - #else // TODO OTG - LPC_USB1->USBMODE_D = USBMODE_DEVICE; - #endif - - // USB1 as fullspeed - LPC_USB1->PORTSC1_D |= (1<<24); +// // Reset controller +// LPC_USB1->USBCMD_D |= 0x02; +// while( LPC_USB1->USBCMD_D & 0x02 ) {} +// +// // Set mode +// #if CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST +// LPC_USB1->USBMODE_H = USBMODE_HOST | (USBMODE_VBUS_HIGH << 5); +// #else // TODO OTG +// LPC_USB1->USBMODE_D = USBMODE_DEVICE; +// #endif +// +// // USB1 as fullspeed +// LPC_USB1->PORTSC1_D |= (1<<24); #endif } diff --git a/hw/bsp/ngx4330/ngx4330.c b/hw/bsp/ngx4330/ngx4330.c index fd6f0119b..e1bafff8d 100644 --- a/hw/bsp/ngx4330/ngx4330.c +++ b/hw/bsp/ngx4330/ngx4330.c @@ -164,19 +164,19 @@ void board_init(void) #if CFG_TUSB_RHPORT0_MODE Chip_USB0_Init(); - // Reset controller - LPC_USB0->USBCMD_D |= 0x02; - while( LPC_USB0->USBCMD_D & 0x02 ) {} - - // Set mode - #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST - LPC_USB0->USBMODE_H = USBMODE_HOST | (USBMODE_VBUS_HIGH << 5); - - LPC_USB0->PORTSC1_D |= (1<<24); // FIXME force full speed for debugging - #else // TODO OTG - LPC_USB0->USBMODE_D = USBMODE_DEVICE; - LPC_USB0->OTGSC = (1<<3) | (1<<0) /*| (1<<16)| (1<<24)| (1<<25)| (1<<26)| (1<<27)| (1<<28)| (1<<29)| (1<<30)*/; - #endif +// // Reset controller +// LPC_USB0->USBCMD_D |= 0x02; +// while( LPC_USB0->USBCMD_D & 0x02 ) {} +// +// // Set mode +// #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST +// LPC_USB0->USBMODE_H = USBMODE_HOST | (USBMODE_VBUS_HIGH << 5); +// +// LPC_USB0->PORTSC1_D |= (1<<24); // FIXME force full speed for debugging +// #else // TODO OTG +// LPC_USB0->USBMODE_D = USBMODE_DEVICE; +// LPC_USB0->OTGSC = (1<<3) | (1<<0) /*| (1<<16)| (1<<24)| (1<<25)| (1<<26)| (1<<27)| (1<<28)| (1<<29)| (1<<30)*/; +// #endif #endif /* USB1 @@ -200,19 +200,19 @@ void board_init(void) #if CFG_TUSB_RHPORT1_MODE Chip_USB1_Init(); - // Reset controller - LPC_USB1->USBCMD_D |= 0x02; - while( LPC_USB1->USBCMD_D & 0x02 ) {} - - // Set mode - #if CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST - LPC_USB1->USBMODE_H = USBMODE_HOST | (USBMODE_VBUS_HIGH << 5); - #else // TODO OTG - LPC_USB1->USBMODE_D = USBMODE_DEVICE; - #endif - - // USB1 as fullspeed - LPC_USB1->PORTSC1_D |= (1<<24); +// // Reset controller +// LPC_USB1->USBCMD_D |= 0x02; +// while( LPC_USB1->USBCMD_D & 0x02 ) {} +// +// // Set mode +// #if CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST +// LPC_USB1->USBMODE_H = USBMODE_HOST | (USBMODE_VBUS_HIGH << 5); +// #else // TODO OTG +// LPC_USB1->USBMODE_D = USBMODE_DEVICE; +// #endif +// +// // USB1 as fullspeed +// LPC_USB1->PORTSC1_D |= (1<<24); // Chip_GPIO_SetPinDIROutput(LPC_GPIO_PORT, 5, 6); /* GPIO5[6] = USB1_PWR_EN */ // Chip_GPIO_SetPinState(LPC_GPIO_PORT, 5, 6, true); /* GPIO5[6] output high */ diff --git a/src/portable/nxp/lpc18_43/dcd_lpc18_43.c b/src/portable/nxp/lpc18_43/dcd_lpc18_43.c index 4972446b6..b9527d65d 100644 --- a/src/portable/nxp/lpc18_43/dcd_lpc18_43.c +++ b/src/portable/nxp/lpc18_43/dcd_lpc18_43.c @@ -79,11 +79,42 @@ enum { INTR_NAK = TU_BIT(16) }; -// PORTSC +// PORTSC1 enum { - PORTSC_CURRENT_CONNECT_STATUS = TU_BIT(0), - PORTSC_FORCE_PORT_RESUME = TU_BIT(6), - PORTSC_SUSPEND = TU_BIT(7) + PORTSC1_CURRENT_CONNECT_STATUS = TU_BIT(0), + PORTSC1_FORCE_PORT_RESUME = TU_BIT(6), + PORTSC1_SUSPEND = TU_BIT(7), + PORTSC1_FORCE_FULL_SPEED = TU_BIT(24), +}; + +// OTGSC +enum { + OTGSC_VBUS_DISCHARGE = TU_BIT(0), + OTGSC_VBUS_CHARGE = TU_BIT(1), +// OTGSC_HWASSIST_AUTORESET = TU_BIT(2), + OTGSC_OTG_TERMINATION = TU_BIT(3), ///< Must set to 1 when OTG go to device mode + OTGSC_DATA_PULSING = TU_BIT(4), + OTGSC_ID_PULLUP = TU_BIT(5), +// OTGSC_HWASSIT_DATA_PULSE = TU_BIT(6), +// OTGSC_HWASSIT_BDIS_ACONN = TU_BIT(7), + OTGSC_ID = TU_BIT(8), ///< 0 = A device, 1 = B Device + OTGSC_A_VBUS_VALID = TU_BIT(9), + OTGSC_A_SESSION_VALID = TU_BIT(10), + OTGSC_B_SESSION_VALID = TU_BIT(11), + OTGSC_B_SESSION_END = TU_BIT(12), + OTGSC_1MS_TOGGLE = TU_BIT(13), + OTGSC_DATA_BUS_PULSING_STATUS = TU_BIT(14), +}; + +// USBMode +enum { + USBMODE_CM_DEVICE = 2, + USBMODE_CM_HOST = 3, + + USBMODE_SLOM = TU_BIT(3), + USBMODE_SDIS = TU_BIT(4), + + USBMODE_VBUS_POWER_SELCT = TU_BIT(5), // Enable for LPC18XX/43XX in host most only }; // Device Registers @@ -258,9 +289,20 @@ static void bus_reset(uint8_t rhport) void dcd_init(uint8_t rhport) { + tu_memclr(&_dcd_data, sizeof(dcd_data_t)); + dcd_registers_t* const dcd_reg = DCD_REGS[rhport]; - tu_memclr(&_dcd_data, sizeof(dcd_data_t)); + // Reset controller + dcd_reg->USBCMD |= USBCMD_RESET; + while( dcd_reg->USBCMD & USBCMD_RESET ) {} + + // Set mode to device, must be set immediately after reset + dcd_reg->USBMODE = USBMODE_CM_DEVICE; + dcd_reg->OTGSC = OTGSC_VBUS_DISCHARGE | OTGSC_OTG_TERMINATION; + + // TODO Force fullspeed on non-highspeed port + // dcd_reg->PORTSC1 = PORTSC1_FORCE_FULL_SPEED; dcd_reg->ENDPTLISTADDR = (uint32_t) _dcd_data.qhd; // Endpoint List Address has to be 2K alignment dcd_reg->USBSTS = dcd_reg->USBSTS; @@ -423,7 +465,7 @@ void dcd_isr(uint8_t rhport) if (int_status & INTR_SUSPEND) { - if (dcd_reg->PORTSC1 & PORTSC_SUSPEND) + if (dcd_reg->PORTSC1 & PORTSC1_SUSPEND) { // Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration. if ((dcd_reg->DEVICEADDR >> 25) & 0x0f) @@ -436,7 +478,7 @@ void dcd_isr(uint8_t rhport) // TODO disconnection does not generate interrupt !!!!!! // if (int_status & INTR_PORT_CHANGE) // { -// if ( !(dcd_reg->PORTSC1 & PORTSC_CURRENT_CONNECT_STATUS) ) +// if ( !(dcd_reg->PORTSC1 & PORTSC1_CURRENT_CONNECT_STATUS) ) // { // dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_UNPLUGGED }; // dcd_event_handler(&event, true); -- cgit v1.3.1 From 6123b600fc9706cec73f1116eeaf0246773fccf9 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 22 Nov 2019 14:47:07 +0700 Subject: rename dcd_lpc18_43 to dcd_transdimension --- src/portable/nxp/lpc18_43/dcd_lpc18_43.c | 530 --------------------- .../nxp/transdimension/dcd_transdimension.c | 530 +++++++++++++++++++++ 2 files changed, 530 insertions(+), 530 deletions(-) delete mode 100644 src/portable/nxp/lpc18_43/dcd_lpc18_43.c create mode 100644 src/portable/nxp/transdimension/dcd_transdimension.c (limited to 'src/portable') diff --git a/src/portable/nxp/lpc18_43/dcd_lpc18_43.c b/src/portable/nxp/lpc18_43/dcd_lpc18_43.c deleted file mode 100644 index b9527d65d..000000000 --- a/src/portable/nxp/lpc18_43/dcd_lpc18_43.c +++ /dev/null @@ -1,530 +0,0 @@ -/* - * 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 && (CFG_TUSB_MCU == OPT_MCU_LPC18XX || \ - CFG_TUSB_MCU == OPT_MCU_LPC43XX || \ - CFG_TUSB_MCU == OPT_MCU_RT10XX) - -//--------------------------------------------------------------------+ -// INCLUDE -//--------------------------------------------------------------------+ -#include "common/tusb_common.h" -#include "device/dcd.h" - -#if CFG_TUSB_MCU == OPT_MCU_RT10XX - #include "fsl_device_registers.h" - #define DCD_REGS_BASE { (dcd_registers_t*) USB1_BASE, (dcd_registers_t*) USB2_BASE } - IRQn_Type DCD_IRQn[] = { USB_OTG1_IRQn, USB_OTG2_IRQn }; - -#else - #include "chip.h" - #define DCD_REGS_BASE { (dcd_registers_t*) LPC_USB0_BASE, (dcd_registers_t*) LPC_USB1_BASE } - IRQn_Type DCD_IRQn[] = { USB0_IRQn, USB1_IRQn }; -#endif - -//--------------------------------------------------------------------+ -// MACRO CONSTANT TYPEDEF -//--------------------------------------------------------------------+ - -// ENDPTCTRL -enum { - ENDPTCTRL_STALL = TU_BIT(0), - ENDPTCTRL_TOGGLE_INHIBIT = TU_BIT(5), ///< used for test only - ENDPTCTRL_TOGGLE_RESET = TU_BIT(6), - ENDPTCTRL_ENABLE = TU_BIT(7) -}; - -// USBCMD -enum { - USBCMD_RUN_STOP = TU_BIT(0), - USBCMD_RESET = TU_BIT(1), - USBCMD_SETUP_TRIPWIRE = TU_BIT(13), - USBCMD_ADD_QTD_TRIPWIRE = TU_BIT(14) ///< This bit is used as a semaphore to ensure the to proper addition of a new dTD to an active (primed) endpoint’s linked list. This bit is set and cleared by software during the process of adding a new dTD -}; -// Interrupt Threshold bit 23:16 - -// USBSTS, USBINTR -enum { - INTR_USB = TU_BIT(0), - INTR_ERROR = TU_BIT(1), - INTR_PORT_CHANGE = TU_BIT(2), - INTR_RESET = TU_BIT(6), - INTR_SOF = TU_BIT(7), - INTR_SUSPEND = TU_BIT(8), - INTR_NAK = TU_BIT(16) -}; - -// PORTSC1 -enum { - PORTSC1_CURRENT_CONNECT_STATUS = TU_BIT(0), - PORTSC1_FORCE_PORT_RESUME = TU_BIT(6), - PORTSC1_SUSPEND = TU_BIT(7), - PORTSC1_FORCE_FULL_SPEED = TU_BIT(24), -}; - -// OTGSC -enum { - OTGSC_VBUS_DISCHARGE = TU_BIT(0), - OTGSC_VBUS_CHARGE = TU_BIT(1), -// OTGSC_HWASSIST_AUTORESET = TU_BIT(2), - OTGSC_OTG_TERMINATION = TU_BIT(3), ///< Must set to 1 when OTG go to device mode - OTGSC_DATA_PULSING = TU_BIT(4), - OTGSC_ID_PULLUP = TU_BIT(5), -// OTGSC_HWASSIT_DATA_PULSE = TU_BIT(6), -// OTGSC_HWASSIT_BDIS_ACONN = TU_BIT(7), - OTGSC_ID = TU_BIT(8), ///< 0 = A device, 1 = B Device - OTGSC_A_VBUS_VALID = TU_BIT(9), - OTGSC_A_SESSION_VALID = TU_BIT(10), - OTGSC_B_SESSION_VALID = TU_BIT(11), - OTGSC_B_SESSION_END = TU_BIT(12), - OTGSC_1MS_TOGGLE = TU_BIT(13), - OTGSC_DATA_BUS_PULSING_STATUS = TU_BIT(14), -}; - -// USBMode -enum { - USBMODE_CM_DEVICE = 2, - USBMODE_CM_HOST = 3, - - USBMODE_SLOM = TU_BIT(3), - USBMODE_SDIS = TU_BIT(4), - - USBMODE_VBUS_POWER_SELCT = TU_BIT(5), // Enable for LPC18XX/43XX in host most only -}; - -// Device Registers -typedef struct -{ - //------------- ID + HW Parameter Registers-------------// - __I uint32_t TU_RESERVED[64]; ///< For iMX RT10xx, but not used by LPC18XX/LPC43XX - - //------------- Capability Registers-------------// - __I uint8_t CAPLENGTH; ///< Capability Registers Length - __I uint8_t TU_RESERVED[1]; - __I uint16_t HCIVERSION; ///< Host Controller Interface Version - - __I uint32_t HCSPARAMS; ///< Host Controller Structural Parameters - __I uint32_t HCCPARAMS; ///< Host Controller Capability Parameters - __I uint32_t TU_RESERVED[5]; - - __I uint16_t DCIVERSION; ///< Device Controller Interface Version - __I uint8_t TU_RESERVED[2]; - - __I uint32_t DCCPARAMS; ///< Device Controller Capability Parameters - __I uint32_t TU_RESERVED[6]; - - //------------- Operational Registers -------------// - __IO uint32_t USBCMD; ///< USB Command Register - __IO uint32_t USBSTS; ///< USB Status Register - __IO uint32_t USBINTR; ///< Interrupt Enable Register - __IO uint32_t FRINDEX; ///< USB Frame Index - __I uint32_t TU_RESERVED; - __IO uint32_t DEVICEADDR; ///< Device Address - __IO uint32_t ENDPTLISTADDR; ///< Endpoint List Address - __I uint32_t TU_RESERVED; - __IO uint32_t BURSTSIZE; ///< Programmable Burst Size - __IO uint32_t TXFILLTUNING; ///< TX FIFO Fill Tuning - uint32_t TU_RESERVED[4]; - __IO uint32_t ENDPTNAK; ///< Endpoint NAK - __IO uint32_t ENDPTNAKEN; ///< Endpoint NAK Enable - __I uint32_t TU_RESERVED; - __IO uint32_t PORTSC1; ///< Port Status & Control - __I uint32_t TU_RESERVED[7]; - __IO uint32_t OTGSC; ///< On-The-Go Status & control - __IO uint32_t USBMODE; ///< USB Device Mode - __IO uint32_t ENDPTSETUPSTAT; ///< Endpoint Setup Status - __IO uint32_t ENDPTPRIME; ///< Endpoint Prime - __IO uint32_t ENDPTFLUSH; ///< Endpoint Flush - __I uint32_t ENDPTSTAT; ///< Endpoint Status - __IO uint32_t ENDPTCOMPLETE; ///< Endpoint Complete - __IO uint32_t ENDPTCTRL[8]; ///< Endpoint Control 0 - 7 -} dcd_registers_t; - - -// Queue Transfer Descriptor -typedef struct -{ - // Word 0: Next QTD Pointer - uint32_t next; ///< Next link pointer This field contains the physical memory address of the next dTD to be processed - - // Word 1: qTQ Token - uint32_t : 3 ; - volatile uint32_t xact_err : 1 ; - uint32_t : 1 ; - volatile uint32_t buffer_err : 1 ; - volatile uint32_t halted : 1 ; - volatile uint32_t active : 1 ; - uint32_t : 2 ; - uint32_t iso_mult_override : 2 ; ///< This field can be used for transmit ISOs to override the MULT field in the dQH. This field must be zero for all packet types that are not transmit-ISO. - uint32_t : 3 ; - uint32_t int_on_complete : 1 ; - volatile uint32_t total_bytes : 15 ; - uint32_t : 0 ; - - // Word 2-6: Buffer Page Pointer List, Each element in the list is a 4K page aligned, physical memory address. The lower 12 bits in each pointer are reserved (except for the first one) as each memory pointer must reference the start of a 4K page - uint32_t buffer[5]; ///< buffer1 has frame_n for TODO Isochronous - - //------------- DCD Area -------------// - uint16_t expected_bytes; - uint8_t reserved[2]; -} dcd_qtd_t; - -TU_VERIFY_STATIC( sizeof(dcd_qtd_t) == 32, "size is not correct"); - -// Queue Head -typedef struct -{ - // Word 0: Capabilities and Characteristics - uint32_t : 15 ; ///< Number of packets executed per transaction descriptor 00 - Execute N transactions as demonstrated by the USB variable length protocol where N is computed using Max_packet_length and the Total_bytes field in the dTD. 01 - Execute one transaction 10 - Execute two transactions 11 - Execute three transactions Remark: Non-isochronous endpoints must set MULT = 00. Remark: Isochronous endpoints must set MULT = 01, 10, or 11 as needed. - uint32_t int_on_setup : 1 ; ///< Interrupt on setup This bit is used on control type endpoints to indicate if USBINT is set in response to a setup being received. - uint32_t max_package_size : 11 ; ///< This directly corresponds to the maximum packet size of the associated endpoint (wMaxPacketSize) - uint32_t : 2 ; - uint32_t zero_length_termination : 1 ; ///< This bit is used for non-isochronous endpoints to indicate when a zero-length packet is received to terminate transfers in case the total transfer length is “multiple”. 0 - Enable zero-length packet to terminate transfers equal to a multiple of Max_packet_length (default). 1 - Disable zero-length packet on transfers that are equal in length to a multiple Max_packet_length. - uint32_t iso_mult : 2 ; ///< - uint32_t : 0 ; - - // Word 1: Current qTD Pointer - volatile uint32_t qtd_addr; - - // Word 2-9: Transfer Overlay - volatile dcd_qtd_t qtd_overlay; - - // Word 10-11: Setup request (control OUT only) - volatile tusb_control_request_t setup_request; - - //--------------------------------------------------------------------+ - /// Due to the fact QHD is 64 bytes aligned but occupies only 48 bytes - /// thus there are 16 bytes padding free that we can make use of. - //--------------------------------------------------------------------+ - uint8_t reserved[16]; -} dcd_qhd_t; - -TU_VERIFY_STATIC( sizeof(dcd_qhd_t) == 64, "size is not correct"); - -//--------------------------------------------------------------------+ -// Variables -//--------------------------------------------------------------------+ - -#define QHD_MAX 12 -#define QTD_NEXT_INVALID 0x01 - -typedef struct { - // Must be at 2K alignment - dcd_qhd_t qhd[QHD_MAX] TU_ATTR_ALIGNED(64); - dcd_qtd_t qtd[QHD_MAX] TU_ATTR_ALIGNED(32); -}dcd_data_t; - -static dcd_data_t _dcd_data CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(2048); -static dcd_registers_t* DCD_REGS[] = DCD_REGS_BASE; - -//--------------------------------------------------------------------+ -// CONTROLLER API -//--------------------------------------------------------------------+ - -/// follows LPC43xx User Manual 23.10.3 -static void bus_reset(uint8_t rhport) -{ - dcd_registers_t* dcd_reg = DCD_REGS[rhport]; - - // The reset value for all endpoint types is the control endpoint. If one endpoint - // direction is enabled and the paired endpoint of opposite direction is disabled, then the - // endpoint type of the unused direction must bechanged from the control type to any other - // type (e.g. bulk). Leaving an unconfigured endpoint control will cause undefined behavior - // for the data PID tracking on the active endpoint. - - // USB0 has 5 but USB1 only has 3 non-control endpoints - for( int i=1; i < (rhport ? 6 : 4); i++) - { - dcd_reg->ENDPTCTRL[i] = (TUSB_XFER_BULK << 2) | (TUSB_XFER_BULK << 18); - } - - //------------- Clear All Registers -------------// - dcd_reg->ENDPTNAK = dcd_reg->ENDPTNAK; - dcd_reg->ENDPTNAKEN = 0; - dcd_reg->USBSTS = dcd_reg->USBSTS; - dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT; - dcd_reg->ENDPTCOMPLETE = dcd_reg->ENDPTCOMPLETE; - - while (dcd_reg->ENDPTPRIME); - dcd_reg->ENDPTFLUSH = 0xFFFFFFFF; - while (dcd_reg->ENDPTFLUSH); - - // read reset bit in portsc - - //------------- Queue Head & Queue TD -------------// - tu_memclr(&_dcd_data, sizeof(dcd_data_t)); - - //------------- Set up Control Endpoints (0 OUT, 1 IN) -------------// - _dcd_data.qhd[0].zero_length_termination = _dcd_data.qhd[1].zero_length_termination = 1; - _dcd_data.qhd[0].max_package_size = _dcd_data.qhd[1].max_package_size = CFG_TUD_ENDPOINT0_SIZE; - _dcd_data.qhd[0].qtd_overlay.next = _dcd_data.qhd[1].qtd_overlay.next = QTD_NEXT_INVALID; - - _dcd_data.qhd[0].int_on_setup = 1; // OUT only -} - -void dcd_init(uint8_t rhport) -{ - tu_memclr(&_dcd_data, sizeof(dcd_data_t)); - - dcd_registers_t* const dcd_reg = DCD_REGS[rhport]; - - // Reset controller - dcd_reg->USBCMD |= USBCMD_RESET; - while( dcd_reg->USBCMD & USBCMD_RESET ) {} - - // Set mode to device, must be set immediately after reset - dcd_reg->USBMODE = USBMODE_CM_DEVICE; - dcd_reg->OTGSC = OTGSC_VBUS_DISCHARGE | OTGSC_OTG_TERMINATION; - - // TODO Force fullspeed on non-highspeed port - // dcd_reg->PORTSC1 = PORTSC1_FORCE_FULL_SPEED; - - dcd_reg->ENDPTLISTADDR = (uint32_t) _dcd_data.qhd; // Endpoint List Address has to be 2K alignment - dcd_reg->USBSTS = dcd_reg->USBSTS; - dcd_reg->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_RESET | INTR_SUSPEND | INTR_SOF; - - dcd_reg->USBCMD &= ~0x00FF0000; // Interrupt Threshold Interval = 0 - dcd_reg->USBCMD |= TU_BIT(0); // connect -} - -void dcd_int_enable(uint8_t rhport) -{ - NVIC_EnableIRQ(DCD_IRQn[rhport]); -} - -void dcd_int_disable(uint8_t rhport) -{ - NVIC_DisableIRQ(DCD_IRQn[rhport]); -} - -void dcd_set_address(uint8_t rhport, uint8_t dev_addr) -{ - // Response with status first before changing device address - dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); - - DCD_REGS[rhport]->DEVICEADDR = (dev_addr << 25) | TU_BIT(24); -} - -void dcd_set_config(uint8_t rhport, uint8_t config_num) -{ - (void) rhport; - (void) config_num; - // nothing to do -} - -void dcd_remote_wakeup(uint8_t rhport) -{ - (void) rhport; -} - -//--------------------------------------------------------------------+ -// HELPER -//--------------------------------------------------------------------+ -// index to bit position in register -static inline uint8_t ep_idx2bit(uint8_t ep_idx) -{ - return ep_idx/2 + ( (ep_idx%2) ? 16 : 0); -} - -static void qtd_init(dcd_qtd_t* p_qtd, void * data_ptr, uint16_t total_bytes) -{ - tu_memclr(p_qtd, sizeof(dcd_qtd_t)); - - p_qtd->next = QTD_NEXT_INVALID; - p_qtd->active = 1; - p_qtd->total_bytes = p_qtd->expected_bytes = total_bytes; - - if (data_ptr != NULL) - { - p_qtd->buffer[0] = (uint32_t) data_ptr; - for(uint8_t i=1; i<5; i++) - { - p_qtd->buffer[i] |= tu_align4k( p_qtd->buffer[i-1] ) + 4096; - } - } -} - -//--------------------------------------------------------------------+ -// DCD Endpoint Port -//--------------------------------------------------------------------+ -void dcd_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_REGS[rhport]->ENDPTCTRL[epnum] |= ENDPTCTRL_STALL << (dir ? 16 : 0); -} - -void dcd_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); - - // data toggle also need to be reset - DCD_REGS[rhport]->ENDPTCTRL[epnum] |= ENDPTCTRL_TOGGLE_RESET << ( dir ? 16 : 0 ); - DCD_REGS[rhport]->ENDPTCTRL[epnum] &= ~(ENDPTCTRL_STALL << ( dir ? 16 : 0)); -} - -bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) -{ - // TODO not support ISO yet - TU_VERIFY ( p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS); - - uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress); - uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); - uint8_t const ep_idx = 2*epnum + dir; - - // USB0 has 5, USB1 has 3 non-control endpoints - TU_ASSERT( epnum <= (rhport ? 3 : 5) ); - - //------------- Prepare Queue Head -------------// - dcd_qhd_t * p_qhd = &_dcd_data.qhd[ep_idx]; - tu_memclr(p_qhd, sizeof(dcd_qhd_t)); - - p_qhd->zero_length_termination = 1; - p_qhd->max_package_size = p_endpoint_desc->wMaxPacketSize.size; - p_qhd->qtd_overlay.next = QTD_NEXT_INVALID; - - // Enable EP Control - DCD_REGS[rhport]->ENDPTCTRL[epnum] |= ((p_endpoint_desc->bmAttributes.xfer << 2) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET) << (dir ? 16 : 0); - - return true; -} - -bool dcd_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); - uint8_t const ep_idx = 2*epnum + dir; - - if ( epnum == 0 ) - { - // follows UM 24.10.8.1.1 Setup packet handling using setup lockout mechanism - // wait until ENDPTSETUPSTAT before priming data/status in response TODO add time out - while(DCD_REGS[rhport]->ENDPTSETUPSTAT & TU_BIT(0)) {} - } - - dcd_qhd_t * p_qhd = &_dcd_data.qhd[ep_idx]; - dcd_qtd_t * p_qtd = &_dcd_data.qtd[ep_idx]; - - //------------- Prepare qtd -------------// - qtd_init(p_qtd, buffer, total_bytes); - p_qtd->int_on_complete = true; - p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // link qtd to qhd - - // start transfer - DCD_REGS[rhport]->ENDPTPRIME = TU_BIT( ep_idx2bit(ep_idx) ) ; - - return true; -} - -//--------------------------------------------------------------------+ -// ISR -//--------------------------------------------------------------------+ -void dcd_isr(uint8_t rhport) -{ - dcd_registers_t* const dcd_reg = DCD_REGS[rhport]; - - uint32_t const int_enable = dcd_reg->USBINTR; - uint32_t const int_status = dcd_reg->USBSTS & int_enable; - dcd_reg->USBSTS = int_status; // Acknowledge handled interrupt - - // disabled interrupt sources - if (int_status == 0) return; - - if (int_status & INTR_RESET) - { - bus_reset(rhport); - dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, true); - } - - if (int_status & INTR_SUSPEND) - { - if (dcd_reg->PORTSC1 & PORTSC1_SUSPEND) - { - // Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration. - if ((dcd_reg->DEVICEADDR >> 25) & 0x0f) - { - dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); - } - } - } - - // TODO disconnection does not generate interrupt !!!!!! -// if (int_status & INTR_PORT_CHANGE) -// { -// if ( !(dcd_reg->PORTSC1 & PORTSC1_CURRENT_CONNECT_STATUS) ) -// { -// dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_UNPLUGGED }; -// dcd_event_handler(&event, true); -// } -// } - - if (int_status & INTR_USB) - { - uint32_t const edpt_complete = dcd_reg->ENDPTCOMPLETE; - dcd_reg->ENDPTCOMPLETE = edpt_complete; // acknowledge - - if (dcd_reg->ENDPTSETUPSTAT) - { - //------------- Set up Received -------------// - // 23.10.10.2 Operational model for setup transfers - dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT;// acknowledge - - dcd_event_setup_received(rhport, (uint8_t*) &_dcd_data.qhd[0].setup_request, true); - } - - if ( edpt_complete ) - { - for(uint8_t ep_idx = 0; ep_idx < QHD_MAX; ep_idx++) - { - if ( tu_bit_test(edpt_complete, ep_idx2bit(ep_idx)) ) - { - // 23.10.12.3 Failed QTD also get ENDPTCOMPLETE set - dcd_qtd_t * p_qtd = &_dcd_data.qtd[ep_idx]; - - uint8_t result = p_qtd->halted ? XFER_RESULT_STALLED : - ( p_qtd->xact_err ||p_qtd->buffer_err ) ? XFER_RESULT_FAILED : XFER_RESULT_SUCCESS; - - uint8_t const ep_addr = (ep_idx/2) | ( (ep_idx & 0x01) ? TUSB_DIR_IN_MASK : 0 ); - dcd_event_xfer_complete(rhport, ep_addr, p_qtd->expected_bytes - p_qtd->total_bytes, result, true); // only number of bytes in the IOC qtd - } - } - } - } - - if (int_status & INTR_SOF) - { - dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true); - } - - if (int_status & INTR_NAK) {} - if (int_status & INTR_ERROR) TU_ASSERT(false, ); -} - -#endif diff --git a/src/portable/nxp/transdimension/dcd_transdimension.c b/src/portable/nxp/transdimension/dcd_transdimension.c new file mode 100644 index 000000000..b9527d65d --- /dev/null +++ b/src/portable/nxp/transdimension/dcd_transdimension.c @@ -0,0 +1,530 @@ +/* + * 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 && (CFG_TUSB_MCU == OPT_MCU_LPC18XX || \ + CFG_TUSB_MCU == OPT_MCU_LPC43XX || \ + CFG_TUSB_MCU == OPT_MCU_RT10XX) + +//--------------------------------------------------------------------+ +// INCLUDE +//--------------------------------------------------------------------+ +#include "common/tusb_common.h" +#include "device/dcd.h" + +#if CFG_TUSB_MCU == OPT_MCU_RT10XX + #include "fsl_device_registers.h" + #define DCD_REGS_BASE { (dcd_registers_t*) USB1_BASE, (dcd_registers_t*) USB2_BASE } + IRQn_Type DCD_IRQn[] = { USB_OTG1_IRQn, USB_OTG2_IRQn }; + +#else + #include "chip.h" + #define DCD_REGS_BASE { (dcd_registers_t*) LPC_USB0_BASE, (dcd_registers_t*) LPC_USB1_BASE } + IRQn_Type DCD_IRQn[] = { USB0_IRQn, USB1_IRQn }; +#endif + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +// ENDPTCTRL +enum { + ENDPTCTRL_STALL = TU_BIT(0), + ENDPTCTRL_TOGGLE_INHIBIT = TU_BIT(5), ///< used for test only + ENDPTCTRL_TOGGLE_RESET = TU_BIT(6), + ENDPTCTRL_ENABLE = TU_BIT(7) +}; + +// USBCMD +enum { + USBCMD_RUN_STOP = TU_BIT(0), + USBCMD_RESET = TU_BIT(1), + USBCMD_SETUP_TRIPWIRE = TU_BIT(13), + USBCMD_ADD_QTD_TRIPWIRE = TU_BIT(14) ///< This bit is used as a semaphore to ensure the to proper addition of a new dTD to an active (primed) endpoint’s linked list. This bit is set and cleared by software during the process of adding a new dTD +}; +// Interrupt Threshold bit 23:16 + +// USBSTS, USBINTR +enum { + INTR_USB = TU_BIT(0), + INTR_ERROR = TU_BIT(1), + INTR_PORT_CHANGE = TU_BIT(2), + INTR_RESET = TU_BIT(6), + INTR_SOF = TU_BIT(7), + INTR_SUSPEND = TU_BIT(8), + INTR_NAK = TU_BIT(16) +}; + +// PORTSC1 +enum { + PORTSC1_CURRENT_CONNECT_STATUS = TU_BIT(0), + PORTSC1_FORCE_PORT_RESUME = TU_BIT(6), + PORTSC1_SUSPEND = TU_BIT(7), + PORTSC1_FORCE_FULL_SPEED = TU_BIT(24), +}; + +// OTGSC +enum { + OTGSC_VBUS_DISCHARGE = TU_BIT(0), + OTGSC_VBUS_CHARGE = TU_BIT(1), +// OTGSC_HWASSIST_AUTORESET = TU_BIT(2), + OTGSC_OTG_TERMINATION = TU_BIT(3), ///< Must set to 1 when OTG go to device mode + OTGSC_DATA_PULSING = TU_BIT(4), + OTGSC_ID_PULLUP = TU_BIT(5), +// OTGSC_HWASSIT_DATA_PULSE = TU_BIT(6), +// OTGSC_HWASSIT_BDIS_ACONN = TU_BIT(7), + OTGSC_ID = TU_BIT(8), ///< 0 = A device, 1 = B Device + OTGSC_A_VBUS_VALID = TU_BIT(9), + OTGSC_A_SESSION_VALID = TU_BIT(10), + OTGSC_B_SESSION_VALID = TU_BIT(11), + OTGSC_B_SESSION_END = TU_BIT(12), + OTGSC_1MS_TOGGLE = TU_BIT(13), + OTGSC_DATA_BUS_PULSING_STATUS = TU_BIT(14), +}; + +// USBMode +enum { + USBMODE_CM_DEVICE = 2, + USBMODE_CM_HOST = 3, + + USBMODE_SLOM = TU_BIT(3), + USBMODE_SDIS = TU_BIT(4), + + USBMODE_VBUS_POWER_SELCT = TU_BIT(5), // Enable for LPC18XX/43XX in host most only +}; + +// Device Registers +typedef struct +{ + //------------- ID + HW Parameter Registers-------------// + __I uint32_t TU_RESERVED[64]; ///< For iMX RT10xx, but not used by LPC18XX/LPC43XX + + //------------- Capability Registers-------------// + __I uint8_t CAPLENGTH; ///< Capability Registers Length + __I uint8_t TU_RESERVED[1]; + __I uint16_t HCIVERSION; ///< Host Controller Interface Version + + __I uint32_t HCSPARAMS; ///< Host Controller Structural Parameters + __I uint32_t HCCPARAMS; ///< Host Controller Capability Parameters + __I uint32_t TU_RESERVED[5]; + + __I uint16_t DCIVERSION; ///< Device Controller Interface Version + __I uint8_t TU_RESERVED[2]; + + __I uint32_t DCCPARAMS; ///< Device Controller Capability Parameters + __I uint32_t TU_RESERVED[6]; + + //------------- Operational Registers -------------// + __IO uint32_t USBCMD; ///< USB Command Register + __IO uint32_t USBSTS; ///< USB Status Register + __IO uint32_t USBINTR; ///< Interrupt Enable Register + __IO uint32_t FRINDEX; ///< USB Frame Index + __I uint32_t TU_RESERVED; + __IO uint32_t DEVICEADDR; ///< Device Address + __IO uint32_t ENDPTLISTADDR; ///< Endpoint List Address + __I uint32_t TU_RESERVED; + __IO uint32_t BURSTSIZE; ///< Programmable Burst Size + __IO uint32_t TXFILLTUNING; ///< TX FIFO Fill Tuning + uint32_t TU_RESERVED[4]; + __IO uint32_t ENDPTNAK; ///< Endpoint NAK + __IO uint32_t ENDPTNAKEN; ///< Endpoint NAK Enable + __I uint32_t TU_RESERVED; + __IO uint32_t PORTSC1; ///< Port Status & Control + __I uint32_t TU_RESERVED[7]; + __IO uint32_t OTGSC; ///< On-The-Go Status & control + __IO uint32_t USBMODE; ///< USB Device Mode + __IO uint32_t ENDPTSETUPSTAT; ///< Endpoint Setup Status + __IO uint32_t ENDPTPRIME; ///< Endpoint Prime + __IO uint32_t ENDPTFLUSH; ///< Endpoint Flush + __I uint32_t ENDPTSTAT; ///< Endpoint Status + __IO uint32_t ENDPTCOMPLETE; ///< Endpoint Complete + __IO uint32_t ENDPTCTRL[8]; ///< Endpoint Control 0 - 7 +} dcd_registers_t; + + +// Queue Transfer Descriptor +typedef struct +{ + // Word 0: Next QTD Pointer + uint32_t next; ///< Next link pointer This field contains the physical memory address of the next dTD to be processed + + // Word 1: qTQ Token + uint32_t : 3 ; + volatile uint32_t xact_err : 1 ; + uint32_t : 1 ; + volatile uint32_t buffer_err : 1 ; + volatile uint32_t halted : 1 ; + volatile uint32_t active : 1 ; + uint32_t : 2 ; + uint32_t iso_mult_override : 2 ; ///< This field can be used for transmit ISOs to override the MULT field in the dQH. This field must be zero for all packet types that are not transmit-ISO. + uint32_t : 3 ; + uint32_t int_on_complete : 1 ; + volatile uint32_t total_bytes : 15 ; + uint32_t : 0 ; + + // Word 2-6: Buffer Page Pointer List, Each element in the list is a 4K page aligned, physical memory address. The lower 12 bits in each pointer are reserved (except for the first one) as each memory pointer must reference the start of a 4K page + uint32_t buffer[5]; ///< buffer1 has frame_n for TODO Isochronous + + //------------- DCD Area -------------// + uint16_t expected_bytes; + uint8_t reserved[2]; +} dcd_qtd_t; + +TU_VERIFY_STATIC( sizeof(dcd_qtd_t) == 32, "size is not correct"); + +// Queue Head +typedef struct +{ + // Word 0: Capabilities and Characteristics + uint32_t : 15 ; ///< Number of packets executed per transaction descriptor 00 - Execute N transactions as demonstrated by the USB variable length protocol where N is computed using Max_packet_length and the Total_bytes field in the dTD. 01 - Execute one transaction 10 - Execute two transactions 11 - Execute three transactions Remark: Non-isochronous endpoints must set MULT = 00. Remark: Isochronous endpoints must set MULT = 01, 10, or 11 as needed. + uint32_t int_on_setup : 1 ; ///< Interrupt on setup This bit is used on control type endpoints to indicate if USBINT is set in response to a setup being received. + uint32_t max_package_size : 11 ; ///< This directly corresponds to the maximum packet size of the associated endpoint (wMaxPacketSize) + uint32_t : 2 ; + uint32_t zero_length_termination : 1 ; ///< This bit is used for non-isochronous endpoints to indicate when a zero-length packet is received to terminate transfers in case the total transfer length is “multiple”. 0 - Enable zero-length packet to terminate transfers equal to a multiple of Max_packet_length (default). 1 - Disable zero-length packet on transfers that are equal in length to a multiple Max_packet_length. + uint32_t iso_mult : 2 ; ///< + uint32_t : 0 ; + + // Word 1: Current qTD Pointer + volatile uint32_t qtd_addr; + + // Word 2-9: Transfer Overlay + volatile dcd_qtd_t qtd_overlay; + + // Word 10-11: Setup request (control OUT only) + volatile tusb_control_request_t setup_request; + + //--------------------------------------------------------------------+ + /// Due to the fact QHD is 64 bytes aligned but occupies only 48 bytes + /// thus there are 16 bytes padding free that we can make use of. + //--------------------------------------------------------------------+ + uint8_t reserved[16]; +} dcd_qhd_t; + +TU_VERIFY_STATIC( sizeof(dcd_qhd_t) == 64, "size is not correct"); + +//--------------------------------------------------------------------+ +// Variables +//--------------------------------------------------------------------+ + +#define QHD_MAX 12 +#define QTD_NEXT_INVALID 0x01 + +typedef struct { + // Must be at 2K alignment + dcd_qhd_t qhd[QHD_MAX] TU_ATTR_ALIGNED(64); + dcd_qtd_t qtd[QHD_MAX] TU_ATTR_ALIGNED(32); +}dcd_data_t; + +static dcd_data_t _dcd_data CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(2048); +static dcd_registers_t* DCD_REGS[] = DCD_REGS_BASE; + +//--------------------------------------------------------------------+ +// CONTROLLER API +//--------------------------------------------------------------------+ + +/// follows LPC43xx User Manual 23.10.3 +static void bus_reset(uint8_t rhport) +{ + dcd_registers_t* dcd_reg = DCD_REGS[rhport]; + + // The reset value for all endpoint types is the control endpoint. If one endpoint + // direction is enabled and the paired endpoint of opposite direction is disabled, then the + // endpoint type of the unused direction must bechanged from the control type to any other + // type (e.g. bulk). Leaving an unconfigured endpoint control will cause undefined behavior + // for the data PID tracking on the active endpoint. + + // USB0 has 5 but USB1 only has 3 non-control endpoints + for( int i=1; i < (rhport ? 6 : 4); i++) + { + dcd_reg->ENDPTCTRL[i] = (TUSB_XFER_BULK << 2) | (TUSB_XFER_BULK << 18); + } + + //------------- Clear All Registers -------------// + dcd_reg->ENDPTNAK = dcd_reg->ENDPTNAK; + dcd_reg->ENDPTNAKEN = 0; + dcd_reg->USBSTS = dcd_reg->USBSTS; + dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT; + dcd_reg->ENDPTCOMPLETE = dcd_reg->ENDPTCOMPLETE; + + while (dcd_reg->ENDPTPRIME); + dcd_reg->ENDPTFLUSH = 0xFFFFFFFF; + while (dcd_reg->ENDPTFLUSH); + + // read reset bit in portsc + + //------------- Queue Head & Queue TD -------------// + tu_memclr(&_dcd_data, sizeof(dcd_data_t)); + + //------------- Set up Control Endpoints (0 OUT, 1 IN) -------------// + _dcd_data.qhd[0].zero_length_termination = _dcd_data.qhd[1].zero_length_termination = 1; + _dcd_data.qhd[0].max_package_size = _dcd_data.qhd[1].max_package_size = CFG_TUD_ENDPOINT0_SIZE; + _dcd_data.qhd[0].qtd_overlay.next = _dcd_data.qhd[1].qtd_overlay.next = QTD_NEXT_INVALID; + + _dcd_data.qhd[0].int_on_setup = 1; // OUT only +} + +void dcd_init(uint8_t rhport) +{ + tu_memclr(&_dcd_data, sizeof(dcd_data_t)); + + dcd_registers_t* const dcd_reg = DCD_REGS[rhport]; + + // Reset controller + dcd_reg->USBCMD |= USBCMD_RESET; + while( dcd_reg->USBCMD & USBCMD_RESET ) {} + + // Set mode to device, must be set immediately after reset + dcd_reg->USBMODE = USBMODE_CM_DEVICE; + dcd_reg->OTGSC = OTGSC_VBUS_DISCHARGE | OTGSC_OTG_TERMINATION; + + // TODO Force fullspeed on non-highspeed port + // dcd_reg->PORTSC1 = PORTSC1_FORCE_FULL_SPEED; + + dcd_reg->ENDPTLISTADDR = (uint32_t) _dcd_data.qhd; // Endpoint List Address has to be 2K alignment + dcd_reg->USBSTS = dcd_reg->USBSTS; + dcd_reg->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_RESET | INTR_SUSPEND | INTR_SOF; + + dcd_reg->USBCMD &= ~0x00FF0000; // Interrupt Threshold Interval = 0 + dcd_reg->USBCMD |= TU_BIT(0); // connect +} + +void dcd_int_enable(uint8_t rhport) +{ + NVIC_EnableIRQ(DCD_IRQn[rhport]); +} + +void dcd_int_disable(uint8_t rhport) +{ + NVIC_DisableIRQ(DCD_IRQn[rhport]); +} + +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) +{ + // Response with status first before changing device address + dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); + + DCD_REGS[rhport]->DEVICEADDR = (dev_addr << 25) | TU_BIT(24); +} + +void dcd_set_config(uint8_t rhport, uint8_t config_num) +{ + (void) rhport; + (void) config_num; + // nothing to do +} + +void dcd_remote_wakeup(uint8_t rhport) +{ + (void) rhport; +} + +//--------------------------------------------------------------------+ +// HELPER +//--------------------------------------------------------------------+ +// index to bit position in register +static inline uint8_t ep_idx2bit(uint8_t ep_idx) +{ + return ep_idx/2 + ( (ep_idx%2) ? 16 : 0); +} + +static void qtd_init(dcd_qtd_t* p_qtd, void * data_ptr, uint16_t total_bytes) +{ + tu_memclr(p_qtd, sizeof(dcd_qtd_t)); + + p_qtd->next = QTD_NEXT_INVALID; + p_qtd->active = 1; + p_qtd->total_bytes = p_qtd->expected_bytes = total_bytes; + + if (data_ptr != NULL) + { + p_qtd->buffer[0] = (uint32_t) data_ptr; + for(uint8_t i=1; i<5; i++) + { + p_qtd->buffer[i] |= tu_align4k( p_qtd->buffer[i-1] ) + 4096; + } + } +} + +//--------------------------------------------------------------------+ +// DCD Endpoint Port +//--------------------------------------------------------------------+ +void dcd_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_REGS[rhport]->ENDPTCTRL[epnum] |= ENDPTCTRL_STALL << (dir ? 16 : 0); +} + +void dcd_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); + + // data toggle also need to be reset + DCD_REGS[rhport]->ENDPTCTRL[epnum] |= ENDPTCTRL_TOGGLE_RESET << ( dir ? 16 : 0 ); + DCD_REGS[rhport]->ENDPTCTRL[epnum] &= ~(ENDPTCTRL_STALL << ( dir ? 16 : 0)); +} + +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) +{ + // TODO not support ISO yet + TU_VERIFY ( p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS); + + uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress); + uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); + uint8_t const ep_idx = 2*epnum + dir; + + // USB0 has 5, USB1 has 3 non-control endpoints + TU_ASSERT( epnum <= (rhport ? 3 : 5) ); + + //------------- Prepare Queue Head -------------// + dcd_qhd_t * p_qhd = &_dcd_data.qhd[ep_idx]; + tu_memclr(p_qhd, sizeof(dcd_qhd_t)); + + p_qhd->zero_length_termination = 1; + p_qhd->max_package_size = p_endpoint_desc->wMaxPacketSize.size; + p_qhd->qtd_overlay.next = QTD_NEXT_INVALID; + + // Enable EP Control + DCD_REGS[rhport]->ENDPTCTRL[epnum] |= ((p_endpoint_desc->bmAttributes.xfer << 2) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET) << (dir ? 16 : 0); + + return true; +} + +bool dcd_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); + uint8_t const ep_idx = 2*epnum + dir; + + if ( epnum == 0 ) + { + // follows UM 24.10.8.1.1 Setup packet handling using setup lockout mechanism + // wait until ENDPTSETUPSTAT before priming data/status in response TODO add time out + while(DCD_REGS[rhport]->ENDPTSETUPSTAT & TU_BIT(0)) {} + } + + dcd_qhd_t * p_qhd = &_dcd_data.qhd[ep_idx]; + dcd_qtd_t * p_qtd = &_dcd_data.qtd[ep_idx]; + + //------------- Prepare qtd -------------// + qtd_init(p_qtd, buffer, total_bytes); + p_qtd->int_on_complete = true; + p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // link qtd to qhd + + // start transfer + DCD_REGS[rhport]->ENDPTPRIME = TU_BIT( ep_idx2bit(ep_idx) ) ; + + return true; +} + +//--------------------------------------------------------------------+ +// ISR +//--------------------------------------------------------------------+ +void dcd_isr(uint8_t rhport) +{ + dcd_registers_t* const dcd_reg = DCD_REGS[rhport]; + + uint32_t const int_enable = dcd_reg->USBINTR; + uint32_t const int_status = dcd_reg->USBSTS & int_enable; + dcd_reg->USBSTS = int_status; // Acknowledge handled interrupt + + // disabled interrupt sources + if (int_status == 0) return; + + if (int_status & INTR_RESET) + { + bus_reset(rhport); + dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, true); + } + + if (int_status & INTR_SUSPEND) + { + if (dcd_reg->PORTSC1 & PORTSC1_SUSPEND) + { + // Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration. + if ((dcd_reg->DEVICEADDR >> 25) & 0x0f) + { + dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); + } + } + } + + // TODO disconnection does not generate interrupt !!!!!! +// if (int_status & INTR_PORT_CHANGE) +// { +// if ( !(dcd_reg->PORTSC1 & PORTSC1_CURRENT_CONNECT_STATUS) ) +// { +// dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_UNPLUGGED }; +// dcd_event_handler(&event, true); +// } +// } + + if (int_status & INTR_USB) + { + uint32_t const edpt_complete = dcd_reg->ENDPTCOMPLETE; + dcd_reg->ENDPTCOMPLETE = edpt_complete; // acknowledge + + if (dcd_reg->ENDPTSETUPSTAT) + { + //------------- Set up Received -------------// + // 23.10.10.2 Operational model for setup transfers + dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT;// acknowledge + + dcd_event_setup_received(rhport, (uint8_t*) &_dcd_data.qhd[0].setup_request, true); + } + + if ( edpt_complete ) + { + for(uint8_t ep_idx = 0; ep_idx < QHD_MAX; ep_idx++) + { + if ( tu_bit_test(edpt_complete, ep_idx2bit(ep_idx)) ) + { + // 23.10.12.3 Failed QTD also get ENDPTCOMPLETE set + dcd_qtd_t * p_qtd = &_dcd_data.qtd[ep_idx]; + + uint8_t result = p_qtd->halted ? XFER_RESULT_STALLED : + ( p_qtd->xact_err ||p_qtd->buffer_err ) ? XFER_RESULT_FAILED : XFER_RESULT_SUCCESS; + + uint8_t const ep_addr = (ep_idx/2) | ( (ep_idx & 0x01) ? TUSB_DIR_IN_MASK : 0 ); + dcd_event_xfer_complete(rhport, ep_addr, p_qtd->expected_bytes - p_qtd->total_bytes, result, true); // only number of bytes in the IOC qtd + } + } + } + } + + if (int_status & INTR_SOF) + { + dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true); + } + + if (int_status & INTR_NAK) {} + if (int_status & INTR_ERROR) TU_ASSERT(false, ); +} + +#endif -- cgit v1.3.1 From 281e8cd9ecd4b324982a7ceaab14fdfa9ce27a1d Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 22 Nov 2019 15:42:46 +0700 Subject: rename OPT_MCU_RT10XX to OPT_MCU_IMXRT10XX --- examples/device/cdc_msc/src/tusb_config.h | 2 +- examples/device/cdc_msc_hid_freertos/src/tusb_config.h | 2 +- examples/device/dfu_rt/src/tusb_config.h | 2 +- examples/device/hid_composite/src/tusb_config.h | 2 +- examples/device/hid_generic_inout/src/tusb_config.h | 2 +- examples/device/midi_test/src/tusb_config.h | 2 +- examples/device/msc_dual_lun/src/tusb_config.h | 2 +- examples/device/usbtmc/src/tusb_config.h | 2 +- examples/device/webusb_serial/src/tusb_config.h | 2 +- examples/host/cdc_msc_hid/src/tusb_config.h | 2 +- hw/bsp/mimxrt1064_evk/board.mk | 2 +- src/portable/nxp/transdimension/dcd_transdimension.c | 4 ++-- src/tusb_option.h | 2 +- test/test/support/tusb_config.h | 2 +- 14 files changed, 15 insertions(+), 15 deletions(-) (limited to 'src/portable') diff --git a/examples/device/cdc_msc/src/tusb_config.h b/examples/device/cdc_msc/src/tusb_config.h index 2b3002bea..0210abdad 100644 --- a/examples/device/cdc_msc/src/tusb_config.h +++ b/examples/device/cdc_msc/src/tusb_config.h @@ -39,7 +39,7 @@ #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_RT10XX +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_IMXRT10XX #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) #else #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE diff --git a/examples/device/cdc_msc_hid_freertos/src/tusb_config.h b/examples/device/cdc_msc_hid_freertos/src/tusb_config.h index 2b55faab0..32c313f18 100644 --- a/examples/device/cdc_msc_hid_freertos/src/tusb_config.h +++ b/examples/device/cdc_msc_hid_freertos/src/tusb_config.h @@ -39,7 +39,7 @@ #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_RT10XX +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_IMXRT10XX #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) #else #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE diff --git a/examples/device/dfu_rt/src/tusb_config.h b/examples/device/dfu_rt/src/tusb_config.h index 4fab17f2f..0ba90226f 100644 --- a/examples/device/dfu_rt/src/tusb_config.h +++ b/examples/device/dfu_rt/src/tusb_config.h @@ -21,7 +21,7 @@ #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_RT10XX +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_IMXRT10XX #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) #else #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE diff --git a/examples/device/hid_composite/src/tusb_config.h b/examples/device/hid_composite/src/tusb_config.h index c17465397..91dee4413 100644 --- a/examples/device/hid_composite/src/tusb_config.h +++ b/examples/device/hid_composite/src/tusb_config.h @@ -39,7 +39,7 @@ #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_RT10XX +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_IMXRT10XX #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) #else #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE diff --git a/examples/device/hid_generic_inout/src/tusb_config.h b/examples/device/hid_generic_inout/src/tusb_config.h index bb292e787..5950f1d99 100644 --- a/examples/device/hid_generic_inout/src/tusb_config.h +++ b/examples/device/hid_generic_inout/src/tusb_config.h @@ -39,7 +39,7 @@ #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_RT10XX +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_IMXRT10XX #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) #else #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE diff --git a/examples/device/midi_test/src/tusb_config.h b/examples/device/midi_test/src/tusb_config.h index 62b4e88fa..1d90a9486 100644 --- a/examples/device/midi_test/src/tusb_config.h +++ b/examples/device/midi_test/src/tusb_config.h @@ -39,7 +39,7 @@ #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_RT10XX +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_IMXRT10XX #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) #else #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE diff --git a/examples/device/msc_dual_lun/src/tusb_config.h b/examples/device/msc_dual_lun/src/tusb_config.h index 85de12797..1025fa468 100644 --- a/examples/device/msc_dual_lun/src/tusb_config.h +++ b/examples/device/msc_dual_lun/src/tusb_config.h @@ -39,7 +39,7 @@ #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_RT10XX +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_IMXRT10XX #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) #else #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE diff --git a/examples/device/usbtmc/src/tusb_config.h b/examples/device/usbtmc/src/tusb_config.h index 4d2f77de3..a3c14d7d3 100644 --- a/examples/device/usbtmc/src/tusb_config.h +++ b/examples/device/usbtmc/src/tusb_config.h @@ -21,7 +21,7 @@ #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_RT10XX +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_IMXRT10XX #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) #else #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE diff --git a/examples/device/webusb_serial/src/tusb_config.h b/examples/device/webusb_serial/src/tusb_config.h index f82ef2b27..92b974002 100644 --- a/examples/device/webusb_serial/src/tusb_config.h +++ b/examples/device/webusb_serial/src/tusb_config.h @@ -39,7 +39,7 @@ #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_RT10XX +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_IMXRT10XX #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) #else #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE diff --git a/examples/host/cdc_msc_hid/src/tusb_config.h b/examples/host/cdc_msc_hid/src/tusb_config.h index ef05da66e..f8a6dc985 100644 --- a/examples/host/cdc_msc_hid/src/tusb_config.h +++ b/examples/host/cdc_msc_hid/src/tusb_config.h @@ -40,7 +40,7 @@ #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_RT10XX +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_IMXRT10XX #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_HOST | OPT_MODE_HIGH_SPEED) #else #define CFG_TUSB_RHPORT0_MODE OPT_MODE_HOST diff --git a/hw/bsp/mimxrt1064_evk/board.mk b/hw/bsp/mimxrt1064_evk/board.mk index cd3c17ae7..94011ec89 100644 --- a/hw/bsp/mimxrt1064_evk/board.mk +++ b/hw/bsp/mimxrt1064_evk/board.mk @@ -8,7 +8,7 @@ CFLAGS += \ -DCPU_MIMXRT1064DVL6A \ -DXIP_EXTERNAL_FLASH=1 \ -DXIP_BOOT_HEADER_ENABLE=1 \ - -DCFG_TUSB_MCU=OPT_MCU_RT10XX \ + -DCFG_TUSB_MCU=OPT_MCU_IMXRT10XX \ -DCFG_TUSB_MEM_SECTION='__attribute__((section(".data")))' \ -DCFG_TUSB_MEM_ALIGN='__attribute__((aligned(64)))' diff --git a/src/portable/nxp/transdimension/dcd_transdimension.c b/src/portable/nxp/transdimension/dcd_transdimension.c index b9527d65d..a17770ce0 100644 --- a/src/portable/nxp/transdimension/dcd_transdimension.c +++ b/src/portable/nxp/transdimension/dcd_transdimension.c @@ -28,7 +28,7 @@ #if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC18XX || \ CFG_TUSB_MCU == OPT_MCU_LPC43XX || \ - CFG_TUSB_MCU == OPT_MCU_RT10XX) + CFG_TUSB_MCU == OPT_MCU_IMXRT10XX) //--------------------------------------------------------------------+ // INCLUDE @@ -36,7 +36,7 @@ #include "common/tusb_common.h" #include "device/dcd.h" -#if CFG_TUSB_MCU == OPT_MCU_RT10XX +#if CFG_TUSB_MCU == OPT_MCU_IMXRT10XX #include "fsl_device_registers.h" #define DCD_REGS_BASE { (dcd_registers_t*) USB1_BASE, (dcd_registers_t*) USB2_BASE } IRQn_Type DCD_IRQn[] = { USB_OTG1_IRQn, USB_OTG2_IRQn }; diff --git a/src/tusb_option.h b/src/tusb_option.h index cc7b46755..d7cbcb2d3 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -72,7 +72,7 @@ #define OPT_MCU_VALENTYUSB_EPTRI 600 ///< Fomu eptri config -#define OPT_MCU_RT10XX 700 ///< NXP iMX RT10xx +#define OPT_MCU_IMXRT10XX 700 ///< NXP iMX RT10xx /** @} */ diff --git a/test/test/support/tusb_config.h b/test/test/support/tusb_config.h index 888ac76a5..9ebd7e43b 100644 --- a/test/test/support/tusb_config.h +++ b/test/test/support/tusb_config.h @@ -43,7 +43,7 @@ #define CFG_TUSB_MCU OPT_MCU_NRF5X #endif -#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_RT10XX +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_IMXRT10XX #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) #else #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE -- cgit v1.3.1 From 38b14725f736c12be1d661dcd32dbb9b2e625a08 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 22 Nov 2019 17:40:12 +0700 Subject: rename OPT_MCU_IMXRT10XX to OPT_MCU_MIMXRT10XX --- examples/device/cdc_msc/src/tusb_config.h | 2 +- examples/device/cdc_msc_hid_freertos/src/tusb_config.h | 2 +- examples/device/dfu_rt/src/tusb_config.h | 2 +- examples/device/hid_composite/src/tusb_config.h | 2 +- examples/device/hid_generic_inout/src/tusb_config.h | 2 +- examples/device/midi_test/src/tusb_config.h | 2 +- examples/device/msc_dual_lun/src/tusb_config.h | 2 +- examples/device/usbtmc/src/tusb_config.h | 2 +- examples/device/webusb_serial/src/tusb_config.h | 2 +- examples/host/cdc_msc_hid/src/tusb_config.h | 2 +- hw/bsp/mimxrt1060_evk/board.mk | 2 +- hw/bsp/mimxrt1064_evk/board.mk | 2 +- src/portable/nxp/transdimension/dcd_transdimension.c | 4 ++-- src/tusb_option.h | 2 +- test/test/support/tusb_config.h | 2 +- 15 files changed, 16 insertions(+), 16 deletions(-) (limited to 'src/portable') diff --git a/examples/device/cdc_msc/src/tusb_config.h b/examples/device/cdc_msc/src/tusb_config.h index 0210abdad..5b7a1671e 100644 --- a/examples/device/cdc_msc/src/tusb_config.h +++ b/examples/device/cdc_msc/src/tusb_config.h @@ -39,7 +39,7 @@ #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_IMXRT10XX +#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 diff --git a/examples/device/cdc_msc_hid_freertos/src/tusb_config.h b/examples/device/cdc_msc_hid_freertos/src/tusb_config.h index 32c313f18..6788a7483 100644 --- a/examples/device/cdc_msc_hid_freertos/src/tusb_config.h +++ b/examples/device/cdc_msc_hid_freertos/src/tusb_config.h @@ -39,7 +39,7 @@ #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_IMXRT10XX +#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 diff --git a/examples/device/dfu_rt/src/tusb_config.h b/examples/device/dfu_rt/src/tusb_config.h index 0ba90226f..27afdb6b6 100644 --- a/examples/device/dfu_rt/src/tusb_config.h +++ b/examples/device/dfu_rt/src/tusb_config.h @@ -21,7 +21,7 @@ #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_IMXRT10XX +#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 diff --git a/examples/device/hid_composite/src/tusb_config.h b/examples/device/hid_composite/src/tusb_config.h index 91dee4413..dd360741d 100644 --- a/examples/device/hid_composite/src/tusb_config.h +++ b/examples/device/hid_composite/src/tusb_config.h @@ -39,7 +39,7 @@ #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_IMXRT10XX +#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 diff --git a/examples/device/hid_generic_inout/src/tusb_config.h b/examples/device/hid_generic_inout/src/tusb_config.h index 5950f1d99..0053a7d0a 100644 --- a/examples/device/hid_generic_inout/src/tusb_config.h +++ b/examples/device/hid_generic_inout/src/tusb_config.h @@ -39,7 +39,7 @@ #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_IMXRT10XX +#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 diff --git a/examples/device/midi_test/src/tusb_config.h b/examples/device/midi_test/src/tusb_config.h index 1d90a9486..5837db4eb 100644 --- a/examples/device/midi_test/src/tusb_config.h +++ b/examples/device/midi_test/src/tusb_config.h @@ -39,7 +39,7 @@ #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_IMXRT10XX +#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 diff --git a/examples/device/msc_dual_lun/src/tusb_config.h b/examples/device/msc_dual_lun/src/tusb_config.h index 1025fa468..baf857b71 100644 --- a/examples/device/msc_dual_lun/src/tusb_config.h +++ b/examples/device/msc_dual_lun/src/tusb_config.h @@ -39,7 +39,7 @@ #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_IMXRT10XX +#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 diff --git a/examples/device/usbtmc/src/tusb_config.h b/examples/device/usbtmc/src/tusb_config.h index a3c14d7d3..2932ea4d1 100644 --- a/examples/device/usbtmc/src/tusb_config.h +++ b/examples/device/usbtmc/src/tusb_config.h @@ -21,7 +21,7 @@ #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_IMXRT10XX +#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 diff --git a/examples/device/webusb_serial/src/tusb_config.h b/examples/device/webusb_serial/src/tusb_config.h index 92b974002..30acdaccf 100644 --- a/examples/device/webusb_serial/src/tusb_config.h +++ b/examples/device/webusb_serial/src/tusb_config.h @@ -39,7 +39,7 @@ #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_IMXRT10XX +#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 diff --git a/examples/host/cdc_msc_hid/src/tusb_config.h b/examples/host/cdc_msc_hid/src/tusb_config.h index f8a6dc985..7c1818109 100644 --- a/examples/host/cdc_msc_hid/src/tusb_config.h +++ b/examples/host/cdc_msc_hid/src/tusb_config.h @@ -40,7 +40,7 @@ #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_IMXRT10XX +#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_HOST | OPT_MODE_HIGH_SPEED) #else #define CFG_TUSB_RHPORT0_MODE OPT_MODE_HOST diff --git a/hw/bsp/mimxrt1060_evk/board.mk b/hw/bsp/mimxrt1060_evk/board.mk index e81e41c11..89f5a0f3e 100644 --- a/hw/bsp/mimxrt1060_evk/board.mk +++ b/hw/bsp/mimxrt1060_evk/board.mk @@ -8,7 +8,7 @@ CFLAGS += \ -DCPU_MIMXRT1062DVL6A \ -DXIP_EXTERNAL_FLASH=1 \ -DXIP_BOOT_HEADER_ENABLE=1 \ - -DCFG_TUSB_MCU=OPT_MCU_IMXRT10XX \ + -DCFG_TUSB_MCU=OPT_MCU_MIMXRT10XX \ -DCFG_TUSB_MEM_SECTION='__attribute__((section(".data")))' \ -DCFG_TUSB_MEM_ALIGN='__attribute__((aligned(64)))' diff --git a/hw/bsp/mimxrt1064_evk/board.mk b/hw/bsp/mimxrt1064_evk/board.mk index 94011ec89..d991168ef 100644 --- a/hw/bsp/mimxrt1064_evk/board.mk +++ b/hw/bsp/mimxrt1064_evk/board.mk @@ -8,7 +8,7 @@ CFLAGS += \ -DCPU_MIMXRT1064DVL6A \ -DXIP_EXTERNAL_FLASH=1 \ -DXIP_BOOT_HEADER_ENABLE=1 \ - -DCFG_TUSB_MCU=OPT_MCU_IMXRT10XX \ + -DCFG_TUSB_MCU=OPT_MCU_MIMXRT10XX \ -DCFG_TUSB_MEM_SECTION='__attribute__((section(".data")))' \ -DCFG_TUSB_MEM_ALIGN='__attribute__((aligned(64)))' diff --git a/src/portable/nxp/transdimension/dcd_transdimension.c b/src/portable/nxp/transdimension/dcd_transdimension.c index a17770ce0..cc85bb62c 100644 --- a/src/portable/nxp/transdimension/dcd_transdimension.c +++ b/src/portable/nxp/transdimension/dcd_transdimension.c @@ -28,7 +28,7 @@ #if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC18XX || \ CFG_TUSB_MCU == OPT_MCU_LPC43XX || \ - CFG_TUSB_MCU == OPT_MCU_IMXRT10XX) + CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX) //--------------------------------------------------------------------+ // INCLUDE @@ -36,7 +36,7 @@ #include "common/tusb_common.h" #include "device/dcd.h" -#if CFG_TUSB_MCU == OPT_MCU_IMXRT10XX +#if CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX #include "fsl_device_registers.h" #define DCD_REGS_BASE { (dcd_registers_t*) USB1_BASE, (dcd_registers_t*) USB2_BASE } IRQn_Type DCD_IRQn[] = { USB_OTG1_IRQn, USB_OTG2_IRQn }; diff --git a/src/tusb_option.h b/src/tusb_option.h index d7cbcb2d3..ae8c02eeb 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -72,7 +72,7 @@ #define OPT_MCU_VALENTYUSB_EPTRI 600 ///< Fomu eptri config -#define OPT_MCU_IMXRT10XX 700 ///< NXP iMX RT10xx +#define OPT_MCU_MIMXRT10XX 700 ///< NXP iMX RT10xx /** @} */ diff --git a/test/test/support/tusb_config.h b/test/test/support/tusb_config.h index 9ebd7e43b..5e7b70e0d 100644 --- a/test/test/support/tusb_config.h +++ b/test/test/support/tusb_config.h @@ -43,7 +43,7 @@ #define CFG_TUSB_MCU OPT_MCU_NRF5X #endif -#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_IMXRT10XX +#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 -- cgit v1.3.1 From d991466a8e45d03a8c95da894b2992b1b8a7d0b0 Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 23 Nov 2019 00:48:56 +0700 Subject: fix rt1010 rt1020 only has 1 usb controller, rt1050 rt1060 has 2 --- hw/bsp/mimxrt1020_evk/board.h | 34 +++ hw/bsp/mimxrt1020_evk/board.mk | 55 +++++ .../evkmimxrt1020_flexspi_nor_config.c | 49 ++++ .../evkmimxrt1020_flexspi_nor_config.h | 268 +++++++++++++++++++++ hw/bsp/mimxrt1020_evk/mimxrt1020_evk.c | 169 +++++++++++++ .../nxp/transdimension/dcd_transdimension.c | 9 + 6 files changed, 584 insertions(+) create mode 100644 hw/bsp/mimxrt1020_evk/board.h create mode 100644 hw/bsp/mimxrt1020_evk/board.mk create mode 100644 hw/bsp/mimxrt1020_evk/evkmimxrt1020_flexspi_nor_config.c create mode 100644 hw/bsp/mimxrt1020_evk/evkmimxrt1020_flexspi_nor_config.h create mode 100644 hw/bsp/mimxrt1020_evk/mimxrt1020_evk.c (limited to 'src/portable') diff --git a/hw/bsp/mimxrt1020_evk/board.h b/hw/bsp/mimxrt1020_evk/board.h new file mode 100644 index 000000000..f1bd06369 --- /dev/null +++ b/hw/bsp/mimxrt1020_evk/board.h @@ -0,0 +1,34 @@ +/* + * 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. + */ + + +#ifndef BOARD_H_ +#define BOARD_H_ + +// required since iMX RT10xx SDK include this file for board size +#define BOARD_FLASH_SIZE (0x800000U) + +#endif /* BOARD_H_ */ diff --git a/hw/bsp/mimxrt1020_evk/board.mk b/hw/bsp/mimxrt1020_evk/board.mk new file mode 100644 index 000000000..538626e99 --- /dev/null +++ b/hw/bsp/mimxrt1020_evk/board.mk @@ -0,0 +1,55 @@ +CFLAGS += \ + -mthumb \ + -mabi=aapcs \ + -mcpu=cortex-m7 \ + -mfloat-abi=hard \ + -mfpu=fpv5-d16 \ + -D__ARMVFP__=0 -D__ARMFPV5__=0\ + -DCPU_MIMXRT1021DAG5A \ + -DXIP_EXTERNAL_FLASH=1 \ + -DXIP_BOOT_HEADER_ENABLE=1 \ + -DCFG_TUSB_MCU=OPT_MCU_MIMXRT10XX \ + -DCFG_TUSB_MEM_SECTION='__attribute__((section(".data")))' \ + -DCFG_TUSB_MEM_ALIGN='__attribute__((aligned(64)))' + +# mcu driver cause following warnings +#CFLAGS += -Wno-error=float-equal -Wno-error=nested-externs +CFLAGS += -Wno-error=unused-parameter + +MCU_DIR = hw/mcu/nxp/sdk/devices/MIMXRT1021 + +# All source paths should be relative to the top level. +LD_FILE = $(MCU_DIR)/gcc/MIMXRT1021xxxxx_flexspi_nor.ld + +SRC_C += \ + $(MCU_DIR)/system_MIMXRT1021.c \ + $(MCU_DIR)/xip/fsl_flexspi_nor_boot.c \ + $(MCU_DIR)/project_template/clock_config.c \ + $(MCU_DIR)/drivers/fsl_clock.c \ + $(MCU_DIR)/drivers/fsl_gpio.c \ + $(MCU_DIR)/drivers/fsl_common.c \ + $(MCU_DIR)/drivers/fsl_lpuart.c + +INC += \ + $(TOP)/hw/bsp/$(BOARD) \ + $(TOP)/$(MCU_DIR)/../../CMSIS/Include \ + $(TOP)/$(MCU_DIR) \ + $(TOP)/$(MCU_DIR)/drivers \ + $(TOP)/$(MCU_DIR)/project_template \ + +SRC_S += $(MCU_DIR)/gcc/startup_MIMXRT1021.S + +# For TinyUSB port source +VENDOR = nxp +CHIP_FAMILY = transdimension + +# For freeRTOS port source +FREERTOS_PORT = ARM_CM7 + +# For flash-jlink target +JLINK_DEVICE = CPU_MIMXRT1021DAG5A +JLINK_IF = swd + +# flash by copying bin file to DAP Mass Storage +flash: $(BUILD)/$(BOARD)-firmware.bin + cp $< /media/$(USER)/RT1020-EVK/ diff --git a/hw/bsp/mimxrt1020_evk/evkmimxrt1020_flexspi_nor_config.c b/hw/bsp/mimxrt1020_evk/evkmimxrt1020_flexspi_nor_config.c new file mode 100644 index 000000000..6bafe195f --- /dev/null +++ b/hw/bsp/mimxrt1020_evk/evkmimxrt1020_flexspi_nor_config.c @@ -0,0 +1,49 @@ +/* + * Copyright 2018 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "evkmimxrt1020_flexspi_nor_config.h" + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.xip_board" +#endif + +/******************************************************************************* + * Code + ******************************************************************************/ +#if defined(XIP_BOOT_HEADER_ENABLE) && (XIP_BOOT_HEADER_ENABLE == 1) +#if defined(__CC_ARM) || defined(__ARMCC_VERSION) || defined(__GNUC__) +__attribute__((section(".boot_hdr.conf"))) +#elif defined(__ICCARM__) +#pragma location = ".boot_hdr.conf" +#endif + +const flexspi_nor_config_t qspiflash_config = { + .memConfig = + { + .tag = FLEXSPI_CFG_BLK_TAG, + .version = FLEXSPI_CFG_BLK_VERSION, + .readSampleClkSrc = kFlexSPIReadSampleClk_LoopbackFromDqsPad, + .csHoldTime = 3u, + .csSetupTime = 3u, + // Enable DDR mode, Wordaddassable, Safe configuration, Differential clock + .sflashPadType = kSerialFlash_4Pads, + .serialClkFreq = kFlexSpiSerialClk_100MHz, + .sflashA1Size = 8u * 1024u * 1024u, + .lookupTable = + { + // Read LUTs + FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, 0x18), + FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_4PAD, 0x06, READ_SDR, FLEXSPI_4PAD, 0x04), + }, + }, + .pageSize = 256u, + .sectorSize = 4u * 1024u, + .blockSize = 256u * 1024u, + .isUniformBlockSize = false, +}; +#endif /* XIP_BOOT_HEADER_ENABLE */ diff --git a/hw/bsp/mimxrt1020_evk/evkmimxrt1020_flexspi_nor_config.h b/hw/bsp/mimxrt1020_evk/evkmimxrt1020_flexspi_nor_config.h new file mode 100644 index 000000000..f5e1aca5c --- /dev/null +++ b/hw/bsp/mimxrt1020_evk/evkmimxrt1020_flexspi_nor_config.h @@ -0,0 +1,268 @@ +/* + * Copyright 2018 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __EVKMIMXRT1020_FLEXSPI_NOR_CONFIG__ +#define __EVKMIMXRT1020_FLEXSPI_NOR_CONFIG__ + +#include +#include +#include "fsl_common.h" + +/*! @name Driver version */ +/*@{*/ +/*! @brief XIP_BOARD driver version 2.0.0. */ +#define FSL_XIP_BOARD_DRIVER_VERSION (MAKE_VERSION(2, 0, 0)) +/*@}*/ + +/* FLEXSPI memory config block related defintions */ +#define FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian +#define FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0 +#define FLEXSPI_CFG_BLK_SIZE (512) + +/* FLEXSPI Feature related definitions */ +#define FLEXSPI_FEATURE_HAS_PARALLEL_MODE 1 + +/* Lookup table related defintions */ +#define CMD_INDEX_READ 0 +#define CMD_INDEX_READSTATUS 1 +#define CMD_INDEX_WRITEENABLE 2 +#define CMD_INDEX_WRITE 4 + +#define CMD_LUT_SEQ_IDX_READ 0 +#define CMD_LUT_SEQ_IDX_READSTATUS 1 +#define CMD_LUT_SEQ_IDX_WRITEENABLE 3 +#define CMD_LUT_SEQ_IDX_WRITE 9 + +#define CMD_SDR 0x01 +#define CMD_DDR 0x21 +#define RADDR_SDR 0x02 +#define RADDR_DDR 0x22 +#define CADDR_SDR 0x03 +#define CADDR_DDR 0x23 +#define MODE1_SDR 0x04 +#define MODE1_DDR 0x24 +#define MODE2_SDR 0x05 +#define MODE2_DDR 0x25 +#define MODE4_SDR 0x06 +#define MODE4_DDR 0x26 +#define MODE8_SDR 0x07 +#define MODE8_DDR 0x27 +#define WRITE_SDR 0x08 +#define WRITE_DDR 0x28 +#define READ_SDR 0x09 +#define READ_DDR 0x29 +#define LEARN_SDR 0x0A +#define LEARN_DDR 0x2A +#define DATSZ_SDR 0x0B +#define DATSZ_DDR 0x2B +#define DUMMY_SDR 0x0C +#define DUMMY_DDR 0x2C +#define DUMMY_RWDS_SDR 0x0D +#define DUMMY_RWDS_DDR 0x2D +#define JMP_ON_CS 0x1F +#define STOP 0 + +#define FLEXSPI_1PAD 0 +#define FLEXSPI_2PAD 1 +#define FLEXSPI_4PAD 2 +#define FLEXSPI_8PAD 3 + +#define FLEXSPI_LUT_SEQ(cmd0, pad0, op0, cmd1, pad1, op1) \ + (FLEXSPI_LUT_OPERAND0(op0) | FLEXSPI_LUT_NUM_PADS0(pad0) | FLEXSPI_LUT_OPCODE0(cmd0) | FLEXSPI_LUT_OPERAND1(op1) | \ + FLEXSPI_LUT_NUM_PADS1(pad1) | FLEXSPI_LUT_OPCODE1(cmd1)) + +//!@brief Definitions for FlexSPI Serial Clock Frequency +typedef enum _FlexSpiSerialClockFreq +{ + kFlexSpiSerialClk_30MHz = 1, + kFlexSpiSerialClk_50MHz = 2, + kFlexSpiSerialClk_60MHz = 3, + kFlexSpiSerialClk_75MHz = 4, + kFlexSpiSerialClk_80MHz = 5, + kFlexSpiSerialClk_100MHz = 6, + kFlexSpiSerialClk_133MHz = 7, + kFlexSpiSerialClk_166MHz = 8, + kFlexSpiSerialClk_200MHz = 9, +} flexspi_serial_clk_freq_t; + +//!@brief FlexSPI clock configuration type +enum +{ + kFlexSpiClk_SDR, //!< Clock configure for SDR mode + kFlexSpiClk_DDR, //!< Clock configurat for DDR mode +}; + +//!@brief FlexSPI Read Sample Clock Source definition +typedef enum _FlashReadSampleClkSource +{ + kFlexSPIReadSampleClk_LoopbackInternally = 0, + kFlexSPIReadSampleClk_LoopbackFromDqsPad = 1, + kFlexSPIReadSampleClk_LoopbackFromSckPad = 2, + kFlexSPIReadSampleClk_ExternalInputFromDqsPad = 3, +} flexspi_read_sample_clk_t; + +//!@brief Misc feature bit definitions +enum +{ + kFlexSpiMiscOffset_DiffClkEnable = 0, //!< Bit for Differential clock enable + kFlexSpiMiscOffset_Ck2Enable = 1, //!< Bit for CK2 enable + kFlexSpiMiscOffset_ParallelEnable = 2, //!< Bit for Parallel mode enable + kFlexSpiMiscOffset_WordAddressableEnable = 3, //!< Bit for Word Addressable enable + kFlexSpiMiscOffset_SafeConfigFreqEnable = 4, //!< Bit for Safe Configuration Frequency enable + kFlexSpiMiscOffset_PadSettingOverrideEnable = 5, //!< Bit for Pad setting override enable + kFlexSpiMiscOffset_DdrModeEnable = 6, //!< Bit for DDR clock confiuration indication. +}; + +//!@brief Flash Type Definition +enum +{ + kFlexSpiDeviceType_SerialNOR = 1, //!< Flash devices are Serial NOR + kFlexSpiDeviceType_SerialNAND = 2, //!< Flash devices are Serial NAND + kFlexSpiDeviceType_SerialRAM = 3, //!< Flash devices are Serial RAM/HyperFLASH + kFlexSpiDeviceType_MCP_NOR_NAND = 0x12, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial NAND + kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash deivce is MCP device, A1 is Serial NOR, A2 is Serial RAMs +}; + +//!@brief Flash Pad Definitions +enum +{ + kSerialFlash_1Pad = 1, + kSerialFlash_2Pads = 2, + kSerialFlash_4Pads = 4, + kSerialFlash_8Pads = 8, +}; + +//!@brief FlexSPI LUT Sequence structure +typedef struct _lut_sequence +{ + uint8_t seqNum; //!< Sequence Number, valid number: 1-16 + uint8_t seqId; //!< Sequence Index, valid number: 0-15 + uint16_t reserved; +} flexspi_lut_seq_t; + +//!@brief Flash Configuration Command Type +enum +{ + kDeviceConfigCmdType_Generic, //!< Generic command, for example: configure dummy cycles, drive strength, etc + kDeviceConfigCmdType_QuadEnable, //!< Quad Enable command + kDeviceConfigCmdType_Spi2Xpi, //!< Switch from SPI to DPI/QPI/OPI mode + kDeviceConfigCmdType_Xpi2Spi, //!< Switch from DPI/QPI/OPI to SPI mode + kDeviceConfigCmdType_Spi2NoCmd, //!< Switch to 0-4-4/0-8-8 mode + kDeviceConfigCmdType_Reset, //!< Reset device command +}; + +//!@brief FlexSPI Memory Configuration Block +typedef struct _FlexSPIConfig +{ + uint32_t tag; //!< [0x000-0x003] Tag, fixed value 0x42464346UL + uint32_t version; //!< [0x004-0x007] Version,[31:24] -'V', [23:16] - Major, [15:8] - Minor, [7:0] - bugfix + uint32_t reserved0; //!< [0x008-0x00b] Reserved for future use + uint8_t readSampleClkSrc; //!< [0x00c-0x00c] Read Sample Clock Source, valid value: 0/1/3 + uint8_t csHoldTime; //!< [0x00d-0x00d] CS hold time, default value: 3 + uint8_t csSetupTime; //!< [0x00e-0x00e] CS setup time, default value: 3 + uint8_t columnAddressWidth; //!< [0x00f-0x00f] Column Address with, for HyperBus protocol, it is fixed to 3, For + //! Serial NAND, need to refer to datasheet + uint8_t deviceModeCfgEnable; //!< [0x010-0x010] Device Mode Configure enable flag, 1 - Enable, 0 - Disable + uint8_t deviceModeType; //!< [0x011-0x011] Specify the configuration command type:Quad Enable, DPI/QPI/OPI switch, + //! Generic configuration, etc. + uint16_t waitTimeCfgCommands; //!< [0x012-0x013] Wait time for all configuration commands, unit: 100us, Used for + //! DPI/QPI/OPI switch or reset command + flexspi_lut_seq_t deviceModeSeq; //!< [0x014-0x017] Device mode sequence info, [7:0] - LUT sequence id, [15:8] - LUt + //! sequence number, [31:16] Reserved + uint32_t deviceModeArg; //!< [0x018-0x01b] Argument/Parameter for device configuration + uint8_t configCmdEnable; //!< [0x01c-0x01c] Configure command Enable Flag, 1 - Enable, 0 - Disable + uint8_t configModeType[3]; //!< [0x01d-0x01f] Configure Mode Type, similar as deviceModeTpe + flexspi_lut_seq_t + configCmdSeqs[3]; //!< [0x020-0x02b] Sequence info for Device Configuration command, similar as deviceModeSeq + uint32_t reserved1; //!< [0x02c-0x02f] Reserved for future use + uint32_t configCmdArgs[3]; //!< [0x030-0x03b] Arguments/Parameters for device Configuration commands + uint32_t reserved2; //!< [0x03c-0x03f] Reserved for future use + uint32_t controllerMiscOption; //!< [0x040-0x043] Controller Misc Options, see Misc feature bit definitions for more + //! details + uint8_t deviceType; //!< [0x044-0x044] Device Type: See Flash Type Definition for more details + uint8_t sflashPadType; //!< [0x045-0x045] Serial Flash Pad Type: 1 - Single, 2 - Dual, 4 - Quad, 8 - Octal + uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequencey, device specific definitions, See System Boot + //! Chapter for more details + uint8_t lutCustomSeqEnable; //!< [0x047-0x047] LUT customization Enable, it is required if the program/erase cannot + //! be done using 1 LUT sequence, currently, only applicable to HyperFLASH + uint32_t reserved3[2]; //!< [0x048-0x04f] Reserved for future use + uint32_t sflashA1Size; //!< [0x050-0x053] Size of Flash connected to A1 + uint32_t sflashA2Size; //!< [0x054-0x057] Size of Flash connected to A2 + uint32_t sflashB1Size; //!< [0x058-0x05b] Size of Flash connected to B1 + uint32_t sflashB2Size; //!< [0x05c-0x05f] Size of Flash connected to B2 + uint32_t csPadSettingOverride; //!< [0x060-0x063] CS pad setting override value + uint32_t sclkPadSettingOverride; //!< [0x064-0x067] SCK pad setting override value + uint32_t dataPadSettingOverride; //!< [0x068-0x06b] data pad setting override value + uint32_t dqsPadSettingOverride; //!< [0x06c-0x06f] DQS pad setting override value + uint32_t timeoutInMs; //!< [0x070-0x073] Timeout threshold for read status command + uint32_t commandInterval; //!< [0x074-0x077] CS deselect interval between two commands + uint16_t dataValidTime[2]; //!< [0x078-0x07b] CLK edge to data valid time for PORT A and PORT B, in terms of 0.1ns + uint16_t busyOffset; //!< [0x07c-0x07d] Busy offset, valid value: 0-31 + uint16_t busyBitPolarity; //!< [0x07e-0x07f] Busy flag polarity, 0 - busy flag is 1 when flash device is busy, 1 - + //! busy flag is 0 when flash device is busy + uint32_t lookupTable[64]; //!< [0x080-0x17f] Lookup table holds Flash command sequences + flexspi_lut_seq_t lutCustomSeq[12]; //!< [0x180-0x1af] Customizable LUT Sequences + uint32_t reserved4[4]; //!< [0x1b0-0x1bf] Reserved for future use +} flexspi_mem_config_t; + +/* */ +#define NOR_CMD_INDEX_READ CMD_INDEX_READ //!< 0 +#define NOR_CMD_INDEX_READSTATUS CMD_INDEX_READSTATUS //!< 1 +#define NOR_CMD_INDEX_WRITEENABLE CMD_INDEX_WRITEENABLE //!< 2 +#define NOR_CMD_INDEX_ERASESECTOR 3 //!< 3 +#define NOR_CMD_INDEX_PAGEPROGRAM CMD_INDEX_WRITE //!< 4 +#define NOR_CMD_INDEX_CHIPERASE 5 //!< 5 +#define NOR_CMD_INDEX_DUMMY 6 //!< 6 +#define NOR_CMD_INDEX_ERASEBLOCK 7 //!< 7 + +#define NOR_CMD_LUT_SEQ_IDX_READ CMD_LUT_SEQ_IDX_READ //!< 0 READ LUT sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READSTATUS \ + CMD_LUT_SEQ_IDX_READSTATUS //!< 1 Read Status LUT sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READSTATUS_XPI \ + 2 //!< 2 Read status DPI/QPI/OPI sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE \ + CMD_LUT_SEQ_IDX_WRITEENABLE //!< 3 Write Enable sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE_XPI \ + 4 //!< 4 Write Enable DPI/QPI/OPI sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_ERASESECTOR 5 //!< 5 Erase Sector sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_ERASEBLOCK 8 //!< 8 Erase Block sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM \ + CMD_LUT_SEQ_IDX_WRITE //!< 9 Program sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_CHIPERASE 11 //!< 11 Chip Erase sequence in lookupTable id stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READ_SFDP 13 //!< 13 Read SFDP sequence in lookupTable id stored in config block +#define NOR_CMD_LUT_SEQ_IDX_RESTORE_NOCMD \ + 14 //!< 14 Restore 0-4-4/0-8-8 mode sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_EXIT_NOCMD \ + 15 //!< 15 Exit 0-4-4/0-8-8 mode sequence id in lookupTable stored in config blobk + +/* + * Serial NOR configuration block + */ +typedef struct _flexspi_nor_config +{ + flexspi_mem_config_t memConfig; //!< Common memory configuration info via FlexSPI + uint32_t pageSize; //!< Page size of Serial NOR + uint32_t sectorSize; //!< Sector size of Serial NOR + uint8_t ipcmdSerialClkFreq; //!< Clock frequency for IP command + uint8_t isUniformBlockSize; //!< Sector/Block size is the same + uint8_t reserved0[2]; //!< Reserved for future use + uint8_t serialNorType; //!< Serial NOR Flash type: 0/1/2/3 + uint8_t needExitNoCmdMode; //!< Need to exit NoCmd mode before other IP command + uint8_t halfClkForNonReadCmd; //!< Half the Serial Clock for non-read command: true/false + uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP commmand execution + uint32_t blockSize; //!< Block size + uint32_t reserve2[11]; //!< Reserved for future use +} flexspi_nor_config_t; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif +#endif /* __EVKMIMXRT1020_FLEXSPI_NOR_CONFIG__ */ diff --git a/hw/bsp/mimxrt1020_evk/mimxrt1020_evk.c b/hw/bsp/mimxrt1020_evk/mimxrt1020_evk.c new file mode 100644 index 000000000..708397f7f --- /dev/null +++ b/hw/bsp/mimxrt1020_evk/mimxrt1020_evk.c @@ -0,0 +1,169 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018, hathach (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 "../board.h" +#include "fsl_device_registers.h" +#include "fsl_gpio.h" +#include "fsl_iomuxc.h" +#include "fsl_clock.h" +#include "fsl_lpuart.h" + +#include "clock_config.h" + +#define LED_PINMUX IOMUXC_GPIO_AD_B0_05_GPIO1_IO05 +#define LED_PORT GPIO1 +#define LED_PIN 5 +#define LED_STATE_ON 0 + +// SW8 button +#define BUTTON_PINMUX IOMUXC_SNVS_WAKEUP_GPIO5_IO00 +#define BUTTON_PORT GPIO5 +#define BUTTON_PIN 0 +#define BUTTON_STATE_ACTIVE 0 + +// UART +#define UART_PORT LPUART1 +#define UART_RX_PINMUX IOMUXC_GPIO_AD_B0_07_LPUART1_RX +#define UART_TX_PINMUX IOMUXC_GPIO_AD_B0_06_LPUART1_TX + +const uint8_t dcd_data[] = { 0x00 }; + +void board_init(void) +{ + // Init clock + BOARD_BootClockRUN(); + SystemCoreClockUpdate(); + + // Enable IOCON clock + CLOCK_EnableClock(kCLOCK_Iomuxc); + +#if CFG_TUSB_OS == OPT_OS_NONE + // 1ms tick timer + SysTick_Config(SystemCoreClock / 1000); +#elif CFG_TUSB_OS == OPT_OS_FREERTOS + // If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher ) +// NVIC_SetPriority(USB0_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY ); +#endif + + // LED + IOMUXC_SetPinMux( LED_PINMUX, 0U); + IOMUXC_SetPinConfig( LED_PINMUX, 0x10B0U); + + gpio_pin_config_t led_config = { kGPIO_DigitalOutput, 0, kGPIO_NoIntmode }; + GPIO_PinInit(LED_PORT, LED_PIN, &led_config); + board_led_write(true); + + // Button + IOMUXC_SetPinMux( BUTTON_PINMUX, 0U); + gpio_pin_config_t button_config = { kGPIO_DigitalInput, 0, kGPIO_IntRisingEdge, }; + GPIO_PinInit(BUTTON_PORT, BUTTON_PIN, &button_config); + + // UART + IOMUXC_SetPinMux( UART_TX_PINMUX, 0U); + IOMUXC_SetPinMux( UART_RX_PINMUX, 0U); + IOMUXC_SetPinConfig( UART_TX_PINMUX, 0x10B0u); + IOMUXC_SetPinConfig( UART_RX_PINMUX, 0x10B0u); + + lpuart_config_t uart_config; + LPUART_GetDefaultConfig(&uart_config); + uart_config.baudRate_Bps = CFG_BOARD_UART_BAUDRATE; + uart_config.enableTx = true; + uart_config.enableRx = true; + LPUART_Init(UART_PORT, &uart_config, (CLOCK_GetPllFreq(kCLOCK_PllUsb1) / 6U) / (CLOCK_GetDiv(kCLOCK_UartDiv) + 1U)); + + //------------- USB0 -------------// + // Clock + CLOCK_EnableUsbhs0PhyPllClock(kCLOCK_Usbphy480M, 480000000U); + CLOCK_EnableUsbhs0Clock(kCLOCK_Usb480M, 480000000U); + + USBPHY_Type* usb_phy = USBPHY; + + // Enable PHY support for Low speed device + LS via FS Hub + usb_phy->CTRL |= USBPHY_CTRL_SET_ENUTMILEVEL2_MASK | USBPHY_CTRL_SET_ENUTMILEVEL3_MASK; + + // Enable all power for normal operation + usb_phy->PWD = 0; + + // TX Timing + uint32_t phytx = usb_phy->TX; + phytx &= ~(USBPHY_TX_D_CAL_MASK | USBPHY_TX_TXCAL45DM_MASK | USBPHY_TX_TXCAL45DP_MASK); + phytx |= USBPHY_TX_D_CAL(0x0C) | USBPHY_TX_TXCAL45DP(0x06) | USBPHY_TX_TXCAL45DM(0x06); + usb_phy->TX = phytx; +} + +//--------------------------------------------------------------------+ +// USB Interrupt Handler +//--------------------------------------------------------------------+ +void USB_OTG1_IRQHandler(void) +{ + #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST + tuh_isr(0); + #endif + + #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE + tud_isr(0); + #endif +} + +//--------------------------------------------------------------------+ +// Board porting API +//--------------------------------------------------------------------+ + +void board_led_write(bool state) +{ + GPIO_PinWrite(LED_PORT, LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON)); +} + +uint32_t board_button_read(void) +{ + // active low + return BUTTON_STATE_ACTIVE == GPIO_PinRead(BUTTON_PORT, BUTTON_PIN); +} + +int board_uart_read(uint8_t* buf, int len) +{ + LPUART_ReadBlocking(UART_PORT, buf, len); + return len; +} + +int board_uart_write(void const * buf, int len) +{ + LPUART_WriteBlocking(UART_PORT, (uint8_t*)buf, len); + return len; +} + +#if CFG_TUSB_OS == OPT_OS_NONE +volatile uint32_t system_ticks = 0; +void SysTick_Handler(void) +{ + system_ticks++; +} + +uint32_t board_millis(void) +{ + return system_ticks; +} +#endif diff --git a/src/portable/nxp/transdimension/dcd_transdimension.c b/src/portable/nxp/transdimension/dcd_transdimension.c index cc85bb62c..2ec7ae51c 100644 --- a/src/portable/nxp/transdimension/dcd_transdimension.c +++ b/src/portable/nxp/transdimension/dcd_transdimension.c @@ -38,8 +38,17 @@ #if CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX #include "fsl_device_registers.h" + +// RT1010 and RT1020 only has 1 USB controller +#if FSL_FEATURE_SOC_USBHS_COUNT == 1 + #define DCD_REGS_BASE { (dcd_registers_t*) USB_BASE } + IRQn_Type DCD_IRQn[] = { USB_OTG1_IRQn }; + +// RT1050, RT1060 has 2 USB controllers +#else #define DCD_REGS_BASE { (dcd_registers_t*) USB1_BASE, (dcd_registers_t*) USB2_BASE } IRQn_Type DCD_IRQn[] = { USB_OTG1_IRQn, USB_OTG2_IRQn }; +#endif #else #include "chip.h" -- cgit v1.3.1 From 58e8265964ef8260851465b56c1a3d48445ec04d Mon Sep 17 00:00:00 2001 From: hathach Date: Sun, 24 Nov 2019 23:46:28 +0700 Subject: usb hw init OK --- hw/bsp/samg55xplained/board.mk | 6 +- hw/bsp/samg55xplained/samg55xplained.c | 8 +++ src/portable/microchip/samg/dcd_samg.c | 116 +++++++++++++++++++++++++++++++++ src/tusb_option.h | 1 + 4 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 src/portable/microchip/samg/dcd_samg.c (limited to 'src/portable') diff --git a/hw/bsp/samg55xplained/board.mk b/hw/bsp/samg55xplained/board.mk index 785594505..eff773e04 100644 --- a/hw/bsp/samg55xplained/board.mk +++ b/hw/bsp/samg55xplained/board.mk @@ -6,7 +6,7 @@ CFLAGS += \ -mfloat-abi=hard \ -mfpu=fpv4-sp-d16 \ -nostdlib -nostartfiles \ - -DCFG_TUSB_MCU=OPT_MCU_NONE + -DCFG_TUSB_MCU=OPT_MCU_SAMG #CFLAGS += -Wno-error=undef @@ -35,8 +35,8 @@ INC += \ $(TOP)/$(ASF_DIR)/CMSIS/Core/Include # For TinyUSB port source -VENDOR = . -CHIP_FAMILY = template +VENDOR = microchip +CHIP_FAMILY = samg # For freeRTOS port source FREERTOS_PORT = ARM_CM4F diff --git a/hw/bsp/samg55xplained/samg55xplained.c b/hw/bsp/samg55xplained/samg55xplained.c index b050d3624..79f102504 100644 --- a/hw/bsp/samg55xplained/samg55xplained.c +++ b/hw/bsp/samg55xplained/samg55xplained.c @@ -73,6 +73,14 @@ void board_init(void) // Enble clock _pmc_enable_periph_clock(ID_UDP); + + /* USB Device mode & Transceiver active */ + hri_matrix_write_CCFG_USBMR_reg(MATRIX, CCFG_USBMR_USBMODE); + +// NVIC_EnableIRQ(UDP_IRQn); + + // Attach + hri_udp_write_TXVC_reg(UDP, UDP_TXVC_PUON); } //--------------------------------------------------------------------+ diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c new file mode 100644 index 000000000..793892feb --- /dev/null +++ b/src/portable/microchip/samg/dcd_samg.c @@ -0,0 +1,116 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018, hathach (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 CFG_TUSB_MCU == OPT_MCU_SAMG + +#include "device/dcd.h" + +//--------------------------------------------------------------------+ +// MACRO TYPEDEF CONSTANT ENUM DECLARATION +//--------------------------------------------------------------------+ + + +/*------------------------------------------------------------------*/ +/* Device API + *------------------------------------------------------------------*/ + +// Initialize controller to device mode +void dcd_init (uint8_t rhport) +{ + (void) rhport; +} + +// Enable device interrupt +void dcd_int_enable (uint8_t rhport) +{ + (void) rhport; +} + +// Disable device interrupt +void dcd_int_disable (uint8_t rhport) +{ + (void) rhport; +} + +// Receive Set Address request, mcu port must also include status IN response +void dcd_set_address (uint8_t rhport, uint8_t dev_addr) +{ + (void) rhport; + (void) dev_addr; +} + +// Receive Set Configure request +void dcd_set_config (uint8_t rhport, uint8_t config_num) +{ + (void) rhport; + (void) config_num; +} + +// Wake up host +void dcd_remote_wakeup (uint8_t rhport) +{ + (void) rhport; +} + +//--------------------------------------------------------------------+ +// Endpoint API +//--------------------------------------------------------------------+ + +// Configure endpoint's registers according to descriptor +bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) +{ + (void) rhport; + (void) ep_desc; + return false; +} + +// 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) +{ + (void) rhport; + (void) ep_addr; + (void) buffer; + (void) total_bytes; + return false; +} + +// Stall endpoint +void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + (void) ep_addr; +} + +// clear stall, data toggle is also reset to DATA0 +void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + (void) ep_addr; +} + +#endif diff --git a/src/tusb_option.h b/src/tusb_option.h index 281d44029..6766da074 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -57,6 +57,7 @@ // SAM #define OPT_MCU_SAMD21 200 ///< MicroChip SAMD21 #define OPT_MCU_SAMD51 201 ///< MicroChip SAMD51 +#define OPT_MCU_SAMG 202 ///< MicroChip SAMDG series // STM32 #define OPT_MCU_STM32F0 300 ///< ST STM32F0 -- cgit v1.3.1 From c691dc4cb2dd37998e529d7f3f71de9e947aa80d Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 25 Nov 2019 10:42:32 +0700 Subject: update --- hw/bsp/samg55xplained/samg55xplained.c | 4 +--- src/portable/microchip/samg/dcd_samg.c | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src/portable') diff --git a/hw/bsp/samg55xplained/samg55xplained.c b/hw/bsp/samg55xplained/samg55xplained.c index 79f102504..67d1ae94b 100644 --- a/hw/bsp/samg55xplained/samg55xplained.c +++ b/hw/bsp/samg55xplained/samg55xplained.c @@ -66,7 +66,7 @@ void board_init(void) SysTick_Config(CONF_CPU_FREQUENCY / 1000); #endif - // USB + // USB Pin, Clock init /* Clear SYSIO 10 & 11 for USB DM & DP */ hri_matrix_clear_CCFG_SYSIO_reg(MATRIX, CCFG_SYSIO_SYSIO10 | CCFG_SYSIO_SYSIO11); @@ -77,8 +77,6 @@ void board_init(void) /* USB Device mode & Transceiver active */ hri_matrix_write_CCFG_USBMR_reg(MATRIX, CCFG_USBMR_USBMODE); -// NVIC_EnableIRQ(UDP_IRQn); - // Attach hri_udp_write_TXVC_reg(UDP, UDP_TXVC_PUON); } diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c index 793892feb..d3028e605 100644 --- a/src/portable/microchip/samg/dcd_samg.c +++ b/src/portable/microchip/samg/dcd_samg.c @@ -29,6 +29,7 @@ #if CFG_TUSB_MCU == OPT_MCU_SAMG #include "device/dcd.h" +#include "sam.h" //--------------------------------------------------------------------+ // MACRO TYPEDEF CONSTANT ENUM DECLARATION @@ -49,12 +50,14 @@ void dcd_init (uint8_t rhport) void dcd_int_enable (uint8_t rhport) { (void) rhport; + NVIC_EnableIRQ(UDP_IRQn); } // Disable device interrupt void dcd_int_disable (uint8_t rhport) { (void) rhport; + NVIC_DisableIRQ(UDP_IRQn); } // Receive Set Address request, mcu port must also include status IN response -- cgit v1.3.1 From 745f0b4521a43c8f764b0bcac2d000b17f9aa1ac Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 27 Nov 2019 11:02:24 +0700 Subject: clean up --- hw/bsp/samg55xplained/samg55xplained.c | 12 +++++++++++- src/portable/microchip/samg/dcd_samg.c | 24 +++++++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) (limited to 'src/portable') diff --git a/hw/bsp/samg55xplained/samg55xplained.c b/hw/bsp/samg55xplained/samg55xplained.c index b66b906a0..67e7f9255 100644 --- a/hw/bsp/samg55xplained/samg55xplained.c +++ b/hw/bsp/samg55xplained/samg55xplained.c @@ -75,7 +75,7 @@ void board_init(void) _pmc_enable_periph_clock(ID_FLEXCOM7); gpio_set_pin_function(UART_RX_PIN, MUX_PA27B_FLEXCOM7_RXD); gpio_set_pin_function(UART_TX_PIN, MUX_PA28B_FLEXCOM7_TXD); -// _usart_sync_init(&_edbg_com, FLEXCOM7, _edbg_com_buf, sizeof(_edbg_com_buf), _usart_get_usart_async()); + _usart_sync_init(&_edbg_com, FLEXCOM7); _usart_sync_set_baud_rate(&_edbg_com, CFG_BOARD_UART_BAUDRATE); _usart_sync_set_mode(&_edbg_com, USART_MODE_ASYNCHRONOUS); @@ -99,6 +99,16 @@ void board_init(void) hri_matrix_write_CCFG_USBMR_reg(MATRIX, CCFG_USBMR_USBMODE); } +//--------------------------------------------------------------------+ +// USB Interrupt Handler +//--------------------------------------------------------------------+ +void UDP_Handler(void) +{ + #if CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE + tud_isr(0); + #endif +} + //--------------------------------------------------------------------+ // Board porting API //--------------------------------------------------------------------+ diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c index d3028e605..9b9ebad7b 100644 --- a/src/portable/microchip/samg/dcd_samg.c +++ b/src/portable/microchip/samg/dcd_samg.c @@ -28,8 +28,8 @@ #if CFG_TUSB_MCU == OPT_MCU_SAMG -#include "device/dcd.h" #include "sam.h" +#include "device/dcd.h" //--------------------------------------------------------------------+ // MACRO TYPEDEF CONSTANT ENUM DECLARATION @@ -40,10 +40,22 @@ /* Device API *------------------------------------------------------------------*/ +// Set up endpoint 0, clear all other endpoints +//static void bus_reset(void) +//{ +// +//} + // Initialize controller to device mode void dcd_init (uint8_t rhport) { (void) rhport; + + + + + // Pull-up & Transceiver enable + UDP->UDP_TXVC = UDP_TXVC_PUON; } // Enable device interrupt @@ -116,4 +128,14 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) (void) ep_addr; } +//--------------------------------------------------------------------+ +// ISR +//--------------------------------------------------------------------+ +void dcd_isr(uint8_t rhport) +{ + (void) rhport; + + +} + #endif -- cgit v1.3.1 From bc21714c7ee7767c0aae6d782b6b72b954dddcdf Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 27 Nov 2019 14:49:19 +0700 Subject: detect bus reset --- hw/bsp/samg55xplained/samg55xplained.c | 5 ----- src/portable/microchip/samg/dcd_samg.c | 23 ++++++++++++++++------- 2 files changed, 16 insertions(+), 12 deletions(-) (limited to 'src/portable') diff --git a/hw/bsp/samg55xplained/samg55xplained.c b/hw/bsp/samg55xplained/samg55xplained.c index 67e7f9255..a5e145734 100644 --- a/hw/bsp/samg55xplained/samg55xplained.c +++ b/hw/bsp/samg55xplained/samg55xplained.c @@ -45,12 +45,8 @@ #define UART_TX_PIN GPIO(GPIO_PORTA, 28) #define UART_RX_PIN GPIO(GPIO_PORTA, 27) -//struct usart_async_descriptor _edbg_com; -//static uint8_t _edbg_com_buf[64]; - struct _usart_sync_device _edbg_com; - //------------- IMPLEMENTATION -------------// void board_init(void) { @@ -81,7 +77,6 @@ void board_init(void) _usart_sync_set_mode(&_edbg_com, USART_MODE_ASYNCHRONOUS); _usart_sync_enable(&_edbg_com); - #if CFG_TUSB_OS == OPT_OS_NONE // 1ms tick timer (samd SystemCoreClock may not correct) SysTick_Config(CONF_CPU_FREQUENCY / 1000); diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c index 9b9ebad7b..3eb64eb1a 100644 --- a/src/portable/microchip/samg/dcd_samg.c +++ b/src/portable/microchip/samg/dcd_samg.c @@ -41,10 +41,10 @@ *------------------------------------------------------------------*/ // Set up endpoint 0, clear all other endpoints -//static void bus_reset(void) -//{ -// -//} +static void bus_reset(void) +{ + +} // Initialize controller to device mode void dcd_init (uint8_t rhport) @@ -133,9 +133,18 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) //--------------------------------------------------------------------+ void dcd_isr(uint8_t rhport) { - (void) rhport; - - + uint32_t const intr_mask = UDP->UDP_IMR; + uint32_t const intr_status = UDP->UDP_ISR & intr_mask; + + // clear interrupt + UDP->UDP_ICR = intr_status; + + // Bus reset + if (intr_status & UDP_ISR_ENDBUSRES) + { + bus_reset(); + dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, true); + } } #endif -- cgit v1.3.1 From 6225d5bcfdf3b416d2dd109e88b67bc4fcefe249 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 27 Nov 2019 16:01:25 +0700 Subject: able to handle setup packet --- src/portable/microchip/samg/dcd_samg.c | 47 +++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) (limited to 'src/portable') diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c index 3eb64eb1a..899d054c6 100644 --- a/src/portable/microchip/samg/dcd_samg.c +++ b/src/portable/microchip/samg/dcd_samg.c @@ -43,7 +43,14 @@ // Set up endpoint 0, clear all other endpoints static void bus_reset(void) { + // Enable EP0 control + UDP->UDP_CSR[0] = UDP_CSR_EPEDS_Msk; + // Enable interrupt : EP0, Suspend, Resume, Wakeup + UDP->UDP_IER = UDP_IER_EP0INT_Msk | UDP_IER_RXSUSP_Msk | UDP_IER_RXRSM_Msk | UDP_IER_WAKEUP_Msk; + + // Enable transceiver + UDP->UDP_TXVC &= ~UDP_TXVC_TXVDIS_Msk; } // Initialize controller to device mode @@ -54,8 +61,8 @@ void dcd_init (uint8_t rhport) - // Pull-up & Transceiver enable - UDP->UDP_TXVC = UDP_TXVC_PUON; + // Enable pull-up, disable transceiver + UDP->UDP_TXVC = UDP_TXVC_PUON | UDP_TXVC_TXVDIS_Msk; } // Enable device interrupt @@ -140,11 +147,45 @@ void dcd_isr(uint8_t rhport) UDP->UDP_ICR = intr_status; // Bus reset - if (intr_status & UDP_ISR_ENDBUSRES) + if (intr_status & UDP_ISR_ENDBUSRES_Msk) { bus_reset(); dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, true); } + + // SOF + if (intr_status & UDP_ISR_SOFINT_Msk) dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true); + + // Suspend + if (intr_status & UDP_ISR_RXSUSP_Msk) dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); + + // Resume + if (intr_status & UDP_ISR_RXRSM_Msk) dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); + + // Wakeup +// if (intr_status & UDP_ISR_WAKEUP_Msk) dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); + + //------------- Endpoints -------------// + + if ( intr_status & TU_BIT(0) ) + { + // setup packet + if (UDP->UDP_CSR[0] & UDP_CSR_RXSETUP) + { + // get setup from FIFO + uint8_t setup[8]; + for(uint8_t i=0; iUDP_FDR[0]; + } + + // clear setup bit + UDP->UDP_CSR[0] &= ~UDP_CSR_RXSETUP_Msk; + + // notify usbd + dcd_event_setup_received(rhport, setup, true); + } + } } #endif -- cgit v1.3.1 From 40b454cf28e5a86dac8d261f1276ace108446ffe Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 27 Nov 2019 23:20:48 +0700 Subject: get passed first get device descriptor --- src/common/tusb_common.h | 6 +- src/device/usbd.c | 25 ++----- src/portable/microchip/samg/dcd_samg.c | 122 ++++++++++++++++++++++++++++++--- 3 files changed, 119 insertions(+), 34 deletions(-) (limited to 'src/portable') diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h index 59e1fe7fd..d162e4e8e 100644 --- a/src/common/tusb_common.h +++ b/src/common/tusb_common.h @@ -139,9 +139,9 @@ static inline uint8_t tu_log2(uint32_t value) } // Bit -static inline uint32_t tu_bit_set (uint32_t value, uint8_t n) { return value | TU_BIT(n); } -static inline uint32_t tu_bit_clear(uint32_t value, uint8_t n) { return value & (~TU_BIT(n)); } -static inline bool tu_bit_test (uint32_t value, uint8_t n) { return (value & TU_BIT(n)) ? true : false; } +static inline uint32_t tu_bit_set (uint32_t value, uint8_t pos) { return value | TU_BIT(pos); } +static inline uint32_t tu_bit_clear(uint32_t value, uint8_t pos) { return value & (~TU_BIT(pos)); } +static inline bool tu_bit_test (uint32_t value, uint8_t pos) { return (value & TU_BIT(pos)) ? true : false; } /*------------------------------------------------------------------*/ /* Count number of arguments of __VA_ARGS__ diff --git a/src/device/usbd.c b/src/device/usbd.c index 1797dfafa..530eb09d1 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -830,19 +830,15 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr) { switch (event->event_id) { - case DCD_EVENT_BUS_RESET: - osal_queue_send(_usbd_q, event, in_isr); - break; - case DCD_EVENT_UNPLUGGED: - _usbd_dev.connected = 0; + _usbd_dev.connected = 0; _usbd_dev.configured = 0; - _usbd_dev.suspended = 0; + _usbd_dev.suspended = 0; osal_queue_send(_usbd_q, event, in_isr); break; case DCD_EVENT_SOF: - // nothing to do now + return; // skip SOF event for now break; case DCD_EVENT_SUSPEND: @@ -857,6 +853,7 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr) break; case DCD_EVENT_RESUME: + // skip event if not connected (especially required for SAMD) if ( _usbd_dev.connected ) { _usbd_dev.suspended = 0; @@ -864,21 +861,9 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr) } break; - case DCD_EVENT_SETUP_RECEIVED: - osal_queue_send(_usbd_q, event, in_isr); - break; - - case DCD_EVENT_XFER_COMPLETE: + default: osal_queue_send(_usbd_q, event, in_isr); - TU_ASSERT(event->xfer_complete.result == XFER_RESULT_SUCCESS,); break; - - // Not an DCD event, just a convenient way to defer ISR function should we need to - case USBD_EVENT_FUNC_CALL: - osal_queue_send(_usbd_q, event, in_isr); - break; - - default: break; } } diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c index 899d054c6..27fd35de6 100644 --- a/src/portable/microchip/samg/dcd_samg.c +++ b/src/portable/microchip/samg/dcd_samg.c @@ -35,6 +35,38 @@ // MACRO TYPEDEF CONSTANT ENUM DECLARATION //--------------------------------------------------------------------+ +// Transfer descriptor +typedef struct +{ + uint8_t* buffer; + uint16_t total_len; + volatile uint16_t actual_len; + uint16_t epsize; +} xfer_desc_t; + +// Endpoint 0-5 with OUT & IN +xfer_desc_t _dcd_xfer[6][2]; + +void xfer_begin(xfer_desc_t* xfer, uint8_t * buffer, uint16_t total_bytes) +{ + xfer->buffer = buffer; + xfer->total_len = total_bytes; + xfer->actual_len = 0; +} + +uint16_t xfer_packet_len(xfer_desc_t* xfer) +{ + // also cover zero-length packet + return tu_min16(xfer->total_len - xfer->actual_len, xfer->epsize); +} + +void xfer_packet_done(xfer_desc_t* xfer) +{ + uint16_t const xact_len = xfer_packet_len(xfer); + + xfer->buffer += xact_len; + xfer->actual_len += xact_len; +} /*------------------------------------------------------------------*/ /* Device API @@ -43,6 +75,10 @@ // Set up endpoint 0, clear all other endpoints static void bus_reset(void) { + tu_memclr(_dcd_xfer, sizeof(_dcd_xfer)); + + _dcd_xfer[0][0].epsize = _dcd_xfer[0][1].epsize = CFG_TUD_ENDPOINT0_SIZE; + // Enable EP0 control UDP->UDP_CSR[0] = UDP_CSR_EPEDS_Msk; @@ -58,8 +94,7 @@ void dcd_init (uint8_t rhport) { (void) rhport; - - + tu_memclr(_dcd_xfer, sizeof(_dcd_xfer)); // Enable pull-up, disable transceiver UDP->UDP_TXVC = UDP_TXVC_PUON | UDP_TXVC_TXVDIS_Msk; @@ -115,10 +150,46 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) { (void) rhport; - (void) ep_addr; - (void) buffer; - (void) total_bytes; - return false; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + xfer_desc_t* xfer = &_dcd_xfer[epnum][dir]; + xfer_begin(xfer, buffer, total_bytes); + + uint16_t const xact_len = xfer_packet_len(xfer); + + // control endpoint + if ( epnum == 0 ) + { + // opposite to DIR bit --> status phase + // switch the DIR bit +// if ( dir != tu_bit_test(UDP->UDP_CSR[0], UDP_CSR_DIR_Pos) ) +// { +// +// } + + if (dir == TUSB_DIR_OUT) + { + UDP->UDP_CSR[0] &= ~UDP_CSR_DIR_Msk; + // + }else + { + UDP->UDP_CSR[0] |= UDP_CSR_DIR_Msk; + + // Write data to fifo + for(uint16_t i=0; iUDP_FDR[0] = (uint32_t) buffer[i]; + + // TX ready for transfer + UDP->UDP_CSR[0] |= UDP_CSR_TXPKTRDY_Msk; + } + + }else + { + return false; + } + + return true; } // Stall endpoint @@ -154,13 +225,13 @@ void dcd_isr(uint8_t rhport) } // SOF - if (intr_status & UDP_ISR_SOFINT_Msk) dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true); +// if (intr_status & UDP_ISR_SOFINT_Msk) dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true); // Suspend - if (intr_status & UDP_ISR_RXSUSP_Msk) dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); +// if (intr_status & UDP_ISR_RXSUSP_Msk) dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); // Resume - if (intr_status & UDP_ISR_RXRSM_Msk) dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); +// if (intr_status & UDP_ISR_RXRSM_Msk) dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); // Wakeup // if (intr_status & UDP_ISR_WAKEUP_Msk) dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); @@ -179,11 +250,40 @@ void dcd_isr(uint8_t rhport) setup[i] = (uint8_t) UDP->UDP_FDR[0]; } - // clear setup bit - UDP->UDP_CSR[0] &= ~UDP_CSR_RXSETUP_Msk; + // Set EP0 Dir bit & Clear setup bit +// uint32_t csr = UDP->UDP_CSR[0]; +// csr &= ~(UDP_CSR_RXSETUP_Msk | UDP_CSR_DIR_Msk); +// if ( setup[0] & TUSB_DIR_IN_MASK ) csr |= UDP_CSR_DIR_Msk; +// +// UDP->UDP_CSR[0] = csr; // notify usbd dcd_event_setup_received(rhport, setup, true); + + // Clear Setup bit + UDP->UDP_CSR[0] &= ~UDP_CSR_RXSETUP_Msk; + } + } + + for(uint8_t epnum = 0; epnum < 6; epnum++) + { + // Endpoint IN + if (UDP->UDP_CSR[epnum] & UDP_CSR_TXCOMP_Msk) + { + xfer_desc_t* xfer = &_dcd_xfer[epnum][1]; + uint16_t xact_len = xfer_packet_len(xfer); + xfer_packet_done(xfer); + + dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xact_len, XFER_RESULT_SUCCESS, true); + + // Clear TX Complete bit + UDP->UDP_CSR[0] &= ~UDP_CSR_TXCOMP_Msk; + } + + // Endpoint OUT + if (UDP->UDP_CSR[epnum] & UDP_CSR_RX_DATA_BK0_Msk) + { + dcd_event_bus_signal(rhport, DCD_EVENT_INVALID, true); } } } -- cgit v1.3.1 From 6bc245cb36cd18543916516a91def1f1e3a04e66 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 28 Nov 2019 11:46:36 +0700 Subject: add dcd_edpt0_status_complete() which is useful for several mcu port --- src/common/tusb_common.h | 12 +++++++----- src/device/dcd.h | 4 ++++ src/device/usbd_control.c | 2 ++ src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c | 2 ++ 4 files changed, 15 insertions(+), 5 deletions(-) (limited to 'src/portable') diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h index d162e4e8e..a2c40e669 100644 --- a/src/common/tusb_common.h +++ b/src/common/tusb_common.h @@ -216,17 +216,19 @@ static inline bool tu_bit_test (uint32_t value, uint8_t pos) { return (value void tu_print_mem(void const *buf, uint8_t size, uint16_t count); #ifndef tu_printf - #define tu_printf printf + #define tu_printf printf #endif // Log with debug level 1 -#define TU_LOG1 tu_printf -#define TU_LOG1_MEM tu_print_mem +#define TU_LOG1 tu_printf +#define TU_LOG1_MEM tu_print_mem +#define TU_LOG1_LOCATION() tu_printf("%s: %d:\n", __PRETTY_FUNCTION__, __LINE__) // Log with debug level 2 #if CFG_TUSB_DEBUG > 1 - #define TU_LOG2 TU_LOG1 - #define TU_LOG2_MEM TU_LOG1_MEM + #define TU_LOG2 TU_LOG1 + #define TU_LOG2_MEM TU_LOG1_MEM + #define TU_LOG2_LOCATION() TU_LOG1_LOCATION() #endif #endif // CFG_TUSB_DEBUG diff --git a/src/device/dcd.h b/src/device/dcd.h index dca289e78..8ae232fa6 100644 --- a/src/device/dcd.h +++ b/src/device/dcd.h @@ -110,6 +110,10 @@ 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); diff --git a/src/device/usbd_control.c b/src/device/usbd_control.c index 7f6cf5a40..8b9b8f1cf 100644 --- a/src/device/usbd_control.c +++ b/src/device/usbd_control.c @@ -64,6 +64,7 @@ static inline bool _status_stage_xact(uint8_t rhport, tusb_control_request_t con return dcd_edpt_xfer(rhport, request->bmRequestType_bit.direction ? EDPT_CTRL_OUT : EDPT_CTRL_IN, NULL, 0); } +// Status phase bool tud_control_status(uint8_t rhport, tusb_control_request_t const * request) { _ctrl_xfer.request = (*request); @@ -141,6 +142,7 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result if ( tu_edpt_dir(ep_addr) != _ctrl_xfer.request.bmRequestType_bit.direction ) { TU_ASSERT(0 == xferred_bytes); + if (dcd_edpt0_status_complete) dcd_edpt0_status_complete(rhport, &_ctrl_xfer.request); return true; } diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c index ec182960e..7ba9481ba 100644 --- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c @@ -296,6 +296,8 @@ void dcd_int_disable(uint8_t rhport) void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { (void)rhport; + + // FIXME use dcd_edpt0_status_complete() // We cannot immediatly change it; it must be queued to change after the STATUS packet is sent. // (CTR handler will actually change the address once it sees that the transmission is complete) newDADDR = dev_addr; -- cgit v1.3.1 From 5dc04887e28314c1d74409939af66445ce9626ae Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 28 Nov 2019 11:48:08 +0700 Subject: get passed set address --- src/device/usbd.c | 7 ++++- src/portable/microchip/samg/dcd_samg.c | 49 +++++++++++++++++++++++----------- 2 files changed, 39 insertions(+), 17 deletions(-) (limited to 'src/portable') diff --git a/src/device/usbd.c b/src/device/usbd.c index 530eb09d1..01ec0e5fd 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -405,7 +405,6 @@ void tud_task (void) if ( 0 == epnum ) { - TU_LOG1(" EP Addr = 0x%02X, len = %ld\r\n", ep_addr, event.xfer_complete.len); usbd_control_xfer_cb(event.rhport, ep_addr, event.xfer_complete.result, event.xfer_complete.len); } else @@ -503,7 +502,13 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const // Depending on mcu, status phase could be sent either before or after changing device address // Therefore DCD must include zero-length status response dcd_set_address(rhport, (uint8_t) p_request->wValue); + +// FIXME remove STATUS response from dcd_set_address(), +#if CFG_TUSB_MCU == OPT_MCU_SAMG // skip status for nrf5x mcu + tud_control_status(rhport, p_request); +#else return true; // skip status +#endif break; case TUSB_REQ_GET_CONFIGURATION: diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c index 27fd35de6..bf842eab3 100644 --- a/src/portable/microchip/samg/dcd_samg.c +++ b/src/portable/microchip/samg/dcd_samg.c @@ -119,6 +119,9 @@ void dcd_set_address (uint8_t rhport, uint8_t dev_addr) { (void) rhport; (void) dev_addr; + + // SAMG can only set address after status for this request is complete + // do it at dcd_edpt0_status_complete() } // Receive Set Configure request @@ -138,6 +141,27 @@ 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) +{ + (void) rhport; + + if (request->bRequest == TUSB_REQ_SET_ADDRESS) + { + uint8_t const dev_addr = (uint8_t) request->wValue; + TU_LOG2("dev address = %d", dev_addr); + + // Enable addressed state + UDP->UDP_GLB_STAT |= UDP_GLB_STAT_FADDEN_Msk; + + // Set new address & Function enable bit + UDP->UDP_FADDR |= UDP_FADDR_FADD(dev_addr); + + UDP->UDP_FADDR |= UDP_FADDR_FEN_Msk; + } +} + // Configure endpoint's registers according to descriptor bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) { @@ -162,19 +186,14 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t // control endpoint if ( epnum == 0 ) { - // opposite to DIR bit --> status phase - // switch the DIR bit -// if ( dir != tu_bit_test(UDP->UDP_CSR[0], UDP_CSR_DIR_Pos) ) -// { -// -// } - if (dir == TUSB_DIR_OUT) { + // Clear DIR bit UDP->UDP_CSR[0] &= ~UDP_CSR_DIR_Msk; - // + }else { + // Set DIR bit if needed UDP->UDP_CSR[0] |= UDP_CSR_DIR_Msk; // Write data to fifo @@ -250,13 +269,6 @@ void dcd_isr(uint8_t rhport) setup[i] = (uint8_t) UDP->UDP_FDR[0]; } - // Set EP0 Dir bit & Clear setup bit -// uint32_t csr = UDP->UDP_CSR[0]; -// csr &= ~(UDP_CSR_RXSETUP_Msk | UDP_CSR_DIR_Msk); -// if ( setup[0] & TUSB_DIR_IN_MASK ) csr |= UDP_CSR_DIR_Msk; -// -// UDP->UDP_CSR[0] = csr; - // notify usbd dcd_event_setup_received(rhport, setup, true); @@ -283,7 +295,12 @@ void dcd_isr(uint8_t rhport) // Endpoint OUT if (UDP->UDP_CSR[epnum] & UDP_CSR_RX_DATA_BK0_Msk) { - dcd_event_bus_signal(rhport, DCD_EVENT_INVALID, true); + uint16_t const xact_len = (uint16_t) ((UDP->UDP_CSR[0] & UDP_CSR_RXBYTECNT_Msk) >> UDP_CSR_RXBYTECNT_Pos); + + dcd_event_xfer_complete(rhport, epnum, xact_len, XFER_RESULT_SUCCESS, true); + + // Clear DATA Bank0 bit + UDP->UDP_CSR[0] &= ~UDP_CSR_RX_DATA_BK0_Msk; } } } -- cgit v1.3.1 From 552cc3ef33aee21eba3ea60ad07c461181eeaa58 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 28 Nov 2019 11:56:35 +0700 Subject: implement dcd_edpt_stall --- src/portable/microchip/samg/dcd_samg.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'src/portable') diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c index bf842eab3..1d2e150d5 100644 --- a/src/portable/microchip/samg/dcd_samg.c +++ b/src/portable/microchip/samg/dcd_samg.c @@ -215,7 +215,12 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr) { (void) rhport; - (void) ep_addr; + + uint8_t const epnum = tu_edpt_number(ep_addr); +// uint8_t const dir = tu_edpt_dir(ep_addr); + + // Set force stall bit + UDP->UDP_CSR[epnum] |= UDP_CSR_FORCESTALL_Msk; } // clear stall, data toggle is also reset to DATA0 @@ -302,6 +307,12 @@ void dcd_isr(uint8_t rhport) // Clear DATA Bank0 bit UDP->UDP_CSR[0] &= ~UDP_CSR_RX_DATA_BK0_Msk; } + + // Stall sent to host + if (UDP->UDP_CSR[epnum] & UDP_CSR_STALLSENT_Msk) + { + UDP->UDP_CSR[0] &= ~UDP_CSR_STALLSENT_Msk; + } } } -- cgit v1.3.1 From ac701c398b4e9a43eefe0ee0be38579ca17535df Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 28 Nov 2019 12:18:56 +0700 Subject: clean up --- src/portable/microchip/samg/dcd_samg.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src/portable') diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c index 1d2e150d5..c38621e00 100644 --- a/src/portable/microchip/samg/dcd_samg.c +++ b/src/portable/microchip/samg/dcd_samg.c @@ -150,15 +150,12 @@ void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * re if (request->bRequest == TUSB_REQ_SET_ADDRESS) { uint8_t const dev_addr = (uint8_t) request->wValue; - TU_LOG2("dev address = %d", dev_addr); // Enable addressed state UDP->UDP_GLB_STAT |= UDP_GLB_STAT_FADDEN_Msk; // Set new address & Function enable bit - UDP->UDP_FADDR |= UDP_FADDR_FADD(dev_addr); - - UDP->UDP_FADDR |= UDP_FADDR_FEN_Msk; + UDP->UDP_FADDR = UDP_FADDR_FEN_Msk | UDP_FADDR_FADD(dev_addr); } } -- cgit v1.3.1 From d7558e8a0f12c685f2690c985e5f75df0777065b Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 28 Nov 2019 13:39:29 +0700 Subject: use dcd_edpt0_status_complete() to set address without blocking for samd21/samd51/stm32_fsdev --- src/device/usbd.c | 25 +++++++++----------- src/device/usbd_control.c | 12 ++++++++-- src/portable/microchip/samd21/dcd_samd21.c | 25 +++++++++++++++----- src/portable/microchip/samd51/dcd_samd51.c | 23 ++++++++++++++---- src/portable/microchip/samg/dcd_samg.c | 5 +++- src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c | 34 ++++++++++++++++----------- src/portable/st/synopsys/dcd_synopsys.c | 1 - 7 files changed, 82 insertions(+), 43 deletions(-) (limited to 'src/portable') diff --git a/src/device/usbd.c b/src/device/usbd.c index 01ec0e5fd..497281c2d 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -202,9 +202,10 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const 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 (uint8_t rhport); -bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); +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); //--------------------------------------------------------------------+ @@ -321,7 +322,7 @@ static void usbd_reset(uint8_t rhport) 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(rhport); + usbd_control_reset(); for (uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++) { @@ -376,7 +377,7 @@ void tud_task (void) case DCD_EVENT_SETUP_RECEIVED: TU_LOG2(" "); - TU_LOG1_MEM(&event.setup_received, 1, 8); + TU_LOG2_MEM(&event.setup_received, 1, 8); // 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 @@ -385,7 +386,7 @@ void tud_task (void) // Process control request if ( !process_control_request(event.rhport, &event.setup_received) ) { - TU_LOG1(" Stall EP0\r\n"); + 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); @@ -499,16 +500,12 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const switch ( p_request->bRequest ) { case TUSB_REQ_SET_ADDRESS: - // Depending on mcu, status phase could be sent either before or after changing device address - // Therefore DCD must include zero-length status response + // 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); - -// FIXME remove STATUS response from dcd_set_address(), -#if CFG_TUSB_MCU == OPT_MCU_SAMG // skip status for nrf5x mcu - tud_control_status(rhport, p_request); -#else - return true; // skip status -#endif + // skip tud_control_status() break; case TUSB_REQ_GET_CONFIGURATION: diff --git a/src/device/usbd_control.c b/src/device/usbd_control.c index 8b9b8f1cf..e6d1caf4b 100644 --- a/src/device/usbd_control.c +++ b/src/device/usbd_control.c @@ -119,9 +119,8 @@ bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, vo // USBD API //--------------------------------------------------------------------+ -void usbd_control_reset (uint8_t rhport) +void usbd_control_reset(void) { - (void) rhport; tu_varclr(&_ctrl_xfer); } @@ -131,6 +130,15 @@ void usbd_control_set_complete_callback( bool (*fp) (uint8_t, tusb_control_reque _ctrl_xfer.complete_cb = fp; } +// useful for dcd_set_address where DCD is responsible for status response +void usbd_control_set_request(tusb_control_request_t const *request) +{ + _ctrl_xfer.request = (*request); + _ctrl_xfer.buffer = NULL; + _ctrl_xfer.total_xferred = 0; + _ctrl_xfer.data_len = 0; +} + // callback when a transaction complete on // - DATA stage of control endpoint or // - Status stage diff --git a/src/portable/microchip/samd21/dcd_samd21.c b/src/portable/microchip/samd21/dcd_samd21.c index 231abc7d0..3ffaabf09 100644 --- a/src/portable/microchip/samd21/dcd_samd21.c +++ b/src/portable/microchip/samd21/dcd_samd21.c @@ -98,13 +98,13 @@ void dcd_int_disable(uint8_t rhport) void dcd_set_address (uint8_t rhport, uint8_t dev_addr) { - // Response with status first before changing device address - dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); + (void) dev_addr; - // Wait for EP0 to finish before switching the address. - while (USB->DEVICE.DeviceEndpoint[0].EPSTATUS.bit.BK1RDY == 1) {} + // Response with zlp status + dcd_edpt_xfer(rhport, 0x80, NULL, 0); - USB->DEVICE.DADD.reg = USB_DEVICE_DADD_DADD(dev_addr) | USB_DEVICE_DADD_ADDEN; + // DCD can only set address after status for this request is complete + // do it at dcd_edpt0_status_complete() // Enable SUSPEND interrupt since the bus signal D+/D- are stable now. USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTENCLR_SUSPEND; // clear pending @@ -116,7 +116,6 @@ void dcd_set_config (uint8_t rhport, uint8_t config_num) (void) rhport; (void) config_num; // Nothing to do - } void dcd_remote_wakeup(uint8_t rhport) @@ -130,6 +129,20 @@ void dcd_remote_wakeup(uint8_t rhport) /* DCD Endpoint port *------------------------------------------------------------------*/ +// 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) +{ + (void) rhport; + + if (request->bRequest == TUSB_REQ_SET_ADDRESS) + { + uint8_t const dev_addr = (uint8_t) request->wValue; + USB->DEVICE.DADD.reg = USB_DEVICE_DADD_DADD(dev_addr) | USB_DEVICE_DADD_ADDEN; + } +} + + bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) { (void) rhport; diff --git a/src/portable/microchip/samd51/dcd_samd51.c b/src/portable/microchip/samd51/dcd_samd51.c index f58e7cbc7..1af247702 100644 --- a/src/portable/microchip/samd51/dcd_samd51.c +++ b/src/portable/microchip/samd51/dcd_samd51.c @@ -104,13 +104,13 @@ void dcd_int_disable(uint8_t rhport) void dcd_set_address (uint8_t rhport, uint8_t dev_addr) { - // Response with status first before changing device address - dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); + (void) dev_addr; - // Wait for EP0 to finish before switching the address. - while (USB->DEVICE.DeviceEndpoint[0].EPSTATUS.bit.BK1RDY == 1) {} + // Response with zlp status + dcd_edpt_xfer(rhport, 0x80, NULL, 0); - USB->DEVICE.DADD.reg = USB_DEVICE_DADD_DADD(dev_addr) | USB_DEVICE_DADD_ADDEN; + // DCD can only set address after status for this request is complete + // do it at dcd_edpt0_status_complete() // Enable SUSPEND interrupt since the bus signal D+/D- are stable now. USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTENCLR_SUSPEND; // clear pending @@ -135,6 +135,19 @@ void dcd_remote_wakeup(uint8_t rhport) /* DCD Endpoint port *------------------------------------------------------------------*/ +// 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) +{ + (void) rhport; + + if (request->bRequest == TUSB_REQ_SET_ADDRESS) + { + uint8_t const dev_addr = (uint8_t) request->wValue; + USB->DEVICE.DADD.reg = USB_DEVICE_DADD_DADD(dev_addr) | USB_DEVICE_DADD_ADDEN; + } +} + bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) { (void) rhport; diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c index c38621e00..122dc04b7 100644 --- a/src/portable/microchip/samg/dcd_samg.c +++ b/src/portable/microchip/samg/dcd_samg.c @@ -120,7 +120,10 @@ void dcd_set_address (uint8_t rhport, uint8_t dev_addr) (void) rhport; (void) dev_addr; - // SAMG can only set address after status for this request is complete + // Response with zlp status + dcd_edpt_xfer(rhport, 0x80, NULL, 0); + + // DCD can only set address after status for this request is complete. // do it at dcd_edpt0_status_complete() } diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c index 7ba9481ba..f52e113b7 100644 --- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c @@ -172,7 +172,6 @@ static inline xfer_ctl_t* xfer_ctl_ptr(uint32_t epnum, uint32_t dir) static TU_ATTR_ALIGNED(4) uint32_t _setup_packet[6]; -static uint8_t newDADDR; // Used to set the new device address during the CTR IRQ handler static uint8_t remoteWakeCountdown; // When wake is requested // EP Buffers assigned from end of memory location, to minimize their chance of crashing @@ -297,14 +296,11 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { (void)rhport; - // FIXME use dcd_edpt0_status_complete() - // We cannot immediatly change it; it must be queued to change after the STATUS packet is sent. - // (CTR handler will actually change the address once it sees that the transmission is complete) - newDADDR = dev_addr; - // Respond with status dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); + // DCD can only set address after status for this request is complete. + // do it at dcd_edpt0_status_complete() } // Receive Set Config request @@ -361,7 +357,7 @@ static void dcd_handle_bus_reset(void) ep_buf_ptr = DCD_STM32_BTABLE_BASE + 8*MAX_EP_COUNT; // 8 bytes per endpoint (two TX and two RX words, each) dcd_edpt_open (0, &ep0OUT_desc); dcd_edpt_open (0, &ep0IN_desc); - newDADDR = 0u; + USB->DADDR = USB_DADDR_EF; // Set enable flag, and leaving the device address as zero. } @@ -397,13 +393,7 @@ static uint16_t dcd_ep_ctr_handler(void) if((xfer->total_len == xfer->queued_len)) { dcd_event_xfer_complete(0u, (uint8_t)(0x80 + EPindex), xfer->total_len, XFER_RESULT_SUCCESS, true); - if((newDADDR != 0) && ( xfer->total_len == 0U)) - { - // Delayed setting of the DADDR after the 0-len DATA packet acking the request is sent. - reg16_clear_bits(&USB->DADDR, USB_DADDR_ADD); - USB->DADDR = (uint16_t)(USB->DADDR | newDADDR); // leave the enable bit set - newDADDR = 0; - } + if(xfer->total_len == 0) // Probably a status message? { pcd_clear_rx_dtog(USB,EPindex); @@ -601,6 +591,22 @@ static void dcd_fs_irqHandler(void) { // 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) +{ + (void) rhport; + + if (request->bRequest == TUSB_REQ_SET_ADDRESS) + { + uint8_t const dev_addr = (uint8_t) request->wValue; + + // Setting new address after the whole request is complete + reg16_clear_bits(&USB->DADDR, USB_DADDR_ADD); + USB->DADDR = (uint16_t)(USB->DADDR | dev_addr); // leave the enable bit set + } +} + // The STM32F0 doesn't seem to like |= or &= to manipulate the EP#R registers, // so I'm using the #define from HAL here, instead. diff --git a/src/portable/st/synopsys/dcd_synopsys.c b/src/portable/st/synopsys/dcd_synopsys.c index 7929b6916..e02580e4f 100644 --- a/src/portable/st/synopsys/dcd_synopsys.c +++ b/src/portable/st/synopsys/dcd_synopsys.c @@ -236,7 +236,6 @@ void dcd_set_address (uint8_t rhport, uint8_t dev_addr) (void) rhport; USB_OTG_DeviceTypeDef * dev = DEVICE_BASE; - dev->DCFG |= (dev_addr << USB_OTG_DCFG_DAD_Pos) & USB_OTG_DCFG_DAD_Msk; // Response with status after changing device address -- cgit v1.3.1 From e9725dcdbe8dcb56f7eeedd5671bf0f4c3fe72a2 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 28 Nov 2019 13:48:55 +0700 Subject: use dcd_edpt0_status_complete() to prepare for setup packet for samd21/samd51 --- src/portable/microchip/samd21/dcd_samd21.c | 11 +++-------- src/portable/microchip/samd51/dcd_samd51.c | 8 +++----- 2 files changed, 6 insertions(+), 13 deletions(-) (limited to 'src/portable') diff --git a/src/portable/microchip/samd21/dcd_samd21.c b/src/portable/microchip/samd21/dcd_samd21.c index 3ffaabf09..828b47018 100644 --- a/src/portable/microchip/samd21/dcd_samd21.c +++ b/src/portable/microchip/samd21/dcd_samd21.c @@ -133,13 +133,14 @@ void dcd_remote_wakeup(uint8_t rhport) // 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) { - (void) rhport; - if (request->bRequest == TUSB_REQ_SET_ADDRESS) { uint8_t const dev_addr = (uint8_t) request->wValue; USB->DEVICE.DADD.reg = USB_DEVICE_DADD_DADD(dev_addr) | USB_DEVICE_DADD_ADDEN; } + + // Just finished status stage, prepare for next setup packet + dcd_edpt_xfer(rhport, 0x00, _setup_packet, sizeof(_setup_packet)); } @@ -295,12 +296,6 @@ void maybe_transfer_complete(void) { uint8_t ep_addr = epnum; dcd_event_xfer_complete(0, ep_addr, total_transfer_size, XFER_RESULT_SUCCESS, true); } - - // Just finished status stage (total size = 0), prepare for next setup packet - // TODO could cause issue with actual zero length data used by class such as DFU - if (epnum == 0 && total_transfer_size == 0) { - dcd_edpt_xfer(0, 0, _setup_packet, sizeof(_setup_packet)); - } } } diff --git a/src/portable/microchip/samd51/dcd_samd51.c b/src/portable/microchip/samd51/dcd_samd51.c index 1af247702..abf89c32e 100644 --- a/src/portable/microchip/samd51/dcd_samd51.c +++ b/src/portable/microchip/samd51/dcd_samd51.c @@ -146,6 +146,9 @@ void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * re uint8_t const dev_addr = (uint8_t) request->wValue; USB->DEVICE.DADD.reg = USB_DEVICE_DADD_DADD(dev_addr) | USB_DEVICE_DADD_ADDEN; } + + // Just finished status stage, prepare for next setup packet + dcd_edpt_xfer(rhport, 0x00, _setup_packet, sizeof(_setup_packet)); } bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) @@ -347,11 +350,6 @@ void transfer_complete(uint8_t direction) { } dcd_event_xfer_complete(0, ep_addr, total_transfer_size, XFER_RESULT_SUCCESS, true); - // just finished status stage (total size = 0), prepare for next setup packet - if (epnum == 0 && total_transfer_size == 0) { - dcd_edpt_xfer(0, 0, _setup_packet, sizeof(_setup_packet)); - } - if (direction == TUSB_DIR_IN) { ep->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT1; } else { -- cgit v1.3.1 From 26dcc19b186a85f6ee8d1d94bb672e34865f56e9 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Fri, 29 Nov 2019 18:14:23 +0800 Subject: valentyusb: eptri: don't double-advance read buffer Due to an error, we were double-advancing the FIFO buffer. The end result was that the second half of most reads were getting ignored. This wasn't found during earlier testing because only 64-byte buffers were tested. This corrects this error by avoiding double-advancing the buffer. Signed-off-by: Sean Cross --- src/portable/valentyusb/eptri/dcd_eptri.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/portable') diff --git a/src/portable/valentyusb/eptri/dcd_eptri.c b/src/portable/valentyusb/eptri/dcd_eptri.c index e555fb0b1..67e0c59aa 100644 --- a/src/portable/valentyusb/eptri/dcd_eptri.c +++ b/src/portable/valentyusb/eptri/dcd_eptri.c @@ -226,7 +226,7 @@ static void process_rx(void) { test_buffer[total_read] = c; #endif total_read++; - if ((rx_buffer_offset[rx_ep] + current_offset) < rx_buffer_max[rx_ep]) { + if (current_offset < rx_buffer_max[rx_ep]) { #if LOG_USB usb_log[usb_log_offset].data[usb_log[usb_log_offset].size++] = c; #endif -- cgit v1.3.1 From 208a77678cabd3dbd270a9f93f4ec110b25908ff Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 9 Dec 2019 18:37:20 +0700 Subject: clean up, switch and update submodule samd to circuitpython branch --- hw/bsp/circuitplayground_express/circuitplayground_express.c | 2 +- hw/bsp/feather_m0_express/feather_m0_express.c | 2 +- hw/bsp/feather_m4_express/feather_m4_express.c | 2 +- hw/bsp/metro_m0_express/metro_m0_express.c | 2 +- hw/bsp/metro_m4_express/metro_m4_express.c | 2 +- hw/mcu/microchip | 2 +- src/portable/microchip/samd21/dcd_samd21.c | 2 +- src/portable/microchip/samd51/dcd_samd51.c | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src/portable') diff --git a/hw/bsp/circuitplayground_express/circuitplayground_express.c b/hw/bsp/circuitplayground_express/circuitplayground_express.c index 6ca584eb3..c48d14463 100644 --- a/hw/bsp/circuitplayground_express/circuitplayground_express.c +++ b/hw/bsp/circuitplayground_express/circuitplayground_express.c @@ -24,9 +24,9 @@ * This file is part of the TinyUSB stack. */ +#include "sam.h" #include "bsp/board.h" -#include "sam.h" #include "hal/include/hal_gpio.h" #include "hal/include/hal_init.h" #include "hri/hri_nvmctrl_d21.h" diff --git a/hw/bsp/feather_m0_express/feather_m0_express.c b/hw/bsp/feather_m0_express/feather_m0_express.c index 35505add9..6fb14b5a2 100644 --- a/hw/bsp/feather_m0_express/feather_m0_express.c +++ b/hw/bsp/feather_m0_express/feather_m0_express.c @@ -24,9 +24,9 @@ * This file is part of the TinyUSB stack. */ +#include "sam.h" #include "bsp/board.h" -#include "sam.h" #include "hal/include/hal_gpio.h" #include "hal/include/hal_init.h" #include "hri/hri_nvmctrl_d21.h" diff --git a/hw/bsp/feather_m4_express/feather_m4_express.c b/hw/bsp/feather_m4_express/feather_m4_express.c index fccd0d3af..1e20c846f 100644 --- a/hw/bsp/feather_m4_express/feather_m4_express.c +++ b/hw/bsp/feather_m4_express/feather_m4_express.c @@ -24,9 +24,9 @@ * This file is part of the TinyUSB stack. */ +#include "sam.h" #include "bsp/board.h" -#include "sam.h" #include "hal/include/hal_gpio.h" #include "hal/include/hal_init.h" #include "hpl/gclk/hpl_gclk_base.h" diff --git a/hw/bsp/metro_m0_express/metro_m0_express.c b/hw/bsp/metro_m0_express/metro_m0_express.c index 35505add9..6fb14b5a2 100644 --- a/hw/bsp/metro_m0_express/metro_m0_express.c +++ b/hw/bsp/metro_m0_express/metro_m0_express.c @@ -24,9 +24,9 @@ * This file is part of the TinyUSB stack. */ +#include "sam.h" #include "bsp/board.h" -#include "sam.h" #include "hal/include/hal_gpio.h" #include "hal/include/hal_init.h" #include "hri/hri_nvmctrl_d21.h" diff --git a/hw/bsp/metro_m4_express/metro_m4_express.c b/hw/bsp/metro_m4_express/metro_m4_express.c index 08559e57c..c3c0468a4 100644 --- a/hw/bsp/metro_m4_express/metro_m4_express.c +++ b/hw/bsp/metro_m4_express/metro_m4_express.c @@ -24,9 +24,9 @@ * This file is part of the TinyUSB stack. */ +#include "sam.h" #include "bsp/board.h" -#include "sam.h" #include "hal/include/hal_gpio.h" #include "hal/include/hal_init.h" #include "hpl/gclk/hpl_gclk_base.h" diff --git a/hw/mcu/microchip b/hw/mcu/microchip index 005624a65..66b5a1199 160000 --- a/hw/mcu/microchip +++ b/hw/mcu/microchip @@ -1 +1 @@ -Subproject commit 005624a65dc6598b10eb1287edc92316d8ae3c1d +Subproject commit 66b5a11995025426224e0ba6f377322e6e8893b6 diff --git a/src/portable/microchip/samd21/dcd_samd21.c b/src/portable/microchip/samd21/dcd_samd21.c index 828b47018..9f04ac062 100644 --- a/src/portable/microchip/samd21/dcd_samd21.c +++ b/src/portable/microchip/samd21/dcd_samd21.c @@ -28,8 +28,8 @@ #if TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_SAMD21 -#include "device/dcd.h" #include "sam.h" +#include "device/dcd.h" /*------------------------------------------------------------------*/ /* MACRO TYPEDEF CONSTANT ENUM diff --git a/src/portable/microchip/samd51/dcd_samd51.c b/src/portable/microchip/samd51/dcd_samd51.c index abf89c32e..6ece35ac0 100644 --- a/src/portable/microchip/samd51/dcd_samd51.c +++ b/src/portable/microchip/samd51/dcd_samd51.c @@ -28,8 +28,8 @@ #if TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_SAMD51 -#include "device/dcd.h" #include "sam.h" +#include "device/dcd.h" /*------------------------------------------------------------------*/ /* MACRO TYPEDEF CONSTANT ENUM -- cgit v1.3.1 From 988edce74530a585a4ffa40a7d955d0208892539 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 9 Dec 2019 18:49:38 +0700 Subject: fix build --- src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c | 3 ++- tools/build_all.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src/portable') diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c index f52e113b7..a93a282e0 100644 --- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c @@ -294,7 +294,8 @@ 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) { - (void)rhport; + (void) rhport; + (void) dev_addr; // Respond with status dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); diff --git a/tools/build_all.py b/tools/build_all.py index 9f35aac63..2ea898f45 100644 --- a/tools/build_all.py +++ b/tools/build_all.py @@ -36,7 +36,7 @@ for entry in os.scandir("hw/bsp"): def build_example(example, board): subprocess.run("make -C examples/device/{} BOARD={} clean".format(example, board), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - return subprocess.run("make -j 8 -C examples/device/{} BOARD={} all".format(example, board), shell=True, + return subprocess.run("make -j 4 -C examples/device/{} BOARD={} all".format(example, board), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) -- cgit v1.3.1 From 759fa7628093fd1f92ae188db8d1a98b22d548f0 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 9 Dec 2019 21:36:15 +0700 Subject: implementing dcd_edpt_open --- hw/bsp/samg55xplained/hpl_usart_config.h | 215 ++++++++++++++++++++++++++ hw/bsp/samg55xplained/peripheral_clk_config.h | 85 ++++++++++ src/portable/microchip/samg/dcd_samg.c | 44 ++++-- 3 files changed, 334 insertions(+), 10 deletions(-) create mode 100644 hw/bsp/samg55xplained/hpl_usart_config.h create mode 100644 hw/bsp/samg55xplained/peripheral_clk_config.h (limited to 'src/portable') diff --git a/hw/bsp/samg55xplained/hpl_usart_config.h b/hw/bsp/samg55xplained/hpl_usart_config.h new file mode 100644 index 000000000..4f2837d2e --- /dev/null +++ b/hw/bsp/samg55xplained/hpl_usart_config.h @@ -0,0 +1,215 @@ +/* Auto-generated config file hpl_usart_config.h */ +#ifndef HPL_USART_CONFIG_H +#define HPL_USART_CONFIG_H + +// <<< Use Configuration Wizard in Context Menu >>> + +#include + +#ifndef CONF_USART_7_ENABLE +#define CONF_USART_7_ENABLE 1 +#endif + +// Basic Configuration + +// Frame parity +// <0x0=>Even parity +// <0x1=>Odd parity +// <0x2=>Parity forced to 0 +// <0x3=>Parity forced to 1 +// <0x4=>No parity +// Parity bit mode for USART frame +// usart_parity +#ifndef CONF_USART_7_PARITY +#define CONF_USART_7_PARITY 0x4 +#endif + +// Character Size +// <0x0=>5 bits +// <0x1=>6 bits +// <0x2=>7 bits +// <0x3=>8 bits +// Data character size in USART frame +// usart_character_size +#ifndef CONF_USART_7_CHSIZE +#define CONF_USART_7_CHSIZE 0x3 +#endif + +// Stop Bit +// <0=>1 stop bit +// <1=>1.5 stop bits +// <2=>2 stop bits +// Number of stop bits in USART frame +// usart_stop_bit +#ifndef CONF_USART_7_SBMODE +#define CONF_USART_7_SBMODE 0 +#endif + +// Clock Output Select +// <0=>The USART does not drive the SCK pin +// <1=>The USART drives the SCK pin if USCLKS does not select the external clock SCK +// Clock Output Select in USART sck, if in usrt master mode, please drive SCK. +// usart_clock_output_select +#ifndef CONF_USART_7_CLKO +#define CONF_USART_7_CLKO 0 +#endif + +// Baud rate <1-3000000> +// USART baud rate setting +// usart_baud_rate +#ifndef CONF_USART_7_BAUD +#define CONF_USART_7_BAUD 9600 +#endif + +// + +// Advanced configuration +// usart_advanced +#ifndef CONF_USART_7_ADVANCED_CONFIG +#define CONF_USART_7_ADVANCED_CONFIG 0 +#endif + +// Channel Mode +// <0=>Normal Mode +// <1=>Automatic Echo +// <2=>Local Loopback +// <3=>Remote Loopback +// Channel mode in USART frame +// usart_channel_mode +#ifndef CONF_USART_7_CHMODE +#define CONF_USART_7_CHMODE 0 +#endif + +// 9 bits character enable +// Enable 9 bits character, this has high priority than 5/6/7/8 bits. +// usart_9bits_enable +#ifndef CONF_USART_7_MODE9 +#define CONF_USART_7_MODE9 0 +#endif + +// Variable Sync +// <0=>User defined configuration +// <1=>sync field is updated when a character is written into US_THR +// Variable Synchronization of Command/Data Sync Start Frarm Delimiter +// variable_sync +#ifndef CONF_USART_7_VAR_SYNC +#define CONF_USART_7_VAR_SYNC 0 +#endif + +// Oversampling Mode +// <0=>16 Oversampling +// <1=>8 Oversampling +// Oversampling Mode in UART mode +// usart__oversampling_mode +#ifndef CONF_USART_7_OVER +#define CONF_USART_7_OVER 0 +#endif + +// Inhibit Non Ack +// <0=>The NACK is generated +// <1=>The NACK is not generated +// Inhibit Non Acknowledge +// usart__inack +#ifndef CONF_USART_7_INACK +#define CONF_USART_7_INACK 1 +#endif + +// Disable Successive NACK +// <0=>NACK is sent on the ISO line as soon as a parity error occurs +// <1=>Many parity errors generate a NACK on the ISO line +// Disable Successive NACK +// usart_dsnack +#ifndef CONF_USART_7_DSNACK +#define CONF_USART_7_DSNACK 0 +#endif + +// Inverted Data +// <0=>Data isn't inverted, nomal mode +// <1=>Data is inverted +// Inverted Data +// usart_invdata +#ifndef CONF_USART_7_INVDATA +#define CONF_USART_7_INVDATA 0 +#endif + +// Maximum Number of Automatic Iteration <0-7> +// Defines the maximum number of iterations in mode ISO7816, protocol T = 0. +// usart_max_iteration +#ifndef CONF_USART_7_MAX_ITERATION +#define CONF_USART_7_MAX_ITERATION 0 +#endif + +// Receive Line Filter enable +// whether the USART filters the receive line using a three-sample filter +// usart_receive_filter_enable +#ifndef CONF_USART_7_FILTER +#define CONF_USART_7_FILTER 0 +#endif + +// Manchester Encoder/Decoder Enable +// whether the USART Manchester Encoder/Decoder +// usart_manchester_filter_enable +#ifndef CONF_USART_7_MAN +#define CONF_USART_7_MAN 0 +#endif + +// Manchester Synchronization Mode +// <0=>The Manchester start bit is a 0 to 1 transition +// <1=>The Manchester start bit is a 1 to 0 transition +// Manchester Synchronization Mode +// usart_manchester_synchronization_mode +#ifndef CONF_USART_7_MODSYNC +#define CONF_USART_7_MODSYNC 0 +#endif + +// Start Frame Delimiter Selector +// <0=>Start frame delimiter is COMMAND or DATA SYNC +// <1=>Start frame delimiter is one bit +// Start Frame Delimiter Selector +// usart_start_frame_delimiter +#ifndef CONF_USART_7_ONEBIT +#define CONF_USART_7_ONEBIT 0 +#endif + +// Fractional Part <0-7> +// Fractional part of the baud rate if baud rate generator is in fractional mode +// usart_arch_fractional +#ifndef CONF_USART_7_FRACTIONAL +#define CONF_USART_7_FRACTIONAL 0x0 +#endif + +// Data Order +// <0=>LSB is transmitted first +// <1=>MSB is transmitted first +// Data order of the data bits in the frame +// usart_arch_msbf +#ifndef CONF_USART_7_MSBF +#define CONF_USART_7_MSBF 0 +#endif + +// + +#define CONF_USART_7_MODE 0x0 + +// Calculate BAUD register value in UART mode +#if CONF_FLEXCOM7_CK_SRC < 3 +#ifndef CONF_USART_7_BAUD_CD +#define CONF_USART_7_BAUD_CD ((CONF_FLEXCOM7_FREQUENCY) / CONF_USART_7_BAUD / 8 / (2 - CONF_USART_7_OVER)) +#endif +#ifndef CONF_USART_7_BAUD_FP +#define CONF_USART_7_BAUD_FP \ + ((CONF_FLEXCOM7_FREQUENCY) / CONF_USART_7_BAUD / (2 - CONF_USART_7_OVER) - 8 * CONF_USART_7_BAUD_CD) +#endif +#elif CONF_FLEXCOM7_CK_SRC == 3 +// No division is active. The value written in US_BRGR has no effect. +#ifndef CONF_USART_7_BAUD_CD +#define CONF_USART_7_BAUD_CD 1 +#endif +#ifndef CONF_USART_7_BAUD_FP +#define CONF_USART_7_BAUD_FP 1 +#endif +#endif + +// <<< end of configuration section >>> + +#endif // HPL_USART_CONFIG_H diff --git a/hw/bsp/samg55xplained/peripheral_clk_config.h b/hw/bsp/samg55xplained/peripheral_clk_config.h new file mode 100644 index 000000000..6d390f388 --- /dev/null +++ b/hw/bsp/samg55xplained/peripheral_clk_config.h @@ -0,0 +1,85 @@ +/* Auto-generated config file peripheral_clk_config.h */ +#ifndef PERIPHERAL_CLK_CONFIG_H +#define PERIPHERAL_CLK_CONFIG_H + +// <<< Use Configuration Wizard in Context Menu >>> + +/** + * \def CONF_HCLK_FREQUENCY + * \brief HCLK's Clock frequency + */ +#ifndef CONF_HCLK_FREQUENCY +#define CONF_HCLK_FREQUENCY 8000000 +#endif + +/** + * \def CONF_FCLK_FREQUENCY + * \brief FCLK's Clock frequency + */ +#ifndef CONF_FCLK_FREQUENCY +#define CONF_FCLK_FREQUENCY 8000000 +#endif + +/** + * \def CONF_CPU_FREQUENCY + * \brief CPU's Clock frequency + */ +#ifndef CONF_CPU_FREQUENCY +#define CONF_CPU_FREQUENCY 8000000 +#endif + +/** + * \def CONF_SLCK_FREQUENCY + * \brief Slow Clock frequency + */ +#define CONF_SLCK_FREQUENCY 32768 + +/** + * \def CONF_MCK_FREQUENCY + * \brief Master Clock frequency + */ +#define CONF_MCK_FREQUENCY 8000000 + +// USB Clock Source +// <0=> USB Clock Controller (USB_48M) +// usb_clock_source +// Select the clock source for USB. +#ifndef CONF_UDP_SRC +#define CONF_UDP_SRC 0 +#endif + +/** + * \def CONF_UDP_FREQUENCY + * \brief UDP's Clock frequency + */ +#ifndef CONF_UDP_FREQUENCY +#define CONF_UDP_FREQUENCY 48005120 +#endif + +// FLEXCOM Clock Settings +// FLEXCOM Clock source +// <0=> Master Clock (MCK) +// <1=> MCK / 8 +// <2=> Programmable Clock Controller 6 (PMC_PCK6) +// <2=> Programmable Clock Controller 7 (PMC_PCK7) +// <3=> External Clock +// This defines the clock source for the FLEXCOM, PCK6 is used for FLEXCOM0/1/2/3 and PCK7 is used for FLEXCOM4/5/6/7 +// flexcom_clock_source +#ifndef CONF_FLEXCOM7_CK_SRC +#define CONF_FLEXCOM7_CK_SRC 0 +#endif + +// FLEXCOM External Clock Input on SCK <1-4294967295> +// Inputs the external clock frequency on SCK +// flexcom_clock_freq +#ifndef CONF_FLEXCOM7_SCK_FREQ +#define CONF_FLEXCOM7_SCK_FREQ 10000000 +#endif + +#ifndef CONF_FLEXCOM7_FREQUENCY +#define CONF_FLEXCOM7_FREQUENCY 8000000 +#endif + +// <<< end of configuration section >>> + +#endif // PERIPHERAL_CLK_CONFIG_H diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c index 122dc04b7..f29c5d79b 100644 --- a/src/portable/microchip/samg/dcd_samg.c +++ b/src/portable/microchip/samg/dcd_samg.c @@ -35,6 +35,8 @@ // MACRO TYPEDEF CONSTANT ENUM DECLARATION //--------------------------------------------------------------------+ +#define EP_COUNT 6 + // Transfer descriptor typedef struct { @@ -44,8 +46,8 @@ typedef struct uint16_t epsize; } xfer_desc_t; -// Endpoint 0-5 with OUT & IN -xfer_desc_t _dcd_xfer[6][2]; +// Endpoint 0-5, each can only be either OUT or In +xfer_desc_t _dcd_xfer[EP_COUNT]; void xfer_begin(xfer_desc_t* xfer, uint8_t * buffer, uint16_t total_bytes) { @@ -77,7 +79,7 @@ static void bus_reset(void) { tu_memclr(_dcd_xfer, sizeof(_dcd_xfer)); - _dcd_xfer[0][0].epsize = _dcd_xfer[0][1].epsize = CFG_TUD_ENDPOINT0_SIZE; + _dcd_xfer[0].epsize = CFG_TUD_ENDPOINT0_SIZE; // Enable EP0 control UDP->UDP_CSR[0] = UDP_CSR_EPEDS_Msk; @@ -163,11 +165,29 @@ void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * re } // Configure endpoint's registers according to descriptor +// SAMG doesnt support using a same endpoint with IN and OUT +// e.g EP1 OUT & EP1 IN cannot exist together bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) { (void) rhport; - (void) ep_desc; - return false; + + uint8_t const epnum = tu_edpt_number(ep_desc->bEndpointAddress); + uint8_t const dir = tu_edpt_dir(ep_desc->bEndpointAddress); + + // TODO Isochronous is not supported yet + TU_VERIFY(ep_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS); + TU_VERIFY(epnum < EP_COUNT); + + // Must not already enabled + TU_ASSERT((UDP->UDP_CSR[epnum] & UDP_CSR_EPEDS_Msk) == 0); + + // Configure type and eanble EP + UDP->UDP_CSR[epnum] = UDP_CSR_EPEDS_Msk | UDP_CSR_EPTYPE(ep_desc->bmAttributes.xfer + 4*dir); + + // Enable EP Interrupt + UDP->UDP_IER |= (1 << epnum); + + return true; } // Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack @@ -178,7 +198,7 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const dir = tu_edpt_dir(ep_addr); - xfer_desc_t* xfer = &_dcd_xfer[epnum][dir]; + xfer_desc_t* xfer = &_dcd_xfer[epnum]; xfer_begin(xfer, buffer, total_bytes); uint16_t const xact_len = xfer_packet_len(xfer); @@ -217,7 +237,6 @@ void dcd_edpt_stall (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); // Set force stall bit UDP->UDP_CSR[epnum] |= UDP_CSR_FORCESTALL_Msk; @@ -227,7 +246,11 @@ void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr) void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) { (void) rhport; - (void) ep_addr; + + uint8_t const epnum = tu_edpt_number(ep_addr); + + // clear stall, must also clear data toggle + UDP->UDP_CSR[epnum] &= ~UDP_CSR_FORCESTALL_Msk; } //--------------------------------------------------------------------+ @@ -282,12 +305,13 @@ void dcd_isr(uint8_t rhport) } } - for(uint8_t epnum = 0; epnum < 6; epnum++) + for(uint8_t epnum = 0; epnum < EP_COUNT; epnum++) { + xfer_desc_t* xfer = &_dcd_xfer[epnum]; + // Endpoint IN if (UDP->UDP_CSR[epnum] & UDP_CSR_TXCOMP_Msk) { - xfer_desc_t* xfer = &_dcd_xfer[epnum][1]; uint16_t xact_len = xfer_packet_len(xfer); xfer_packet_done(xfer); -- cgit v1.3.1 From 0316e0ecd4ba10324f53c341480949fe025b8869 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 9 Dec 2019 22:48:35 +0700 Subject: update descriptor, enhance ep in transfer --- examples/device/cdc_msc/src/usb_descriptors.c | 34 +++++++++++++++----- src/portable/microchip/samg/dcd_samg.c | 45 +++++++++++++++++---------- 2 files changed, 56 insertions(+), 23 deletions(-) (limited to 'src/portable') diff --git a/examples/device/cdc_msc/src/usb_descriptors.c b/examples/device/cdc_msc/src/usb_descriptors.c index 11d07e9ba..e8527df0b 100644 --- a/examples/device/cdc_msc/src/usb_descriptors.c +++ b/examples/device/cdc_msc/src/usb_descriptors.c @@ -86,12 +86,32 @@ enum #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 ... - // Note: since CDC EP ( 1 & 2), HID (4) are spot-on, thus we only need to force - // endpoint number for MSC to 5 - #define EPNUM_MSC 0x05 + // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In, 5 Bulk etc ... + #define EPNUM_CDC_NOTIF 0x81 + #define EPNUM_CDC_OUT 0x02 + #define EPNUM_CDC_IN 0x82 + + #define EPNUM_MSC_OUT 0x05 + #define EPNUM_MSC_IN 0x85 + +#elif CFG_TUSB_MCU == OPT_MCU_SAMG + // SAMG doesn't support a same endpoint number with IN and OUT + // e.g EP1 OUT & EP1 IN cannot exist together + #define EPNUM_CDC_NOTIF 0x81 + #define EPNUM_CDC_OUT 0x02 + #define EPNUM_CDC_IN 0x83 + + #define EPNUM_MSC_OUT 0x04 + #define EPNUM_MSC_IN 0x85 + #else - #define EPNUM_MSC 0x03 + #define EPNUM_CDC_NOTIF 0x81 + #define EPNUM_CDC_OUT 0x02 + #define EPNUM_CDC_IN 0x82 + + #define EPNUM_MSC_OUT 0x03 + #define EPNUM_MSC_IN 0x83 + #endif uint8_t const desc_configuration[] = @@ -100,10 +120,10 @@ uint8_t const desc_configuration[] = TUD_CONFIG_DESCRIPTOR(ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), // Interface number, string index, EP notification address and size, EP data address (out, in) and size. - TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, 0x81, 8, 0x02, 0x82, 64), + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64), // Interface number, string index, EP Out & EP In address, EP size - TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC, 0x80 | EPNUM_MSC, (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 512 : 64), + TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 512 : 64), }; diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c index f29c5d79b..06e9902d5 100644 --- a/src/portable/microchip/samg/dcd_samg.c +++ b/src/portable/microchip/samg/dcd_samg.c @@ -70,6 +70,17 @@ void xfer_packet_done(xfer_desc_t* xfer) xfer->actual_len += xact_len; } +//------------- Transaction helpers -------------// +static uint16_t xact_in(uint8_t epnum, xfer_desc_t* xfer) +{ + uint16_t const xact_len = xfer_packet_len(xfer); + + // Write data to fifo + for(uint16_t i=0; iUDP_FDR[epnum] = (uint32_t) xfer->buffer[i]; + + return xact_len; +} + /*------------------------------------------------------------------*/ /* Device API *------------------------------------------------------------------*/ @@ -165,7 +176,7 @@ void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * re } // Configure endpoint's registers according to descriptor -// SAMG doesnt support using a same endpoint with IN and OUT +// SAMG doesn't support a same endpoint number with IN and OUT // e.g EP1 OUT & EP1 IN cannot exist together bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) { @@ -201,31 +212,26 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t xfer_desc_t* xfer = &_dcd_xfer[epnum]; xfer_begin(xfer, buffer, total_bytes); - uint16_t const xact_len = xfer_packet_len(xfer); - - // control endpoint + // Configure DIR bit for control endpoint if ( epnum == 0 ) { if (dir == TUSB_DIR_OUT) { // Clear DIR bit UDP->UDP_CSR[0] &= ~UDP_CSR_DIR_Msk; - }else { - // Set DIR bit if needed + // Set DIR bit UDP->UDP_CSR[0] |= UDP_CSR_DIR_Msk; - - // Write data to fifo - for(uint16_t i=0; iUDP_FDR[0] = (uint32_t) buffer[i]; - - // TX ready for transfer - UDP->UDP_CSR[0] |= UDP_CSR_TXPKTRDY_Msk; } + } - }else + if (dir == TUSB_DIR_IN) { - return false; + xact_in(epnum, xfer); + + // TX ready for transfer + UDP->UDP_CSR[epnum] |= UDP_CSR_TXPKTRDY_Msk; } return true; @@ -312,10 +318,17 @@ void dcd_isr(uint8_t rhport) // Endpoint IN if (UDP->UDP_CSR[epnum] & UDP_CSR_TXCOMP_Msk) { - uint16_t xact_len = xfer_packet_len(xfer); xfer_packet_done(xfer); - dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xact_len, XFER_RESULT_SUCCESS, true); + if ( xact_in(epnum, xfer) ) + { + // TX ready for transfer + UDP->UDP_CSR[epnum] |= UDP_CSR_TXPKTRDY_Msk; + }else + { + // xfer is complete + dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true); + } // Clear TX Complete bit UDP->UDP_CSR[0] &= ~UDP_CSR_TXCOMP_Msk; -- cgit v1.3.1 From 75a2157715ba9977bd9b193b76f1d4182e24d760 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 9 Dec 2019 23:20:21 +0700 Subject: fix samd using TUSB_REQ_SET_ADDRESS in dcd_edpt0_status_complete --- src/portable/microchip/samd21/dcd_samd21.c | 4 +++- src/portable/microchip/samd51/dcd_samd51.c | 6 +++--- src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c | 4 +++- 3 files changed, 9 insertions(+), 5 deletions(-) (limited to 'src/portable') diff --git a/src/portable/microchip/samd21/dcd_samd21.c b/src/portable/microchip/samd21/dcd_samd21.c index 9f04ac062..5e44dc387 100644 --- a/src/portable/microchip/samd21/dcd_samd21.c +++ b/src/portable/microchip/samd21/dcd_samd21.c @@ -133,7 +133,9 @@ void dcd_remote_wakeup(uint8_t rhport) // 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) { - if (request->bRequest == TUSB_REQ_SET_ADDRESS) + if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && + request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && + request->bRequest == TUSB_REQ_SET_ADDRESS ) { uint8_t const dev_addr = (uint8_t) request->wValue; USB->DEVICE.DADD.reg = USB_DEVICE_DADD_DADD(dev_addr) | USB_DEVICE_DADD_ADDEN; diff --git a/src/portable/microchip/samd51/dcd_samd51.c b/src/portable/microchip/samd51/dcd_samd51.c index 6ece35ac0..c298acc53 100644 --- a/src/portable/microchip/samd51/dcd_samd51.c +++ b/src/portable/microchip/samd51/dcd_samd51.c @@ -139,9 +139,9 @@ void dcd_remote_wakeup(uint8_t rhport) // 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) { - (void) rhport; - - if (request->bRequest == TUSB_REQ_SET_ADDRESS) + if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && + request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && + request->bRequest == TUSB_REQ_SET_ADDRESS ) { uint8_t const dev_addr = (uint8_t) request->wValue; USB->DEVICE.DADD.reg = USB_DEVICE_DADD_DADD(dev_addr) | USB_DEVICE_DADD_ADDEN; diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c index a93a282e0..341aa5f55 100644 --- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c @@ -598,7 +598,9 @@ void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * re { (void) rhport; - if (request->bRequest == TUSB_REQ_SET_ADDRESS) + if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && + request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && + request->bRequest == TUSB_REQ_SET_ADDRESS ) { uint8_t const dev_addr = (uint8_t) request->wValue; -- cgit v1.3.1 From fa9607888204b2028f0f53340232a5d4cc47640d Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 9 Dec 2019 23:21:06 +0700 Subject: clear data toggle in clear stall, also set config bit --- src/portable/microchip/samg/dcd_samg.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) (limited to 'src/portable') diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c index 06e9902d5..f3efdeedf 100644 --- a/src/portable/microchip/samg/dcd_samg.c +++ b/src/portable/microchip/samg/dcd_samg.c @@ -145,6 +145,9 @@ void dcd_set_config (uint8_t rhport, uint8_t config_num) { (void) rhport; (void) config_num; + + // Configured State +// UDP->UDP_GLB_STAT |= UDP_GLB_STAT_CONFG_Msk; } // Wake up host @@ -163,15 +166,23 @@ void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * re { (void) rhport; - if (request->bRequest == TUSB_REQ_SET_ADDRESS) + if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && + request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD ) { - uint8_t const dev_addr = (uint8_t) request->wValue; + if (request->bRequest == TUSB_REQ_SET_ADDRESS) + { + uint8_t const dev_addr = (uint8_t) request->wValue; - // Enable addressed state - UDP->UDP_GLB_STAT |= UDP_GLB_STAT_FADDEN_Msk; + // Enable addressed state + UDP->UDP_GLB_STAT |= UDP_GLB_STAT_FADDEN_Msk; - // Set new address & Function enable bit - UDP->UDP_FADDR = UDP_FADDR_FEN_Msk | UDP_FADDR_FADD(dev_addr); + // Set new address & Function enable bit + UDP->UDP_FADDR = UDP_FADDR_FEN_Msk | UDP_FADDR_FADD(dev_addr); + }else if (request->bRequest == TUSB_REQ_SET_CONFIGURATION) + { + // Configured State + UDP->UDP_GLB_STAT |= UDP_GLB_STAT_CONFG_Msk; + } } } @@ -255,8 +266,12 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) uint8_t const epnum = tu_edpt_number(ep_addr); - // clear stall, must also clear data toggle + // clear stall UDP->UDP_CSR[epnum] &= ~UDP_CSR_FORCESTALL_Msk; + + // must also reset EP to clear data toggle + UDP->UDP_RST_EP = tu_bit_set(UDP->UDP_RST_EP, epnum); + UDP->UDP_RST_EP = tu_bit_clear(UDP->UDP_RST_EP, epnum); } //--------------------------------------------------------------------+ -- cgit v1.3.1 From 752aea7d723f5301f90a48a0888507fb333e86f8 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 9 Dec 2019 23:58:13 +0700 Subject: enhance samg --- src/portable/microchip/samg/dcd_samg.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/portable') diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c index f3efdeedf..f05763e38 100644 --- a/src/portable/microchip/samg/dcd_samg.c +++ b/src/portable/microchip/samg/dcd_samg.c @@ -31,6 +31,8 @@ #include "sam.h" #include "device/dcd.h" +// TODO should support (SAM3S || SAM4S || SAM4E || SAMG55) + //--------------------------------------------------------------------+ // MACRO TYPEDEF CONSTANT ENUM DECLARATION //--------------------------------------------------------------------+ @@ -346,24 +348,24 @@ void dcd_isr(uint8_t rhport) } // Clear TX Complete bit - UDP->UDP_CSR[0] &= ~UDP_CSR_TXCOMP_Msk; + UDP->UDP_CSR[epnum] &= ~UDP_CSR_TXCOMP_Msk; } // Endpoint OUT if (UDP->UDP_CSR[epnum] & UDP_CSR_RX_DATA_BK0_Msk) { - uint16_t const xact_len = (uint16_t) ((UDP->UDP_CSR[0] & UDP_CSR_RXBYTECNT_Msk) >> UDP_CSR_RXBYTECNT_Pos); + uint16_t const xact_len = (uint16_t) ((UDP->UDP_CSR[epnum] & UDP_CSR_RXBYTECNT_Msk) >> UDP_CSR_RXBYTECNT_Pos); dcd_event_xfer_complete(rhport, epnum, xact_len, XFER_RESULT_SUCCESS, true); // Clear DATA Bank0 bit - UDP->UDP_CSR[0] &= ~UDP_CSR_RX_DATA_BK0_Msk; + UDP->UDP_CSR[epnum] &= ~UDP_CSR_RX_DATA_BK0_Msk; } // Stall sent to host if (UDP->UDP_CSR[epnum] & UDP_CSR_STALLSENT_Msk) { - UDP->UDP_CSR[0] &= ~UDP_CSR_STALLSENT_Msk; + UDP->UDP_CSR[epnum] &= ~UDP_CSR_STALLSENT_Msk; } } } -- cgit v1.3.1 From 96a9eca6a041c8a7e9e86792f06abd0ef5a187a3 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 11 Dec 2019 14:03:36 +0700 Subject: move VBUS sense out of dcd_synosys to bsp --- hw/bsp/feather_stm32f405/feather_stm32f405.c | 10 +++-- hw/bsp/pyboardv11/pyboardv11.c | 24 ++++++++---- hw/bsp/stm32f207nucleo/stm32f207nucleo.c | 24 +++++++++--- hw/bsp/stm32f407disco/stm32f407disco.c | 18 ++++++--- hw/bsp/stm32f411disco/stm32f411disco.c | 23 +++++++---- hw/bsp/stm32f412disco/stm32f412disco.c | 17 +++++--- hw/bsp/stm32f767nucleo/stm32f767nucleo.c | 3 ++ hw/bsp/stm32h743nucleo/stm32h743nucleo.c | 58 +++++++++++++++++----------- hw/bsp/stm32l476disco/stm32l476disco.c | 22 +++++++---- src/portable/st/synopsys/dcd_synopsys.c | 8 +--- 10 files changed, 136 insertions(+), 71 deletions(-) (limited to 'src/portable') diff --git a/hw/bsp/feather_stm32f405/feather_stm32f405.c b/hw/bsp/feather_stm32f405/feather_stm32f405.c index aec779ea3..e8b15b513 100644 --- a/hw/bsp/feather_stm32f405/feather_stm32f405.c +++ b/hw/bsp/feather_stm32f405/feather_stm32f405.c @@ -152,9 +152,6 @@ void board_init(void) GPIO_InitStruct.Alternate = UART_GPIO_AF; HAL_GPIO_Init(UART_GPIO_PORT, &GPIO_InitStruct); - // Enable USB OTG clock - __HAL_RCC_USB_OTG_FS_CLK_ENABLE(); - // USB Pin Init // PA9- VUSB, PA10- ID, PA11- DM, PA12- DP @@ -179,6 +176,13 @@ void board_init(void) GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + // Enable USB OTG clock + __HAL_RCC_USB_OTG_FS_CLK_ENABLE(); + + // Enable VBUS sense (B device) via pin PA9 + USB_OTG_FS->GCCFG &= ~USB_OTG_GCCFG_NOVBUSSENS; + USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_VBUSBSEN; } //--------------------------------------------------------------------+ diff --git a/hw/bsp/pyboardv11/pyboardv11.c b/hw/bsp/pyboardv11/pyboardv11.c index 2d4c732cc..7452eef71 100644 --- a/hw/bsp/pyboardv11/pyboardv11.c +++ b/hw/bsp/pyboardv11/pyboardv11.c @@ -38,6 +38,14 @@ #define BUTTON_PIN GPIO_PIN_3 #define BUTTON_STATE_ACTIVE 1 + +// enable all LED, Button, Uart, USB clock +static void all_rcc_clk_enable(void) +{ + __HAL_RCC_GPIOA_CLK_ENABLE(); // USB D+, D- + __HAL_RCC_GPIOB_CLK_ENABLE(); // LED, Button +} + /** * @brief System Clock Configuration * The system Clock is configured as follow : @@ -107,14 +115,11 @@ void board_init(void) // Notify runtime of frequency change. SystemCoreClockUpdate(); - __HAL_RCC_GPIOA_CLK_ENABLE(); // USB D+/D- - __HAL_RCC_GPIOB_CLK_ENABLE(); // button, LED + all_rcc_clk_enable(); GPIO_InitTypeDef GPIO_InitStruct; // LED - __HAL_RCC_GPIOD_CLK_ENABLE(); - GPIO_InitStruct.Pin = LED_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; @@ -130,12 +135,8 @@ void board_init(void) GPIO_InitStruct.Speed = GPIO_SPEED_FAST; HAL_GPIO_Init(BUTTON_PORT, &GPIO_InitStruct); - // Enable USB OTG clock - __HAL_RCC_USB_OTG_FS_CLK_ENABLE(); - // USB Pin Init // PA9- VUSB, PA10- ID, PA11- DM, PA12- DP - __HAL_RCC_GPIOA_CLK_ENABLE(); /* Configure DM DP Pins */ GPIO_InitStruct.Pin = GPIO_PIN_11 | GPIO_PIN_12; @@ -158,6 +159,13 @@ void board_init(void) GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + // Enable USB OTG clock + __HAL_RCC_USB_OTG_FS_CLK_ENABLE(); + + // Enable VBUS sense (B device) via pin PA9 + USB_OTG_FS->GCCFG &= ~USB_OTG_GCCFG_NOVBUSSENS; + USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_VBUSBSEN; } //--------------------------------------------------------------------+ diff --git a/hw/bsp/stm32f207nucleo/stm32f207nucleo.c b/hw/bsp/stm32f207nucleo/stm32f207nucleo.c index de0b4753b..a09f26cfb 100644 --- a/hw/bsp/stm32f207nucleo/stm32f207nucleo.c +++ b/hw/bsp/stm32f207nucleo/stm32f207nucleo.c @@ -38,6 +38,14 @@ #define BUTTON_STATE_ACTIVE 1 +// enable all LED, Button, Uart, USB clock +static void all_rcc_clk_enable(void) +{ + __HAL_RCC_GPIOA_CLK_ENABLE(); // USB D+, D- + __HAL_RCC_GPIOB_CLK_ENABLE(); // LED + __HAL_RCC_GPIOC_CLK_ENABLE(); // Button +} + /** * @brief System Clock Configuration * The system Clock is configured as follow : @@ -91,21 +99,22 @@ void board_init(void) #endif SystemClock_Config(); - - // Notify runtime of frequency change. SystemCoreClockUpdate(); - // LED - __HAL_RCC_GPIOB_CLK_ENABLE(); + all_rcc_clk_enable(); + GPIO_InitTypeDef GPIO_InitStruct; + + // LED GPIO_InitStruct.Pin = LED_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(LED_PORT, &GPIO_InitStruct); + board_led_write(false); + // Button - __HAL_RCC_GPIOC_CLK_ENABLE(); GPIO_InitStruct.Pin = BUTTON_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLDOWN; @@ -113,7 +122,6 @@ void board_init(void) HAL_GPIO_Init(BUTTON_PORT, &GPIO_InitStruct); /* Configure DM DP Pins */ - __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = (GPIO_PIN_11 | GPIO_PIN_12); GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; @@ -136,6 +144,10 @@ void board_init(void) /* Enable USB FS Clocks */ __HAL_RCC_USB_OTG_FS_CLK_ENABLE(); + + // Enable VBUS sense (B device) via pin PA9 + USB_OTG_FS->GCCFG &= ~USB_OTG_GCCFG_NOVBUSSENS; + USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_VBUSBSEN; } //--------------------------------------------------------------------+ diff --git a/hw/bsp/stm32f407disco/stm32f407disco.c b/hw/bsp/stm32f407disco/stm32f407disco.c index b80c5c1b3..42cb613c8 100644 --- a/hw/bsp/stm32f407disco/stm32f407disco.c +++ b/hw/bsp/stm32f407disco/stm32f407disco.c @@ -37,6 +37,14 @@ #define BUTTON_PIN GPIO_PIN_0 #define BUTTON_STATE_ACTIVE 1 + +// enable all LED, Button, Uart, USB clock +static void all_rcc_clk_enable(void) +{ + __HAL_RCC_GPIOA_CLK_ENABLE(); // USB D+, D-, Button + __HAL_RCC_GPIOD_CLK_ENABLE(); // LED +} + /** * @brief System Clock Configuration * The system Clock is configured as follow : @@ -102,14 +110,12 @@ void board_init(void) #endif SystemClock_Config(); - - // Notify runtime of frequency change. SystemCoreClockUpdate(); + all_rcc_clk_enable(); GPIO_InitTypeDef GPIO_InitStruct; // LED - __HAL_RCC_GPIOD_CLK_ENABLE(); GPIO_InitStruct.Pin = LED_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; @@ -119,7 +125,6 @@ void board_init(void) board_led_write(false); // Button - __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = BUTTON_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLDOWN; @@ -129,7 +134,6 @@ void board_init(void) // USB Pin Init // PA9- VUSB, PA10- ID, PA11- DM, PA12- DP /* Configure DM DP Pins */ - __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_11 | GPIO_PIN_12; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; @@ -153,6 +157,10 @@ void board_init(void) // Enable USB OTG clock __HAL_RCC_USB_OTG_FS_CLK_ENABLE(); + + // Enable VBUS sense (B device) via pin PA9 + USB_OTG_FS->GCCFG &= ~USB_OTG_GCCFG_NOVBUSSENS; + USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_VBUSBSEN; } //--------------------------------------------------------------------+ diff --git a/hw/bsp/stm32f411disco/stm32f411disco.c b/hw/bsp/stm32f411disco/stm32f411disco.c index d8d79d2f0..9d9c30715 100644 --- a/hw/bsp/stm32f411disco/stm32f411disco.c +++ b/hw/bsp/stm32f411disco/stm32f411disco.c @@ -37,6 +37,13 @@ #define BUTTON_PIN GPIO_PIN_0 #define BUTTON_STATE_ACTIVE 1 +// enable all LED, Button, Uart, USB clock +static void all_rcc_clk_enable(void) +{ + __HAL_RCC_GPIOA_CLK_ENABLE(); // USB D+, D-, Button + __HAL_RCC_GPIOD_CLK_ENABLE(); // LED +} + /** * @brief System Clock Configuration * The system Clock is configured as follow : @@ -102,14 +109,12 @@ void board_init(void) #endif SystemClock_Config(); - - // Notify runtime of frequency change. SystemCoreClockUpdate(); + all_rcc_clk_enable(); GPIO_InitTypeDef GPIO_InitStruct; // LED - __HAL_RCC_GPIOD_CLK_ENABLE(); GPIO_InitStruct.Pin = LED_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; @@ -119,19 +124,14 @@ void board_init(void) board_led_write(false); // Button - __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = BUTTON_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLDOWN; GPIO_InitStruct.Speed = GPIO_SPEED_FAST; HAL_GPIO_Init(BUTTON_PORT, &GPIO_InitStruct); - // Enable USB OTG clock - __HAL_RCC_USB_OTG_FS_CLK_ENABLE(); - /* Configure USB FS GPIOs */ /* Configure USB D+ D- Pins */ - __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_11 | GPIO_PIN_12; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; @@ -152,6 +152,13 @@ void board_init(void) GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + // Enable USB OTG clock + __HAL_RCC_USB_OTG_FS_CLK_ENABLE(); + + // Enable VBUS sense (B device) via pin PA9 + USB_OTG_FS->GCCFG &= ~USB_OTG_GCCFG_NOVBUSSENS; + USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_VBUSBSEN; } //--------------------------------------------------------------------+ diff --git a/hw/bsp/stm32f412disco/stm32f412disco.c b/hw/bsp/stm32f412disco/stm32f412disco.c index 8d93013c1..10778bcfc 100644 --- a/hw/bsp/stm32f412disco/stm32f412disco.c +++ b/hw/bsp/stm32f412disco/stm32f412disco.c @@ -38,6 +38,14 @@ #define BUTTON_PIN GPIO_PIN_0 #define BUTTON_STATE_ACTIVE 1 + +// enable all LED, Button, Uart, USB clock +static void all_rcc_clk_enable(void) +{ + __HAL_RCC_GPIOA_CLK_ENABLE(); // USB D+, D-, Button + __HAL_RCC_GPIOE_CLK_ENABLE(); // LED +} + /** * @brief System Clock Configuration * The system Clock is configured as follow : @@ -114,9 +122,8 @@ static void SystemClock_Config(void) void board_init(void) { SystemClock_Config(); - - // Notify runtime of frequency change. SystemCoreClockUpdate(); + all_rcc_clk_enable(); #if CFG_TUSB_OS == OPT_OS_NONE // 1ms tick timer @@ -129,7 +136,6 @@ void board_init(void) GPIO_InitTypeDef GPIO_InitStruct; // LED - __HAL_RCC_GPIOE_CLK_ENABLE(); GPIO_InitStruct.Pin = LED_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; @@ -139,7 +145,6 @@ void board_init(void) board_led_write(false); // Button - __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = BUTTON_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLDOWN; @@ -148,7 +153,6 @@ void board_init(void) /* Configure USB FS GPIOs */ /* Configure USB D+ D- Pins */ - __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_11 | GPIO_PIN_12; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; @@ -179,6 +183,9 @@ void board_init(void) // Enable USB OTG clock __HAL_RCC_USB_OTG_FS_CLK_ENABLE(); + + // Enable VBUS sense (B device) via pin PA9 + USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_VBDEN; } //--------------------------------------------------------------------+ diff --git a/hw/bsp/stm32f767nucleo/stm32f767nucleo.c b/hw/bsp/stm32f767nucleo/stm32f767nucleo.c index 13966438f..7367c56b8 100644 --- a/hw/bsp/stm32f767nucleo/stm32f767nucleo.c +++ b/hw/bsp/stm32f767nucleo/stm32f767nucleo.c @@ -189,6 +189,9 @@ void board_init(void) /* Enable USB FS Clocks */ __HAL_RCC_USB_OTG_FS_CLK_ENABLE(); + + // Enable VBUS sense (B device) via pin PA9 + USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_VBDEN; } //--------------------------------------------------------------------+ diff --git a/hw/bsp/stm32h743nucleo/stm32h743nucleo.c b/hw/bsp/stm32h743nucleo/stm32h743nucleo.c index f7e46a158..b7071ed64 100644 --- a/hw/bsp/stm32h743nucleo/stm32h743nucleo.c +++ b/hw/bsp/stm32h743nucleo/stm32h743nucleo.c @@ -30,10 +30,23 @@ #include "stm32h7xx.h" #include "stm32h7xx_hal_conf.h" -#define LED_PORT GPIOB -#define LED_PIN GPIO_PIN_0 -#define BUTTON_PORT GPIOC -#define BUTTON_PIN GPIO_PIN_13 +#define LED_PORT GPIOB +#define LED_PIN GPIO_PIN_0 +#define LED_STATE_ON 1 + +#define BUTTON_PORT GPIOC +#define BUTTON_PIN GPIO_PIN_13 +#define BUTTON_STATE_ACTIVE 1 + +// enable all LED, Button, Uart, USB clock +static void all_rcc_clk_enable(void) +{ + __HAL_RCC_GPIOA_CLK_ENABLE(); // USB D+, D- + __HAL_RCC_GPIOB_CLK_ENABLE(); // LED + __HAL_RCC_GPIOC_CLK_ENABLE(); // Button +// __HAL_RCC_GPIOD_CLK_ENABLE(); // Uart tx, rx +// __HAL_RCC_USART3_CLK_ENABLE(); // Uart module +} /* PWR, RCC, GPIO (All): AHB4 (D3 domain) USB{1,2} OTG_{H,F}S: AHB1 (D2 domain) @@ -127,14 +140,27 @@ void board_init(void) #endif SystemClock_Config(); - SystemCoreClockUpdate(); + all_rcc_clk_enable(); GPIO_InitTypeDef GPIO_InitStruct; + // LED + GPIO_InitStruct.Pin = LED_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = GPIO_PULLUP; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + HAL_GPIO_Init(LED_PORT, &GPIO_InitStruct); + + // Button + GPIO_InitStruct.Pin = BUTTON_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_INPUT; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + HAL_GPIO_Init(BUTTON_PORT, &GPIO_InitStruct); + // USB Pin Init // PA9- VUSB, PA10- ID, PA11- DM, PA12- DP - __HAL_RCC_GPIOA_CLK_ENABLE(); /* Configure DM DP Pins */ GPIO_InitStruct.Pin = GPIO_PIN_11 | GPIO_PIN_12; @@ -158,27 +184,13 @@ void board_init(void) GPIO_InitStruct.Alternate = GPIO_AF10_OTG2_HS; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); - // LED - __HAL_RCC_GPIOB_CLK_ENABLE(); - - GPIO_InitStruct.Pin = LED_PIN; - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; - GPIO_InitStruct.Pull = GPIO_PULLUP; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - HAL_GPIO_Init(LED_PORT, &GPIO_InitStruct); - - // Button - __HAL_RCC_GPIOC_CLK_ENABLE(); - GPIO_InitStruct.Pin = BUTTON_PIN; - GPIO_InitStruct.Mode = GPIO_MODE_INPUT; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - HAL_GPIO_Init(BUTTON_PORT, &GPIO_InitStruct); - // https://community.st.com/s/question/0D50X00009XkYZLSA3/stm32h7-nucleo-usb-fs-cdc // TODO: Board init actually works fine without this line. HAL_PWREx_EnableUSBVoltageDetector(); __HAL_RCC_USB2_OTG_FS_CLK_ENABLE(); + + // Enable VBUS sense (B device) via pin PA9 + USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_VBDEN; } //--------------------------------------------------------------------+ diff --git a/hw/bsp/stm32l476disco/stm32l476disco.c b/hw/bsp/stm32l476disco/stm32l476disco.c index a4cad68bb..ac4ef365c 100644 --- a/hw/bsp/stm32l476disco/stm32l476disco.c +++ b/hw/bsp/stm32l476disco/stm32l476disco.c @@ -38,6 +38,16 @@ #define BUTTON_PIN GPIO_PIN_0 #define BUTTON_STATE_ACTIVE 1 + +// enable all LED, Button, Uart, USB clock +static void all_rcc_clk_enable(void) +{ + __HAL_RCC_GPIOA_CLK_ENABLE(); // USB D+, D-, Button + __HAL_RCC_GPIOB_CLK_ENABLE(); // LED + __HAL_RCC_GPIOC_CLK_ENABLE(); // VBUS pin +} + + /** * @brief System Clock Configuration * The system Clock is configured as follow : @@ -117,6 +127,8 @@ void board_init(void) #endif SystemClock_Config(); + SystemCoreClockUpdate(); + all_rcc_clk_enable(); /* Enable Power Clock*/ __HAL_RCC_PWR_CLK_ENABLE(); @@ -124,13 +136,9 @@ void board_init(void) /* Enable USB power on Pwrctrl CR2 register */ HAL_PWREx_EnableVddUSB(); - // Notify runtime of frequency change. - SystemCoreClockUpdate(); - GPIO_InitTypeDef GPIO_InitStruct; // LED - __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitStruct.Pin = LED_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; @@ -140,7 +148,6 @@ void board_init(void) board_led_write(false); // Button - __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = BUTTON_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLDOWN; @@ -149,7 +156,6 @@ void board_init(void) // USB /* Configure DM DP Pins */ - __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = (GPIO_PIN_11 | GPIO_PIN_12); GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; @@ -158,7 +164,6 @@ void board_init(void) HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* Configure VBUS Pin */ - __HAL_RCC_GPIOC_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_11; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; @@ -166,6 +171,9 @@ void board_init(void) /* Enable USB FS Clock */ __HAL_RCC_USB_OTG_FS_CLK_ENABLE(); + + // Enable VBUS sense (B device) via pin PA9 + USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_VBDEN; } //--------------------------------------------------------------------+ diff --git a/src/portable/st/synopsys/dcd_synopsys.c b/src/portable/st/synopsys/dcd_synopsys.c index 7929b6916..b25cfccae 100644 --- a/src/portable/st/synopsys/dcd_synopsys.c +++ b/src/portable/st/synopsys/dcd_synopsys.c @@ -207,12 +207,8 @@ void dcd_init (uint8_t rhport) USB_OTG_GINTMSK_SOFM | USB_OTG_GINTMSK_RXFLVLM /* SB_OTG_GINTMSK_ESUSPM | \ USB_OTG_GINTMSK_USBSUSPM */; - // Enable VBUS hardware sensing, enable pullup, enable peripheral. -#ifdef USB_OTG_GCCFG_VBDEN - USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_VBDEN | USB_OTG_GCCFG_PWRDWN; -#else - USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_VBUSBSEN | USB_OTG_GCCFG_PWRDWN; -#endif + // Enable USB transceiver. + USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_PWRDWN; // Soft Connect -> Enable pullup on D+/D-. // This step does not appear to be specified in the programmer's model. -- cgit v1.3.1 From 9d96b6d66eab8a8dea44db1a2c346f667d22ce21 Mon Sep 17 00:00:00 2001 From: Jeremy Herbert Date: Thu, 12 Dec 2019 09:40:06 +1000 Subject: Update dcd_stm32_fsdev.c Add a note about pin remapping for the STM32F042Fx --- src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/portable') diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c index ec182960e..9e99ea1de 100644 --- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c @@ -30,7 +30,7 @@ /********************************************** * This driver has been tested with the following MCUs: - * - F070, F072, L053 + * - F070, F072, L053, F042F6 * * It also should work with minimal changes for any ST MCU with an "USB A"/"PCD"/"HCD" peripheral. This * covers: @@ -44,6 +44,9 @@ * L4x2, L4x3 1024 byte buffer * * To use this driver, you must: + * - If you are using a device with crystal-less USB, set up the clock recovery system (CRS) + * - Remap pins to be D+/D- on devices that they are shared (for example: F042Fx) + * - This is different to the normal "alternate function" GPIO interface, needs to go through SYSCFG->CFGRx register * - Enable USB clock; Perhaps use __HAL_RCC_USB_CLK_ENABLE(); * - (Optionally configure GPIO HAL to tell it the USB driver is using the USB pins) * - call tusb_init(); -- cgit v1.3.1 From 050fa2fd394912775994d4171774adb475eaf531 Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 21 Dec 2019 19:33:41 +0700 Subject: able to response to scsi inquiry, but failed to response to test unit ready --- src/class/msc/msc_device.c | 9 ++ src/common/tusb_common.h | 2 +- src/device/usbd.c | 5 +- src/portable/microchip/samg/dcd_samg.c | 158 ++++++++++++++++++++++----------- src/tusb.c | 6 +- 5 files changed, 125 insertions(+), 55 deletions(-) (limited to 'src/portable') diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c index bf2b5bb8c..190252265 100644 --- a/src/class/msc/msc_device.c +++ b/src/class/msc/msc_device.c @@ -378,6 +378,9 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t TU_ASSERT( event == XFER_RESULT_SUCCESS && xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE ); + TU_LOG2(" Command Block Wrapper\n"); + TU_LOG2_MEM(p_cbw, xferred_bytes, 2); + p_csw->signature = MSC_CSW_SIGNATURE; p_csw->tag = p_cbw->tag; p_csw->data_residue = 0; @@ -448,6 +451,9 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t break; case MSC_STAGE_DATA: + //TU_LOG2(" SCSI Data\n"); + //TU_LOG2_MEM(_mscd_buf, xferred_bytes, 2); + // OUT transfer, invoke callback if needed if ( !tu_bit_test(p_cbw->dir, 7) ) { @@ -538,6 +544,9 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t // Wait for the command status wrapper complete event if( (ep_addr == p_msc->ep_in) && (xferred_bytes == sizeof(msc_csw_t)) ) { + TU_LOG2(" Command Status Wrapper\n"); + TU_LOG2_MEM(p_csw, xferred_bytes, 2); + // Move to default CMD stage p_msc->stage = MSC_STAGE_CMD; diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h index a2c40e669..f76abca88 100644 --- a/src/common/tusb_common.h +++ b/src/common/tusb_common.h @@ -213,7 +213,7 @@ static inline bool tu_bit_test (uint32_t value, uint8_t pos) { return (value // 2 : print out log #if CFG_TUSB_DEBUG -void tu_print_mem(void const *buf, uint8_t size, uint16_t count); +void tu_print_mem(void const *buf, uint16_t count, uint8_t indent); #ifndef tu_printf #define tu_printf printf diff --git a/src/device/usbd.c b/src/device/usbd.c index 497281c2d..5832f2353 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -376,8 +376,7 @@ void tud_task (void) break; case DCD_EVENT_SETUP_RECEIVED: - TU_LOG2(" "); - TU_LOG2_MEM(&event.setup_received, 1, 8); + 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 @@ -949,6 +948,8 @@ bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t 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; } diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c index f05763e38..96cd775c4 100644 --- a/src/portable/microchip/samg/dcd_samg.c +++ b/src/portable/microchip/samg/dcd_samg.c @@ -51,6 +51,11 @@ typedef struct // Endpoint 0-5, each can only be either OUT or In xfer_desc_t _dcd_xfer[EP_COUNT]; +void xfer_epsize_set(xfer_desc_t* xfer, uint16_t epsize) +{ + xfer->epsize = epsize; +} + void xfer_begin(xfer_desc_t* xfer, uint8_t * buffer, uint16_t total_bytes) { xfer->buffer = buffer; @@ -73,14 +78,23 @@ void xfer_packet_done(xfer_desc_t* xfer) } //------------- Transaction helpers -------------// -static uint16_t xact_in(uint8_t epnum, xfer_desc_t* xfer) -{ - uint16_t const xact_len = xfer_packet_len(xfer); - // Write data to fifo - for(uint16_t i=0; iUDP_FDR[epnum] = (uint32_t) xfer->buffer[i]; +// Write data to EP FIFO, return number of written bytes +static void xact_ep_write(uint8_t epnum, uint8_t* buffer, uint16_t xact_len) +{ + for(uint16_t i=0; iUDP_FDR[epnum] = (uint32_t) buffer[i]; + } +} - return xact_len; +// Read data from EP FIFO +static void xact_ep_read(uint8_t epnum, uint8_t* buffer, uint16_t xact_len) +{ + for(uint16_t i=0; iUDP_FDR[epnum]; + } } /*------------------------------------------------------------------*/ @@ -92,7 +106,7 @@ static void bus_reset(void) { tu_memclr(_dcd_xfer, sizeof(_dcd_xfer)); - _dcd_xfer[0].epsize = CFG_TUD_ENDPOINT0_SIZE; + xfer_epsize_set(&_dcd_xfer[0], CFG_TUD_ENDPOINT0_SIZE); // Enable EP0 control UDP->UDP_CSR[0] = UDP_CSR_EPEDS_Msk; @@ -205,11 +219,13 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) // Must not already enabled TU_ASSERT((UDP->UDP_CSR[epnum] & UDP_CSR_EPEDS_Msk) == 0); + xfer_epsize_set(&_dcd_xfer[epnum], ep_desc->wMaxPacketSize.size); + // Configure type and eanble EP UDP->UDP_CSR[epnum] = UDP_CSR_EPEDS_Msk | UDP_CSR_EPTYPE(ep_desc->bmAttributes.xfer + 4*dir); - // Enable EP Interrupt - UDP->UDP_IER |= (1 << epnum); + // Enable EP Interrupt for IN + if (dir == TUSB_DIR_IN) UDP->UDP_IER |= (1 << epnum); return true; } @@ -225,27 +241,47 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t xfer_desc_t* xfer = &_dcd_xfer[epnum]; xfer_begin(xfer, buffer, total_bytes); - // Configure DIR bit for control endpoint - if ( epnum == 0 ) - { - if (dir == TUSB_DIR_OUT) - { - // Clear DIR bit - UDP->UDP_CSR[0] &= ~UDP_CSR_DIR_Msk; - }else - { - // Set DIR bit - UDP->UDP_CSR[0] |= UDP_CSR_DIR_Msk; - } - } - if (dir == TUSB_DIR_IN) { - xact_in(epnum, xfer); + // Set DIR bit for EP0 + if ( epnum == 0 ) UDP->UDP_CSR[epnum] |= UDP_CSR_DIR_Msk; + + xact_ep_write(epnum, xfer->buffer, xfer_packet_len(xfer)); // TX ready for transfer UDP->UDP_CSR[epnum] |= UDP_CSR_TXPKTRDY_Msk; } + else + { + // Clear DIR bit for EP0 + if ( epnum == 0 ) UDP->UDP_CSR[epnum] &= ~UDP_CSR_DIR_Msk; + + // OUT Data may already received and acked by hardware + // Read it as 1st packet then continue with transfer if needed +// uint16_t const xact_len = (uint16_t) ((UDP->UDP_CSR[epnum] & UDP_CSR_RXBYTECNT_Msk) >> UDP_CSR_RXBYTECNT_Pos); +// +// if ( xact_len ) +// { +// // Read from EP fifo +// xact_ep_read(epnum, xfer->buffer, xact_len); +// xfer_packet_done(xfer); +// +// // Clear DATA Bank0 bit +// UDP->UDP_CSR[epnum] &= ~UDP_CSR_RX_DATA_BK0_Msk; +// +// if ( 0 == xfer_packet_len(xfer) ) +// { +// // Disable OUT EP interrupt when transfer is complete +// UDP->UDP_IER &= ~(1 << epnum); +// +// dcd_event_xfer_complete(rhport, epnum, xact_len, XFER_RESULT_SUCCESS, false); +// return true; // complete +// } +// } + + // Enable interrupt when starting OUT transfer + UDP->UDP_IER |= (1 << epnum); + } return true; } @@ -325,47 +361,67 @@ void dcd_isr(uint8_t rhport) // Clear Setup bit UDP->UDP_CSR[0] &= ~UDP_CSR_RXSETUP_Msk; + + return; } } for(uint8_t epnum = 0; epnum < EP_COUNT; epnum++) { - xfer_desc_t* xfer = &_dcd_xfer[epnum]; - - // Endpoint IN - if (UDP->UDP_CSR[epnum] & UDP_CSR_TXCOMP_Msk) + if ( intr_status & TU_BIT(epnum) ) { - xfer_packet_done(xfer); + xfer_desc_t* xfer = &_dcd_xfer[epnum]; - if ( xact_in(epnum, xfer) ) + // Endpoint IN + if (UDP->UDP_CSR[epnum] & UDP_CSR_TXCOMP_Msk) { - // TX ready for transfer - UDP->UDP_CSR[epnum] |= UDP_CSR_TXPKTRDY_Msk; - }else - { - // xfer is complete - dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true); + xfer_packet_done(xfer); + + uint16_t const xact_len = xfer_packet_len(xfer); + + if (xact_len) + { + // write to EP fifo + xact_ep_write(epnum, xfer->buffer, xact_len); + + // TX ready for transfer + UDP->UDP_CSR[epnum] |= UDP_CSR_TXPKTRDY_Msk; + }else + { + // xfer is complete + dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true); + } + + // Clear TX Complete bit + UDP->UDP_CSR[epnum] &= ~UDP_CSR_TXCOMP_Msk; } - // Clear TX Complete bit - UDP->UDP_CSR[epnum] &= ~UDP_CSR_TXCOMP_Msk; - } + // Endpoint OUT + if (UDP->UDP_CSR[epnum] & UDP_CSR_RX_DATA_BK0_Msk) + { + uint16_t const xact_len = (uint16_t) ((UDP->UDP_CSR[epnum] & UDP_CSR_RXBYTECNT_Msk) >> UDP_CSR_RXBYTECNT_Pos); - // Endpoint OUT - if (UDP->UDP_CSR[epnum] & UDP_CSR_RX_DATA_BK0_Msk) - { - uint16_t const xact_len = (uint16_t) ((UDP->UDP_CSR[epnum] & UDP_CSR_RXBYTECNT_Msk) >> UDP_CSR_RXBYTECNT_Pos); + // Read from EP fifo + xact_ep_read(epnum, xfer->buffer, xact_len); + xfer_packet_done(xfer); - dcd_event_xfer_complete(rhport, epnum, xact_len, XFER_RESULT_SUCCESS, true); + if ( 0 == xfer_packet_len(xfer) ) + { + // Disable OUT EP interrupt when transfer is complete + UDP->UDP_IER &= ~(1 << epnum); - // Clear DATA Bank0 bit - UDP->UDP_CSR[epnum] &= ~UDP_CSR_RX_DATA_BK0_Msk; - } + dcd_event_xfer_complete(rhport, epnum, xact_len, XFER_RESULT_SUCCESS, true); + } - // Stall sent to host - if (UDP->UDP_CSR[epnum] & UDP_CSR_STALLSENT_Msk) - { - UDP->UDP_CSR[epnum] &= ~UDP_CSR_STALLSENT_Msk; + // Clear DATA Bank0 bit + UDP->UDP_CSR[epnum] &= ~UDP_CSR_RX_DATA_BK0_Msk; + } + + // Stall sent to host + if (UDP->UDP_CSR[epnum] & UDP_CSR_STALLSENT_Msk) + { + UDP->UDP_CSR[epnum] &= ~UDP_CSR_STALLSENT_Msk; + } } } } diff --git a/src/tusb.c b/src/tusb.c index 7a1e73ec7..f68387450 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -81,8 +81,10 @@ static void dump_str_line(uint8_t const* buf, uint16_t count) // size : item size in bytes // count : number of item // print offet or not (handfy for dumping large memory) -void tu_print_mem(void const *buf, uint8_t size, uint16_t count) +void tu_print_mem(void const *buf, uint16_t count, uint8_t indent) { + uint8_t const size = 1; // fixed 1 byte for now + if ( !buf || !count ) { tu_printf("NULL\r\n"); @@ -110,6 +112,8 @@ void tu_print_mem(void const *buf, uint8_t size, uint16_t count) tu_printf("\r\n"); } + for(uint8_t s=0; s < indent; s++) tu_printf(" "); + // print offset or absolute address tu_printf("%03lX: ", 16*i/item_per_line); } -- cgit v1.3.1 From 421221baa5b8509b705f76def43fd684c5fe06df Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 21 Dec 2019 23:51:57 +0700 Subject: making more progress, but failed with WRITE10 incorrect queue 448 bytes instead of 512 --- src/class/msc/msc_device.c | 6 +++--- src/portable/microchip/samg/dcd_samg.c | 31 ++++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 12 deletions(-) (limited to 'src/portable') diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c index 190252265..84fe5a194 100644 --- a/src/class/msc/msc_device.c +++ b/src/class/msc/msc_device.c @@ -379,7 +379,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE ); TU_LOG2(" Command Block Wrapper\n"); - TU_LOG2_MEM(p_cbw, xferred_bytes, 2); + // TU_LOG2_MEM(p_cbw, xferred_bytes, 2); p_csw->signature = MSC_CSW_SIGNATURE; p_csw->tag = p_cbw->tag; @@ -451,7 +451,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t break; case MSC_STAGE_DATA: - //TU_LOG2(" SCSI Data\n"); + TU_LOG2(" SCSI Data\n"); //TU_LOG2_MEM(_mscd_buf, xferred_bytes, 2); // OUT transfer, invoke callback if needed @@ -545,7 +545,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t if( (ep_addr == p_msc->ep_in) && (xferred_bytes == sizeof(msc_csw_t)) ) { TU_LOG2(" Command Status Wrapper\n"); - TU_LOG2_MEM(p_csw, xferred_bytes, 2); + // TU_LOG2_MEM(p_csw, xferred_bytes, 2); // Move to default CMD stage p_msc->stage = MSC_STAGE_CMD; diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c index 96cd775c4..fd50237dc 100644 --- a/src/portable/microchip/samg/dcd_samg.c +++ b/src/portable/microchip/samg/dcd_samg.c @@ -63,6 +63,13 @@ void xfer_begin(xfer_desc_t* xfer, uint8_t * buffer, uint16_t total_bytes) xfer->actual_len = 0; } +void xfer_end(xfer_desc_t* xfer) +{ + xfer->buffer = NULL; + xfer->total_len = 0; + xfer->actual_len = 0; +} + uint16_t xfer_packet_len(xfer_desc_t* xfer) { // also cover zero-length packet @@ -258,10 +265,12 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t // OUT Data may already received and acked by hardware // Read it as 1st packet then continue with transfer if needed -// uint16_t const xact_len = (uint16_t) ((UDP->UDP_CSR[epnum] & UDP_CSR_RXBYTECNT_Msk) >> UDP_CSR_RXBYTECNT_Pos); -// -// if ( xact_len ) -// { + if ( UDP->UDP_CSR[epnum] & (UDP_CSR_RX_DATA_BK0_Msk | UDP_CSR_RX_DATA_BK1_Msk) ) + { + uint16_t const xact_len = (uint16_t) ((UDP->UDP_CSR[epnum] & UDP_CSR_RXBYTECNT_Msk) >> UDP_CSR_RXBYTECNT_Pos); + + TU_LOG2("xact_len = %d\r", xact_len); + // // Read from EP fifo // xact_ep_read(epnum, xfer->buffer, xact_len); // xfer_packet_done(xfer); @@ -277,10 +286,10 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t // dcd_event_xfer_complete(rhport, epnum, xact_len, XFER_RESULT_SUCCESS, false); // return true; // complete // } -// } + } // Enable interrupt when starting OUT transfer - UDP->UDP_IER |= (1 << epnum); + if (epnum != 0) UDP->UDP_IER |= (1 << epnum); } return true; @@ -397,10 +406,13 @@ void dcd_isr(uint8_t rhport) } // Endpoint OUT - if (UDP->UDP_CSR[epnum] & UDP_CSR_RX_DATA_BK0_Msk) + // When both Bank0 and Bank1 are both set, there is not way to know which one comes first + if (UDP->UDP_CSR[epnum] & (UDP_CSR_RX_DATA_BK0_Msk | UDP_CSR_RX_DATA_BK1_Msk)) { uint16_t const xact_len = (uint16_t) ((UDP->UDP_CSR[epnum] & UDP_CSR_RXBYTECNT_Msk) >> UDP_CSR_RXBYTECNT_Pos); + //if (epnum != 0) TU_LOG2("xact_len = %d\r", xact_len); + // Read from EP fifo xact_ep_read(epnum, xfer->buffer, xact_len); xfer_packet_done(xfer); @@ -408,13 +420,14 @@ void dcd_isr(uint8_t rhport) if ( 0 == xfer_packet_len(xfer) ) { // Disable OUT EP interrupt when transfer is complete - UDP->UDP_IER &= ~(1 << epnum); + if (epnum != 0) UDP->UDP_IDR |= (1 << epnum); dcd_event_xfer_complete(rhport, epnum, xact_len, XFER_RESULT_SUCCESS, true); +// xfer_end(xfer); } // Clear DATA Bank0 bit - UDP->UDP_CSR[epnum] &= ~UDP_CSR_RX_DATA_BK0_Msk; + UDP->UDP_CSR[epnum] &= ~(UDP_CSR_RX_DATA_BK0_Msk | UDP_CSR_RX_DATA_BK1_Msk); } // Stall sent to host -- cgit v1.3.1 From bbada1d3e6dba5b339c7cd4129e5df7b3203c11b Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 23 Dec 2019 18:54:06 +0700 Subject: adding lookup table for debugging add msc scsi command list --- src/class/msc/msc_device.c | 32 ++++++++++++++++++++++++++++++-- src/common/tusb_common.h | 23 +++++++++++++++++++++++ src/device/usbd.c | 2 +- src/portable/microchip/samg/dcd_samg.c | 5 +++-- 4 files changed, 57 insertions(+), 5 deletions(-) (limited to 'src/portable') diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c index 84fe5a194..93167bc6d 100644 --- a/src/class/msc/msc_device.c +++ b/src/class/msc/msc_device.c @@ -97,6 +97,34 @@ static inline uint16_t rdwr10_get_blockcount(uint8_t const command[]) return tu_ntohs(block_count); } +//--------------------------------------------------------------------+ +// Debug +//--------------------------------------------------------------------+ +#if CFG_TUSB_DEBUG >= 2 + +static lookup_entry_t const _msc_scsi_cmd_lookup[] = +{ + { .key = SCSI_CMD_TEST_UNIT_READY , .data = "Test Unit Ready" }, + { .key = SCSI_CMD_INQUIRY , .data = "Inquiry" }, + { .key = SCSI_CMD_MODE_SELECT_6 , .data = "Mode_Select 6" }, + { .key = SCSI_CMD_MODE_SENSE_6 , .data = "Mode_Sense 6" }, + { .key = SCSI_CMD_START_STOP_UNIT , .data = "Start Stop Unit" }, + { .key = SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL , .data = "Prevent Allow Medium Removal" }, + { .key = SCSI_CMD_READ_CAPACITY_10 , .data = "Read Capacity10" }, + { .key = SCSI_CMD_REQUEST_SENSE , .data = "Request Sense" }, + { .key = SCSI_CMD_READ_FORMAT_CAPACITY , .data = "Read Format Capacity" }, + { .key = SCSI_CMD_READ_10 , .data = "Read10" }, + { .key = SCSI_CMD_WRITE_10 , .data = "Write10" } +}; + +static lookup_table_t const _msc_scsi_cmd_table = +{ + .count = TU_ARRAY_SIZE(_msc_scsi_cmd_lookup), + .items = _msc_scsi_cmd_lookup +}; + +#endif + //--------------------------------------------------------------------+ // APPLICATION API //--------------------------------------------------------------------+ @@ -378,7 +406,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t TU_ASSERT( event == XFER_RESULT_SUCCESS && xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE ); - TU_LOG2(" Command Block Wrapper\n"); + TU_LOG2(" SCSI Command: %s\n", lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0])); // TU_LOG2_MEM(p_cbw, xferred_bytes, 2); p_csw->signature = MSC_CSW_SIGNATURE; @@ -544,7 +572,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t // Wait for the command status wrapper complete event if( (ep_addr == p_msc->ep_in) && (xferred_bytes == sizeof(msc_csw_t)) ) { - TU_LOG2(" Command Status Wrapper\n"); + TU_LOG2(" SCSI Status: %u\n", p_csw->status); // TU_LOG2_MEM(p_csw, xferred_bytes, 2); // Move to default CMD stage diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h index f76abca88..5700c1282 100644 --- a/src/common/tusb_common.h +++ b/src/common/tusb_common.h @@ -231,6 +231,29 @@ void tu_print_mem(void const *buf, uint16_t count, uint8_t indent); #define TU_LOG2_LOCATION() TU_LOG1_LOCATION() #endif + +typedef struct +{ + uint32_t key; + char const * data; +}lookup_entry_t; + +typedef struct +{ + uint16_t count; + lookup_entry_t const* items; +} lookup_table_t; + +static inline char const* lookup_find(lookup_table_t const* p_table, uint32_t key) +{ + for(uint16_t i=0; icount; i++) + { + if (p_table->items[i].key == key) return p_table->items[i].data; + } + + return NULL; +} + #endif // CFG_TUSB_DEBUG #ifndef TU_LOG1 diff --git a/src/device/usbd.c b/src/device/usbd.c index 5832f2353..949e14096 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -211,7 +211,7 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, //--------------------------------------------------------------------+ // Debugging //--------------------------------------------------------------------+ -#if CFG_TUSB_DEBUG > 1 +#if CFG_TUSB_DEBUG >= 2 static char const* const _usbd_event_str[DCD_EVENT_COUNT] = { "INVALID" , diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c index fd50237dc..a93b26caa 100644 --- a/src/portable/microchip/samg/dcd_samg.c +++ b/src/portable/microchip/samg/dcd_samg.c @@ -267,9 +267,9 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t // Read it as 1st packet then continue with transfer if needed if ( UDP->UDP_CSR[epnum] & (UDP_CSR_RX_DATA_BK0_Msk | UDP_CSR_RX_DATA_BK1_Msk) ) { - uint16_t const xact_len = (uint16_t) ((UDP->UDP_CSR[epnum] & UDP_CSR_RXBYTECNT_Msk) >> UDP_CSR_RXBYTECNT_Pos); +// uint16_t const xact_len = (uint16_t) ((UDP->UDP_CSR[epnum] & UDP_CSR_RXBYTECNT_Msk) >> UDP_CSR_RXBYTECNT_Pos); - TU_LOG2("xact_len = %d\r", xact_len); +// TU_LOG2("xact_len = %d\r", xact_len); // // Read from EP fifo // xact_ep_read(epnum, xfer->buffer, xact_len); @@ -406,6 +406,7 @@ void dcd_isr(uint8_t rhport) } // Endpoint OUT + // Ping-Pong is a must for Bulk/Iso // When both Bank0 and Bank1 are both set, there is not way to know which one comes first if (UDP->UDP_CSR[epnum] & (UDP_CSR_RX_DATA_BK0_Msk | UDP_CSR_RX_DATA_BK1_Msk)) { -- cgit v1.3.1 From 5690e7b304d9179dd1b8e35b3f61e016b7822c31 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 25 Dec 2019 23:47:47 +0700 Subject: fix samd51 setup packet handling issue occur due to race condition https://github.com/adafruit/Adafruit_TinyUSB_Arduino/issues/37 --- README.md | 2 +- src/portable/microchip/samd21/dcd_samd21.c | 2 +- src/portable/microchip/samd51/dcd_samd51.c | 13 +++++-------- 3 files changed, 7 insertions(+), 10 deletions(-) (limited to 'src/portable') diff --git a/README.md b/README.md index 3f7e7ecdf..798c10a5d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![tinyUSB_240x100](https://user-images.githubusercontent.com/249515/62646655-f9393200-b978-11e9-9c53-484862f15503.png) -[![Build Status](https://travis-ci.org/hathach/tinyusb.svg?branch=master)](https://travis-ci.org/hathach/tinyusb) [![License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT)[![Coverity](https://img.shields.io/coverity/scan/458.svg)](https://scan.coverity.com/projects/tinyusb) +[![Build Status](https://travis-ci.org/hathach/tinyusb.svg?branch=master)](https://travis-ci.org/hathach/tinyusb) [![License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT) [![Coverity](https://img.shields.io/coverity/scan/458.svg)](https://scan.coverity.com/projects/tinyusb) TinyUSB is an open-source cross-platform USB Host/Device stack for embedded system, designed to be memory-safe with no dynamic allocation and thread-safe with all interrupt events are deferred then handled in the non-ISR task function. diff --git a/src/portable/microchip/samd21/dcd_samd21.c b/src/portable/microchip/samd21/dcd_samd21.c index 5e44dc387..c96c87f88 100644 --- a/src/portable/microchip/samd21/dcd_samd21.c +++ b/src/portable/microchip/samd21/dcd_samd21.c @@ -253,7 +253,7 @@ static bool maybe_handle_setup_packet(void) { USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_RXSTP; // This copies the data elsewhere so we can reuse the buffer. - dcd_event_setup_received(0, (uint8_t*) sram_registers[0][0].ADDR.reg, true); + dcd_event_setup_received(0, _setup_packet, true); return true; } return false; diff --git a/src/portable/microchip/samd51/dcd_samd51.c b/src/portable/microchip/samd51/dcd_samd51.c index c298acc53..598ab7bbb 100644 --- a/src/portable/microchip/samd51/dcd_samd51.c +++ b/src/portable/microchip/samd51/dcd_samd51.c @@ -34,7 +34,7 @@ /*------------------------------------------------------------------*/ /* MACRO TYPEDEF CONSTANT ENUM *------------------------------------------------------------------*/ -static UsbDeviceDescBank sram_registers[8][2]; +static TU_ATTR_ALIGNED(4) UsbDeviceDescBank sram_registers[8][2]; static TU_ATTR_ALIGNED(4) uint8_t _setup_packet[8]; // Setup the control endpoint 0. @@ -69,7 +69,7 @@ void dcd_init (uint8_t rhport) USB->DEVICE.PADCAL.bit.TRANSP = (*((uint32_t*) USB_FUSES_TRANSP_ADDR) & USB_FUSES_TRANSP_Msk) >> USB_FUSES_TRANSP_Pos; USB->DEVICE.PADCAL.bit.TRANSN = (*((uint32_t*) USB_FUSES_TRANSN_ADDR) & USB_FUSES_TRANSN_Msk) >> USB_FUSES_TRANSN_Pos; - USB->DEVICE.PADCAL.bit.TRIM = (*((uint32_t*) USB_FUSES_TRIM_ADDR) & USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos; + USB->DEVICE.PADCAL.bit.TRIM = (*((uint32_t*) USB_FUSES_TRIM_ADDR) & USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos; USB->DEVICE.QOSCTRL.bit.CQOS = 3; USB->DEVICE.QOSCTRL.bit.DQOS = 3; @@ -81,7 +81,7 @@ void dcd_init (uint8_t rhport) while (USB->DEVICE.SYNCBUSY.bit.ENABLE == 1) {} USB->DEVICE.INTFLAG.reg |= USB->DEVICE.INTFLAG.reg; // clear pending - USB->DEVICE.INTENSET.reg = USB_DEVICE_INTENSET_SOF | USB_DEVICE_INTENSET_EORST; + USB->DEVICE.INTENSET.reg = /* USB_DEVICE_INTENSET_SOF | */ USB_DEVICE_INTENSET_EORST; } void dcd_int_enable(uint8_t rhport) @@ -257,7 +257,8 @@ static bool maybe_handle_setup_packet(void) { USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_RXSTP; // This copies the data elsewhere so we can reuse the buffer. - dcd_event_setup_received(0, (uint8_t*) sram_registers[0][0].ADDR.reg, true); + dcd_event_setup_received(0, _setup_packet, true); + return true; } return false; @@ -336,11 +337,7 @@ void transfer_complete(uint8_t direction) { continue; } - if (direction == TUSB_DIR_OUT && maybe_handle_setup_packet()) { - continue; - } UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum]; - UsbDeviceDescBank* bank = &sram_registers[epnum][direction]; uint16_t total_transfer_size = bank->PCKSIZE.bit.BYTE_COUNT; -- cgit v1.3.1 From 49b316143ad4e1bd6130b3032b8418be7e0bc2c3 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 26 Dec 2019 17:11:24 +0700 Subject: improve samd, fix missing setup packet occasionally --- src/portable/microchip/samd51/dcd_samd51.c | 32 ++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 11 deletions(-) (limited to 'src/portable') diff --git a/src/portable/microchip/samd51/dcd_samd51.c b/src/portable/microchip/samd51/dcd_samd51.c index 598ab7bbb..4bb214cb6 100644 --- a/src/portable/microchip/samd51/dcd_samd51.c +++ b/src/portable/microchip/samd51/dcd_samd51.c @@ -37,6 +37,15 @@ static TU_ATTR_ALIGNED(4) UsbDeviceDescBank sram_registers[8][2]; static TU_ATTR_ALIGNED(4) uint8_t _setup_packet[8]; + +// ready for receiving SETUP packet +static inline void prepare_setup(void) +{ + // Only make sure the EP0 OUT buffer is ready + // SETUP token doesn't check any other parameters + sram_registers[0][0].ADDR.reg = (uint32_t) _setup_packet; +} + // Setup the control endpoint 0. static void bus_reset(void) { @@ -51,10 +60,9 @@ static void bus_reset(void) ep->EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT0 | USB_DEVICE_EPINTENSET_TRCPT1 | USB_DEVICE_EPINTENSET_RXSTP; // Prepare for setup packet - dcd_edpt_xfer(0, 0, _setup_packet, sizeof(_setup_packet)); + prepare_setup(); } - /*------------------------------------------------------------------*/ /* Controller API *------------------------------------------------------------------*/ @@ -148,7 +156,9 @@ void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * re } // Just finished status stage, prepare for next setup packet - dcd_edpt_xfer(rhport, 0x00, _setup_packet, sizeof(_setup_packet)); + // Note: we may already prepare setup when the last EP0 OUT complete. + // but it has no harm to do it again here + prepare_setup(); } bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) @@ -197,12 +207,6 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t UsbDeviceDescBank* bank = &sram_registers[epnum][dir]; UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum]; - // A setup token can occur immediately after an OUT STATUS packet so make sure we have a valid - // buffer for the control endpoint. - if (epnum == 0 && dir == 0 && buffer == NULL) { - buffer = _setup_packet; - } - bank->ADDR.reg = (uint32_t) buffer; if ( dir == TUSB_DIR_OUT ) { @@ -254,11 +258,11 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) static bool maybe_handle_setup_packet(void) { if (USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.bit.RXSTP) { - USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_RXSTP; - // This copies the data elsewhere so we can reuse the buffer. dcd_event_setup_received(0, _setup_packet, true); + USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_RXSTP; + return true; } return false; @@ -337,6 +341,12 @@ void transfer_complete(uint8_t direction) { continue; } + // A SETUP token can occur immediately after an OUT packet + // so make sure we have a valid buffer for the control endpoint. + if (epnum == 0 && direction == TUSB_DIR_OUT ) { + prepare_setup(); + } + UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum]; UsbDeviceDescBank* bank = &sram_registers[epnum][direction]; uint16_t total_transfer_size = bank->PCKSIZE.bit.BYTE_COUNT; -- cgit v1.3.1 From b5684c5c102dbe54988357d1ea0fd457f6cbfc5c Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 26 Dec 2019 21:46:59 +0700 Subject: merge dcd_samd51 & dcd_samd21 into dcd_samd --- hw/bsp/circuitplayground_express/board.mk | 2 +- hw/bsp/feather_m0_express/board.mk | 2 +- hw/bsp/feather_m4_express/board.mk | 2 +- hw/bsp/metro_m0_express/board.mk | 2 +- hw/bsp/metro_m4_express/board.mk | 2 +- src/portable/microchip/samd/dcd_samd.c | 429 +++++++++++++++++++++++++++++ src/portable/microchip/samd21/dcd_samd21.c | 353 ------------------------ src/portable/microchip/samd51/dcd_samd51.c | 384 -------------------------- 8 files changed, 434 insertions(+), 742 deletions(-) create mode 100644 src/portable/microchip/samd/dcd_samd.c delete mode 100644 src/portable/microchip/samd21/dcd_samd21.c delete mode 100644 src/portable/microchip/samd51/dcd_samd51.c (limited to 'src/portable') diff --git a/hw/bsp/circuitplayground_express/board.mk b/hw/bsp/circuitplayground_express/board.mk index 115a81187..7ea90e8e1 100644 --- a/hw/bsp/circuitplayground_express/board.mk +++ b/hw/bsp/circuitplayground_express/board.mk @@ -31,7 +31,7 @@ INC += \ # For TinyUSB port source VENDOR = microchip -CHIP_FAMILY = samd21 +CHIP_FAMILY = samd # For freeRTOS port source FREERTOS_PORT = ARM_CM0 diff --git a/hw/bsp/feather_m0_express/board.mk b/hw/bsp/feather_m0_express/board.mk index fe1913331..d4530a478 100644 --- a/hw/bsp/feather_m0_express/board.mk +++ b/hw/bsp/feather_m0_express/board.mk @@ -32,7 +32,7 @@ INC += \ # For TinyUSB port source VENDOR = microchip -CHIP_FAMILY = samd21 +CHIP_FAMILY = samd # For freeRTOS port source FREERTOS_PORT = ARM_CM0 diff --git a/hw/bsp/feather_m4_express/board.mk b/hw/bsp/feather_m4_express/board.mk index 010e4b868..f42aa7703 100644 --- a/hw/bsp/feather_m4_express/board.mk +++ b/hw/bsp/feather_m4_express/board.mk @@ -35,7 +35,7 @@ INC += \ # For TinyUSB port source VENDOR = microchip -CHIP_FAMILY = samd51 +CHIP_FAMILY = samd # For freeRTOS port source FREERTOS_PORT = ARM_CM4F diff --git a/hw/bsp/metro_m0_express/board.mk b/hw/bsp/metro_m0_express/board.mk index 2039219a4..668d9a5b6 100644 --- a/hw/bsp/metro_m0_express/board.mk +++ b/hw/bsp/metro_m0_express/board.mk @@ -32,7 +32,7 @@ INC += \ # For TinyUSB port source VENDOR = microchip -CHIP_FAMILY = samd21 +CHIP_FAMILY = samd # For freeRTOS port source FREERTOS_PORT = ARM_CM0 diff --git a/hw/bsp/metro_m4_express/board.mk b/hw/bsp/metro_m4_express/board.mk index 010e4b868..f42aa7703 100644 --- a/hw/bsp/metro_m4_express/board.mk +++ b/hw/bsp/metro_m4_express/board.mk @@ -35,7 +35,7 @@ INC += \ # For TinyUSB port source VENDOR = microchip -CHIP_FAMILY = samd51 +CHIP_FAMILY = samd # For freeRTOS port source FREERTOS_PORT = ARM_CM4F diff --git a/src/portable/microchip/samd/dcd_samd.c b/src/portable/microchip/samd/dcd_samd.c new file mode 100644 index 000000000..eccc9c097 --- /dev/null +++ b/src/portable/microchip/samd/dcd_samd.c @@ -0,0 +1,429 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries + * + * 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_TUSB_MCU == OPT_MCU_SAMD51 || CFG_TUSB_MCU == OPT_MCU_SAMD21) + +#include "sam.h" +#include "device/dcd.h" + +/*------------------------------------------------------------------*/ +/* MACRO TYPEDEF CONSTANT ENUM + *------------------------------------------------------------------*/ +static TU_ATTR_ALIGNED(4) UsbDeviceDescBank sram_registers[8][2]; +static TU_ATTR_ALIGNED(4) uint8_t _setup_packet[8]; + + +// ready for receiving SETUP packet +static inline void prepare_setup(void) +{ + // Only make sure the EP0 OUT buffer is ready + // SETUP token doesn't check any other parameters + sram_registers[0][0].ADDR.reg = (uint32_t) _setup_packet; +} + +// Setup the control endpoint 0. +static void bus_reset(void) +{ + // Max size of packets is 64 bytes. + UsbDeviceDescBank* bank_out = &sram_registers[0][TUSB_DIR_OUT]; + bank_out->PCKSIZE.bit.SIZE = 0x3; + UsbDeviceDescBank* bank_in = &sram_registers[0][TUSB_DIR_IN]; + bank_in->PCKSIZE.bit.SIZE = 0x3; + + UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[0]; + ep->EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE0(0x1) | USB_DEVICE_EPCFG_EPTYPE1(0x1); + ep->EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT0 | USB_DEVICE_EPINTENSET_TRCPT1 | USB_DEVICE_EPINTENSET_RXSTP; + + // Prepare for setup packet + prepare_setup(); +} + + +/*------------------------------------------------------------------*/ +/* Controller API + *------------------------------------------------------------------*/ +void dcd_init (uint8_t rhport) +{ + (void) rhport; + + // Reset to get in a clean state. + USB->DEVICE.CTRLA.bit.SWRST = true; + while (USB->DEVICE.SYNCBUSY.bit.SWRST == 0) {} + while (USB->DEVICE.SYNCBUSY.bit.SWRST == 1) {} + + USB->DEVICE.PADCAL.bit.TRANSP = (*((uint32_t*) USB_FUSES_TRANSP_ADDR) & USB_FUSES_TRANSP_Msk) >> USB_FUSES_TRANSP_Pos; + USB->DEVICE.PADCAL.bit.TRANSN = (*((uint32_t*) USB_FUSES_TRANSN_ADDR) & USB_FUSES_TRANSN_Msk) >> USB_FUSES_TRANSN_Pos; + USB->DEVICE.PADCAL.bit.TRIM = (*((uint32_t*) USB_FUSES_TRIM_ADDR) & USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos; + + USB->DEVICE.QOSCTRL.bit.CQOS = 3; // High Quality + USB->DEVICE.QOSCTRL.bit.DQOS = 3; // High Quality + + // Configure registers + USB->DEVICE.DESCADD.reg = (uint32_t) &sram_registers; + USB->DEVICE.CTRLB.reg = USB_DEVICE_CTRLB_SPDCONF_FS; + USB->DEVICE.CTRLA.reg = USB_CTRLA_MODE_DEVICE | USB_CTRLA_ENABLE | USB_CTRLA_RUNSTDBY; + while (USB->DEVICE.SYNCBUSY.bit.ENABLE == 1) {} + + USB->DEVICE.INTFLAG.reg |= USB->DEVICE.INTFLAG.reg; // clear pending + USB->DEVICE.INTENSET.reg = /* USB_DEVICE_INTENSET_SOF | */ USB_DEVICE_INTENSET_EORST; +} + +void dcd_int_enable(uint8_t rhport) +{ + (void) rhport; + +#if CFG_TUSB_MCU == OPT_MCU_SAMD51 + NVIC_EnableIRQ(USB_0_IRQn); + NVIC_EnableIRQ(USB_1_IRQn); + NVIC_EnableIRQ(USB_2_IRQn); + NVIC_EnableIRQ(USB_3_IRQn); +#elif CFG_TUSB_MCU == OPT_MCU_SAMD21 + NVIC_EnableIRQ(USB_IRQn); +#endif +} + +void dcd_int_disable(uint8_t rhport) +{ + (void) rhport; + +#if CFG_TUSB_MCU == OPT_MCU_SAMD51 + NVIC_DisableIRQ(USB_3_IRQn); + NVIC_DisableIRQ(USB_2_IRQn); + NVIC_DisableIRQ(USB_1_IRQn); + NVIC_DisableIRQ(USB_0_IRQn); +#elif CFG_TUSB_MCU == OPT_MCU_SAMD21 + NVIC_DisableIRQ(USB_IRQn); +#endif +} + +void dcd_set_address (uint8_t rhport, uint8_t dev_addr) +{ + (void) dev_addr; + + // Response with zlp status + dcd_edpt_xfer(rhport, 0x80, NULL, 0); + + // DCD can only set address after status for this request is complete + // do it at dcd_edpt0_status_complete() + + // Enable SUSPEND interrupt since the bus signal D+/D- are stable now. + USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTENCLR_SUSPEND; // clear pending + USB->DEVICE.INTENSET.reg = USB_DEVICE_INTENSET_SUSPEND; +} + +void dcd_set_config (uint8_t rhport, uint8_t config_num) +{ + (void) rhport; + (void) config_num; + // Nothing to do +} + +void dcd_remote_wakeup(uint8_t rhport) +{ + (void) rhport; + + USB->DEVICE.CTRLB.bit.UPRSM = 1; +} + +/*------------------------------------------------------------------*/ +/* DCD Endpoint port + *------------------------------------------------------------------*/ + +// 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) +{ + (void) rhport; + + if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && + request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && + request->bRequest == TUSB_REQ_SET_ADDRESS ) + { + uint8_t const dev_addr = (uint8_t) request->wValue; + USB->DEVICE.DADD.reg = USB_DEVICE_DADD_DADD(dev_addr) | USB_DEVICE_DADD_ADDEN; + } + + // Just finished status stage, prepare for next setup packet + // Note: we may already prepare setup when the last EP0 OUT complete. + // but it has no harm to do it again here + prepare_setup(); +} + +bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) +{ + (void) rhport; + + uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress); + uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress); + + UsbDeviceDescBank* bank = &sram_registers[epnum][dir]; + uint32_t size_value = 0; + while (size_value < 7) { + if (1 << (size_value + 3) == desc_edpt->wMaxPacketSize.size) { + break; + } + size_value++; + } + + // unsupported endpoint size + if ( size_value == 7 && desc_edpt->wMaxPacketSize.size != 1023 ) return false; + + bank->PCKSIZE.bit.SIZE = size_value; + + UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum]; + + if ( dir == TUSB_DIR_OUT ) + { + ep->EPCFG.bit.EPTYPE0 = desc_edpt->bmAttributes.xfer + 1; + ep->EPINTENSET.bit.TRCPT0 = true; + }else + { + ep->EPCFG.bit.EPTYPE1 = desc_edpt->bmAttributes.xfer + 1; + ep->EPINTENSET.bit.TRCPT1 = true; + } + + return true; +} + +bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) +{ + (void) rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + UsbDeviceDescBank* bank = &sram_registers[epnum][dir]; + UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum]; + + bank->ADDR.reg = (uint32_t) buffer; + if ( dir == TUSB_DIR_OUT ) + { + bank->PCKSIZE.bit.MULTI_PACKET_SIZE = total_bytes; + bank->PCKSIZE.bit.BYTE_COUNT = 0; + ep->EPSTATUSCLR.reg |= USB_DEVICE_EPSTATUSCLR_BK0RDY; + ep->EPINTFLAG.reg |= USB_DEVICE_EPINTFLAG_TRFAIL0; + } else + { + bank->PCKSIZE.bit.MULTI_PACKET_SIZE = 0; + bank->PCKSIZE.bit.BYTE_COUNT = total_bytes; + ep->EPSTATUSSET.reg |= USB_DEVICE_EPSTATUSSET_BK1RDY; + ep->EPINTFLAG.reg |= USB_DEVICE_EPINTFLAG_TRFAIL1; + } + + return true; +} + +void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum]; + + if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { + ep->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ1; + } else { + ep->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ0; + } +} + +void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum]; + + if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { + ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ1 | USB_DEVICE_EPSTATUSCLR_DTGLIN; + } else { + ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ0 | USB_DEVICE_EPSTATUSCLR_DTGLOUT; + } +} + +//--------------------------------------------------------------------+ +// Interrupt Handler +//--------------------------------------------------------------------+ +void maybe_transfer_complete(void) { + uint32_t epints = USB->DEVICE.EPINTSMRY.reg; + + for (uint8_t epnum = 0; epnum < USB_EPT_NUM; epnum++) { + if ((epints & (1 << epnum)) == 0) { + continue; + } + + UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum]; + uint32_t epintflag = ep->EPINTFLAG.reg; + + // Handle IN completions + if ((epintflag & USB_DEVICE_EPINTFLAG_TRCPT1) != 0) { + UsbDeviceDescBank* bank = &sram_registers[epnum][TUSB_DIR_IN]; + uint16_t total_transfer_size = bank->PCKSIZE.bit.BYTE_COUNT; + + uint8_t ep_addr = epnum | TUSB_DIR_IN_MASK; + dcd_event_xfer_complete(0, ep_addr, total_transfer_size, XFER_RESULT_SUCCESS, true); + + ep->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT1; + } + + // Handle OUT completions + if ((epintflag & USB_DEVICE_EPINTFLAG_TRCPT0) != 0) { + + // A SETUP token can occur immediately after an OUT packet + // so make sure we have a valid buffer for the control endpoint. + if (epnum == 0) { + prepare_setup(); + } + + UsbDeviceDescBank* bank = &sram_registers[epnum][TUSB_DIR_OUT]; + uint16_t total_transfer_size = bank->PCKSIZE.bit.BYTE_COUNT; + + uint8_t ep_addr = epnum; + dcd_event_xfer_complete(0, ep_addr, total_transfer_size, XFER_RESULT_SUCCESS, true); + + ep->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT0; + } + } +} + + +void dcd_isr (uint8_t rhport) +{ + (void) rhport; + + uint32_t int_status = USB->DEVICE.INTFLAG.reg & USB->DEVICE.INTENSET.reg; + + /*------------- Interrupt Processing -------------*/ + if ( int_status & USB_DEVICE_INTFLAG_SOF ) + { + USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_SOF; + dcd_event_bus_signal(0, DCD_EVENT_SOF, true); + } + + // SAMD doesn't distinguish between Suspend and Disconnect state. + // Both condition will cause SUSPEND interrupt triggered. + // To prevent being triggered when D+/D- are not stable, SUSPEND interrupt is only + // enabled when we received SET_ADDRESS request and cleared on Bus Reset + if ( int_status & USB_DEVICE_INTFLAG_SUSPEND ) + { + USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_SUSPEND; + + // Enable wakeup interrupt + USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_WAKEUP; // clear pending + USB->DEVICE.INTENSET.reg = USB_DEVICE_INTFLAG_WAKEUP; + + dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true); + } + + // Wakeup interrupt is only enabled when we got suspended. + // Wakeup interrupt will disable itself + if ( int_status & USB_DEVICE_INTFLAG_WAKEUP ) + { + USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_WAKEUP; + + // disable wakeup interrupt itself + USB->DEVICE.INTENCLR.reg = USB_DEVICE_INTFLAG_WAKEUP; + dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); + } + + if ( int_status & USB_DEVICE_INTFLAG_EORST ) + { + USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_EORST; + + // Disable both suspend and wakeup interrupt + USB->DEVICE.INTENCLR.reg = USB_DEVICE_INTFLAG_WAKEUP | USB_DEVICE_INTFLAG_SUSPEND; + + bus_reset(); + dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true); + } + + // Handle SETUP packet + if (USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.bit.RXSTP) + { + // This copies the data elsewhere so we can reuse the buffer. + dcd_event_setup_received(0, _setup_packet, true); + + USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_RXSTP; + } + + // Handle complete transfer + maybe_transfer_complete(); +} + +#if CFG_TUSB_MCU == OPT_MCU_SAMD51 + +/* + *------------------------------------------------------------------*/ +/* USB_EORSM_DNRSM, USB_EORST_RST, USB_LPMSUSP_DDISC, USB_LPM_DCONN, +USB_MSOF, USB_RAMACER, USB_RXSTP_TXSTP_0, USB_RXSTP_TXSTP_1, +USB_RXSTP_TXSTP_2, USB_RXSTP_TXSTP_3, USB_RXSTP_TXSTP_4, +USB_RXSTP_TXSTP_5, USB_RXSTP_TXSTP_6, USB_RXSTP_TXSTP_7, +USB_STALL0_STALL_0, USB_STALL0_STALL_1, USB_STALL0_STALL_2, +USB_STALL0_STALL_3, USB_STALL0_STALL_4, USB_STALL0_STALL_5, +USB_STALL0_STALL_6, USB_STALL0_STALL_7, USB_STALL1_0, USB_STALL1_1, +USB_STALL1_2, USB_STALL1_3, USB_STALL1_4, USB_STALL1_5, USB_STALL1_6, +USB_STALL1_7, USB_SUSPEND, USB_TRFAIL0_TRFAIL_0, USB_TRFAIL0_TRFAIL_1, +USB_TRFAIL0_TRFAIL_2, USB_TRFAIL0_TRFAIL_3, USB_TRFAIL0_TRFAIL_4, +USB_TRFAIL0_TRFAIL_5, USB_TRFAIL0_TRFAIL_6, USB_TRFAIL0_TRFAIL_7, +USB_TRFAIL1_PERR_0, USB_TRFAIL1_PERR_1, USB_TRFAIL1_PERR_2, +USB_TRFAIL1_PERR_3, USB_TRFAIL1_PERR_4, USB_TRFAIL1_PERR_5, +USB_TRFAIL1_PERR_6, USB_TRFAIL1_PERR_7, USB_UPRSM, USB_WAKEUP */ +void USB_0_Handler(void) { + dcd_isr(0); +} + +/* USB_SOF_HSOF */ +void USB_1_Handler(void) { + dcd_isr(0); +} + +// Bank zero is for OUT and SETUP transactions. +/* USB_TRCPT0_0, USB_TRCPT0_1, USB_TRCPT0_2, +USB_TRCPT0_3, USB_TRCPT0_4, USB_TRCPT0_5, +USB_TRCPT0_6, USB_TRCPT0_7 */ +void USB_2_Handler(void) { + dcd_isr(0); +} + +// Bank one is used for IN transactions. +/* USB_TRCPT1_0, USB_TRCPT1_1, USB_TRCPT1_2, +USB_TRCPT1_3, USB_TRCPT1_4, USB_TRCPT1_5, +USB_TRCPT1_6, USB_TRCPT1_7 */ +void USB_3_Handler(void) { + dcd_isr(0); +} + +#elif CFG_TUSB_MCU == OPT_MCU_SAMD21 + +void USB_Handler(void) { + dcd_isr(0); +} + +#endif + +#endif diff --git a/src/portable/microchip/samd21/dcd_samd21.c b/src/portable/microchip/samd21/dcd_samd21.c deleted file mode 100644 index c96c87f88..000000000 --- a/src/portable/microchip/samd21/dcd_samd21.c +++ /dev/null @@ -1,353 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries - * - * 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_TUSB_MCU == OPT_MCU_SAMD21 - -#include "sam.h" -#include "device/dcd.h" - -/*------------------------------------------------------------------*/ -/* MACRO TYPEDEF CONSTANT ENUM - *------------------------------------------------------------------*/ -static TU_ATTR_ALIGNED(4) UsbDeviceDescBank sram_registers[8][2]; -static TU_ATTR_ALIGNED(4) uint8_t _setup_packet[8]; - -// Setup the control endpoint 0. -static void bus_reset(void) -{ - // Max size of packets is 64 bytes. - UsbDeviceDescBank* bank_out = &sram_registers[0][TUSB_DIR_OUT]; - bank_out->PCKSIZE.bit.SIZE = 0x3; - UsbDeviceDescBank* bank_in = &sram_registers[0][TUSB_DIR_IN]; - bank_in->PCKSIZE.bit.SIZE = 0x3; - - UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[0]; - ep->EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE0(0x1) | USB_DEVICE_EPCFG_EPTYPE1(0x1); - ep->EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT0 | USB_DEVICE_EPINTENSET_TRCPT1 | USB_DEVICE_EPINTENSET_RXSTP; - - // Prepare for setup packet - dcd_edpt_xfer(0, 0, _setup_packet, sizeof(_setup_packet)); -} - - -/*------------------------------------------------------------------*/ -/* Controller API - *------------------------------------------------------------------*/ -void dcd_init (uint8_t rhport) -{ - (void) rhport; - - // Reset to get in a clean state. - USB->DEVICE.CTRLA.bit.SWRST = true; - while (USB->DEVICE.SYNCBUSY.bit.SWRST == 0) {} - while (USB->DEVICE.SYNCBUSY.bit.SWRST == 1) {} - - USB->DEVICE.PADCAL.bit.TRANSP = (*((uint32_t*) USB_FUSES_TRANSP_ADDR) & USB_FUSES_TRANSP_Msk) >> USB_FUSES_TRANSP_Pos; - USB->DEVICE.PADCAL.bit.TRANSN = (*((uint32_t*) USB_FUSES_TRANSN_ADDR) & USB_FUSES_TRANSN_Msk) >> USB_FUSES_TRANSN_Pos; - USB->DEVICE.PADCAL.bit.TRIM = (*((uint32_t*) USB_FUSES_TRIM_ADDR) & USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos; - - USB->DEVICE.QOSCTRL.bit.CQOS = USB_QOSCTRL_CQOS_HIGH_Val; - USB->DEVICE.QOSCTRL.bit.DQOS = USB_QOSCTRL_DQOS_HIGH_Val; - - // Configure registers - USB->DEVICE.DESCADD.reg = (uint32_t) &sram_registers; - USB->DEVICE.CTRLB.reg = USB_DEVICE_CTRLB_SPDCONF_FS; - USB->DEVICE.CTRLA.reg = USB_CTRLA_MODE_DEVICE | USB_CTRLA_ENABLE | USB_CTRLA_RUNSTDBY; - while (USB->DEVICE.SYNCBUSY.bit.ENABLE == 1) {} - - USB->DEVICE.INTFLAG.reg |= USB->DEVICE.INTFLAG.reg; // clear pending - USB->DEVICE.INTENSET.reg = USB_DEVICE_INTENSET_SOF | USB_DEVICE_INTENSET_EORST; -} - -void dcd_int_enable(uint8_t rhport) -{ - (void) rhport; - NVIC_EnableIRQ(USB_IRQn); -} - -void dcd_int_disable(uint8_t rhport) -{ - (void) rhport; - NVIC_DisableIRQ(USB_IRQn); -} - -void dcd_set_address (uint8_t rhport, uint8_t dev_addr) -{ - (void) dev_addr; - - // Response with zlp status - dcd_edpt_xfer(rhport, 0x80, NULL, 0); - - // DCD can only set address after status for this request is complete - // do it at dcd_edpt0_status_complete() - - // Enable SUSPEND interrupt since the bus signal D+/D- are stable now. - USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTENCLR_SUSPEND; // clear pending - USB->DEVICE.INTENSET.reg = USB_DEVICE_INTENSET_SUSPEND; -} - -void dcd_set_config (uint8_t rhport, uint8_t config_num) -{ - (void) rhport; - (void) config_num; - // Nothing to do -} - -void dcd_remote_wakeup(uint8_t rhport) -{ - (void) rhport; - - USB->DEVICE.CTRLB.bit.UPRSM = 1; -} - -/*------------------------------------------------------------------*/ -/* DCD Endpoint port - *------------------------------------------------------------------*/ - -// 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) -{ - if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && - request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && - request->bRequest == TUSB_REQ_SET_ADDRESS ) - { - uint8_t const dev_addr = (uint8_t) request->wValue; - USB->DEVICE.DADD.reg = USB_DEVICE_DADD_DADD(dev_addr) | USB_DEVICE_DADD_ADDEN; - } - - // Just finished status stage, prepare for next setup packet - dcd_edpt_xfer(rhport, 0x00, _setup_packet, sizeof(_setup_packet)); -} - - -bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) -{ - (void) rhport; - - uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress); - uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress); - - UsbDeviceDescBank* bank = &sram_registers[epnum][dir]; - uint32_t size_value = 0; - while (size_value < 7) { - if (1 << (size_value + 3) == desc_edpt->wMaxPacketSize.size) { - break; - } - size_value++; - } - - // unsupported endpoint size - if ( size_value == 7 && desc_edpt->wMaxPacketSize.size != 1023 ) return false; - - bank->PCKSIZE.bit.SIZE = size_value; - - UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum]; - - if ( dir == TUSB_DIR_OUT ) - { - ep->EPCFG.bit.EPTYPE0 = desc_edpt->bmAttributes.xfer + 1; - ep->EPINTENSET.bit.TRCPT0 = true; - }else - { - ep->EPCFG.bit.EPTYPE1 = desc_edpt->bmAttributes.xfer + 1; - ep->EPINTENSET.bit.TRCPT1 = true; - } - - return true; -} - -bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) -{ - (void) rhport; - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - UsbDeviceDescBank* bank = &sram_registers[epnum][dir]; - UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum]; - - // A setup token can occur immediately after an OUT STATUS packet so make sure we have a valid - // buffer for the control endpoint. - if (epnum == 0 && dir == 0 && buffer == NULL) { - buffer = _setup_packet; - } - - bank->ADDR.reg = (uint32_t) buffer; - if ( dir == TUSB_DIR_OUT ) - { - bank->PCKSIZE.bit.MULTI_PACKET_SIZE = total_bytes; - bank->PCKSIZE.bit.BYTE_COUNT = 0; - ep->EPSTATUSCLR.reg |= USB_DEVICE_EPSTATUSCLR_BK0RDY; - ep->EPINTFLAG.reg |= USB_DEVICE_EPINTFLAG_TRFAIL0; - } else - { - bank->PCKSIZE.bit.MULTI_PACKET_SIZE = 0; - bank->PCKSIZE.bit.BYTE_COUNT = total_bytes; - // bank->PCKSIZE.bit.AUTO_ZLP = 1; - ep->EPSTATUSSET.reg |= USB_DEVICE_EPSTATUSSET_BK1RDY; - ep->EPINTFLAG.reg |= USB_DEVICE_EPINTFLAG_TRFAIL1; - } - - return true; -} - -void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr) -{ - (void) rhport; - - uint8_t const epnum = tu_edpt_number(ep_addr); - UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum]; - - if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { - ep->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ1; - } else { - ep->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ0; - } -} - -void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) -{ - (void) rhport; - - uint8_t const epnum = tu_edpt_number(ep_addr); - UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum]; - - if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { - ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ1 | USB_DEVICE_EPSTATUSCLR_DTGLIN; - } else { - ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ0 | USB_DEVICE_EPSTATUSCLR_DTGLOUT; - } -} - -/*------------------------------------------------------------------*/ - -static bool maybe_handle_setup_packet(void) { - if (USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.bit.RXSTP) - { - USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_RXSTP; - - // This copies the data elsewhere so we can reuse the buffer. - dcd_event_setup_received(0, _setup_packet, true); - return true; - } - return false; -} - -void maybe_transfer_complete(void) { - uint32_t epints = USB->DEVICE.EPINTSMRY.reg; - - for (uint8_t epnum = 0; epnum < USB_EPT_NUM; epnum++) { - if ((epints & (1 << epnum)) == 0) { - continue; - } - - if (maybe_handle_setup_packet()) { - continue; - } - - UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum]; - - uint32_t epintflag = ep->EPINTFLAG.reg; - - uint16_t total_transfer_size = 0; - - // Handle IN completions - if ((epintflag & USB_DEVICE_EPINTFLAG_TRCPT1) != 0) { - ep->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT1; - - UsbDeviceDescBank* bank = &sram_registers[epnum][TUSB_DIR_IN]; - total_transfer_size = bank->PCKSIZE.bit.BYTE_COUNT; - - uint8_t ep_addr = epnum | TUSB_DIR_IN_MASK; - dcd_event_xfer_complete(0, ep_addr, total_transfer_size, XFER_RESULT_SUCCESS, true); - } - - // Handle OUT completions - if ((epintflag & USB_DEVICE_EPINTFLAG_TRCPT0) != 0) { - ep->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT0; - - UsbDeviceDescBank* bank = &sram_registers[epnum][TUSB_DIR_OUT]; - total_transfer_size = bank->PCKSIZE.bit.BYTE_COUNT; - - uint8_t ep_addr = epnum; - dcd_event_xfer_complete(0, ep_addr, total_transfer_size, XFER_RESULT_SUCCESS, true); - } - } -} - -void USB_Handler(void) -{ - uint32_t int_status = USB->DEVICE.INTFLAG.reg & USB->DEVICE.INTENSET.reg; - USB->DEVICE.INTFLAG.reg = int_status; // clear interrupt - - /*------------- Interrupt Processing -------------*/ - if ( int_status & USB_DEVICE_INTFLAG_SOF ) - { - dcd_event_bus_signal(0, DCD_EVENT_SOF, true); - } - - // SAMD doesn't distinguish between Suspend and Disconnect state. - // Both condition will cause SUSPEND interrupt triggered. - // To prevent being triggered when D+/D- are not stable, SUSPEND interrupt is only - // enabled when we received SET_ADDRESS request and cleared on Bus Reset - if ( int_status & USB_DEVICE_INTFLAG_SUSPEND ) - { - // Enable wakeup interrupt - USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_WAKEUP; // clear pending - USB->DEVICE.INTENSET.reg = USB_DEVICE_INTFLAG_WAKEUP; - - dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true); - } - - // Wakeup interrupt is only enabled when we got suspended. - // Wakeup interrupt will disable itself - if ( int_status & USB_DEVICE_INTFLAG_WAKEUP ) - { - // disable wakeup interrupt itself - USB->DEVICE.INTENCLR.reg = USB_DEVICE_INTFLAG_WAKEUP; - dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); - } - - if ( int_status & USB_DEVICE_INTFLAG_EORST ) - { - // Disable both suspend and wakeup interrupt - USB->DEVICE.INTENCLR.reg = USB_DEVICE_INTFLAG_WAKEUP | USB_DEVICE_INTFLAG_SUSPEND; - - bus_reset(); - dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true); - } - - // Setup packet received. - maybe_handle_setup_packet(); - - // Handle complete transfer - maybe_transfer_complete(); -} - -#endif diff --git a/src/portable/microchip/samd51/dcd_samd51.c b/src/portable/microchip/samd51/dcd_samd51.c deleted file mode 100644 index 4bb214cb6..000000000 --- a/src/portable/microchip/samd51/dcd_samd51.c +++ /dev/null @@ -1,384 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries - * - * 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_TUSB_MCU == OPT_MCU_SAMD51 - -#include "sam.h" -#include "device/dcd.h" - -/*------------------------------------------------------------------*/ -/* MACRO TYPEDEF CONSTANT ENUM - *------------------------------------------------------------------*/ -static TU_ATTR_ALIGNED(4) UsbDeviceDescBank sram_registers[8][2]; -static TU_ATTR_ALIGNED(4) uint8_t _setup_packet[8]; - - -// ready for receiving SETUP packet -static inline void prepare_setup(void) -{ - // Only make sure the EP0 OUT buffer is ready - // SETUP token doesn't check any other parameters - sram_registers[0][0].ADDR.reg = (uint32_t) _setup_packet; -} - -// Setup the control endpoint 0. -static void bus_reset(void) -{ - // Max size of packets is 64 bytes. - UsbDeviceDescBank* bank_out = &sram_registers[0][TUSB_DIR_OUT]; - bank_out->PCKSIZE.bit.SIZE = 0x3; - UsbDeviceDescBank* bank_in = &sram_registers[0][TUSB_DIR_IN]; - bank_in->PCKSIZE.bit.SIZE = 0x3; - - UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[0]; - ep->EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE0(0x1) | USB_DEVICE_EPCFG_EPTYPE1(0x1); - ep->EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT0 | USB_DEVICE_EPINTENSET_TRCPT1 | USB_DEVICE_EPINTENSET_RXSTP; - - // Prepare for setup packet - prepare_setup(); -} - -/*------------------------------------------------------------------*/ -/* Controller API - *------------------------------------------------------------------*/ -void dcd_init (uint8_t rhport) -{ - (void) rhport; - - // Reset to get in a clean state. - USB->DEVICE.CTRLA.bit.SWRST = true; - while (USB->DEVICE.SYNCBUSY.bit.SWRST == 0) {} - while (USB->DEVICE.SYNCBUSY.bit.SWRST == 1) {} - - USB->DEVICE.PADCAL.bit.TRANSP = (*((uint32_t*) USB_FUSES_TRANSP_ADDR) & USB_FUSES_TRANSP_Msk) >> USB_FUSES_TRANSP_Pos; - USB->DEVICE.PADCAL.bit.TRANSN = (*((uint32_t*) USB_FUSES_TRANSN_ADDR) & USB_FUSES_TRANSN_Msk) >> USB_FUSES_TRANSN_Pos; - USB->DEVICE.PADCAL.bit.TRIM = (*((uint32_t*) USB_FUSES_TRIM_ADDR) & USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos; - - USB->DEVICE.QOSCTRL.bit.CQOS = 3; - USB->DEVICE.QOSCTRL.bit.DQOS = 3; - - // Configure registers - USB->DEVICE.DESCADD.reg = (uint32_t) &sram_registers; - USB->DEVICE.CTRLB.reg = USB_DEVICE_CTRLB_SPDCONF_FS; - USB->DEVICE.CTRLA.reg = USB_CTRLA_MODE_DEVICE | USB_CTRLA_ENABLE | USB_CTRLA_RUNSTDBY; - while (USB->DEVICE.SYNCBUSY.bit.ENABLE == 1) {} - - USB->DEVICE.INTFLAG.reg |= USB->DEVICE.INTFLAG.reg; // clear pending - USB->DEVICE.INTENSET.reg = /* USB_DEVICE_INTENSET_SOF | */ USB_DEVICE_INTENSET_EORST; -} - -void dcd_int_enable(uint8_t rhport) -{ - (void) rhport; - NVIC_EnableIRQ(USB_0_IRQn); - NVIC_EnableIRQ(USB_1_IRQn); - NVIC_EnableIRQ(USB_2_IRQn); - NVIC_EnableIRQ(USB_3_IRQn); -} - -void dcd_int_disable(uint8_t rhport) -{ - (void) rhport; - NVIC_DisableIRQ(USB_3_IRQn); - NVIC_DisableIRQ(USB_2_IRQn); - NVIC_DisableIRQ(USB_1_IRQn); - NVIC_DisableIRQ(USB_0_IRQn); -} - -void dcd_set_address (uint8_t rhport, uint8_t dev_addr) -{ - (void) dev_addr; - - // Response with zlp status - dcd_edpt_xfer(rhport, 0x80, NULL, 0); - - // DCD can only set address after status for this request is complete - // do it at dcd_edpt0_status_complete() - - // Enable SUSPEND interrupt since the bus signal D+/D- are stable now. - USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTENCLR_SUSPEND; // clear pending - USB->DEVICE.INTENSET.reg = USB_DEVICE_INTENSET_SUSPEND; -} - -void dcd_set_config (uint8_t rhport, uint8_t config_num) -{ - (void) rhport; - (void) config_num; - // Nothing to do -} - -void dcd_remote_wakeup(uint8_t rhport) -{ - (void) rhport; - - USB->DEVICE.CTRLB.bit.UPRSM = 1; -} - -/*------------------------------------------------------------------*/ -/* DCD Endpoint port - *------------------------------------------------------------------*/ - -// 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) -{ - if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && - request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && - request->bRequest == TUSB_REQ_SET_ADDRESS ) - { - uint8_t const dev_addr = (uint8_t) request->wValue; - USB->DEVICE.DADD.reg = USB_DEVICE_DADD_DADD(dev_addr) | USB_DEVICE_DADD_ADDEN; - } - - // Just finished status stage, prepare for next setup packet - // Note: we may already prepare setup when the last EP0 OUT complete. - // but it has no harm to do it again here - prepare_setup(); -} - -bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) -{ - (void) rhport; - - uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress); - uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress); - - UsbDeviceDescBank* bank = &sram_registers[epnum][dir]; - uint32_t size_value = 0; - while (size_value < 7) { - if (1 << (size_value + 3) == desc_edpt->wMaxPacketSize.size) { - break; - } - size_value++; - } - - // unsupported endpoint size - if ( size_value == 7 && desc_edpt->wMaxPacketSize.size != 1023 ) return false; - - bank->PCKSIZE.bit.SIZE = size_value; - - UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum]; - - if ( dir == TUSB_DIR_OUT ) - { - ep->EPCFG.bit.EPTYPE0 = desc_edpt->bmAttributes.xfer + 1; - ep->EPINTENSET.bit.TRCPT0 = true; - }else - { - ep->EPCFG.bit.EPTYPE1 = desc_edpt->bmAttributes.xfer + 1; - ep->EPINTENSET.bit.TRCPT1 = true; - } - - return true; -} - -bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) -{ - (void) rhport; - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - UsbDeviceDescBank* bank = &sram_registers[epnum][dir]; - UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum]; - - bank->ADDR.reg = (uint32_t) buffer; - if ( dir == TUSB_DIR_OUT ) - { - bank->PCKSIZE.bit.MULTI_PACKET_SIZE = total_bytes; - bank->PCKSIZE.bit.BYTE_COUNT = 0; - ep->EPSTATUSCLR.reg |= USB_DEVICE_EPSTATUSCLR_BK0RDY; - ep->EPINTFLAG.reg |= USB_DEVICE_EPINTFLAG_TRFAIL0; - } else - { - bank->PCKSIZE.bit.MULTI_PACKET_SIZE = 0; - bank->PCKSIZE.bit.BYTE_COUNT = total_bytes; - ep->EPSTATUSSET.reg |= USB_DEVICE_EPSTATUSSET_BK1RDY; - ep->EPINTFLAG.reg |= USB_DEVICE_EPINTFLAG_TRFAIL1; - } - - return true; -} - -void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr) -{ - (void) rhport; - - uint8_t const epnum = tu_edpt_number(ep_addr); - UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum]; - - if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { - ep->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ1; - } else { - ep->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ0; - } -} - -void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) -{ - (void) rhport; - - uint8_t const epnum = tu_edpt_number(ep_addr); - UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum]; - - if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { - ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ1 | USB_DEVICE_EPSTATUSCLR_DTGLIN; - } else { - ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ0 | USB_DEVICE_EPSTATUSCLR_DTGLOUT; - } -} - -/*------------------------------------------------------------------*/ - -static bool maybe_handle_setup_packet(void) { - if (USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.bit.RXSTP) - { - // This copies the data elsewhere so we can reuse the buffer. - dcd_event_setup_received(0, _setup_packet, true); - - USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_RXSTP; - - return true; - } - return false; -} -/* - *------------------------------------------------------------------*/ -/* USB_EORSM_DNRSM, USB_EORST_RST, USB_LPMSUSP_DDISC, USB_LPM_DCONN, -USB_MSOF, USB_RAMACER, USB_RXSTP_TXSTP_0, USB_RXSTP_TXSTP_1, -USB_RXSTP_TXSTP_2, USB_RXSTP_TXSTP_3, USB_RXSTP_TXSTP_4, -USB_RXSTP_TXSTP_5, USB_RXSTP_TXSTP_6, USB_RXSTP_TXSTP_7, -USB_STALL0_STALL_0, USB_STALL0_STALL_1, USB_STALL0_STALL_2, -USB_STALL0_STALL_3, USB_STALL0_STALL_4, USB_STALL0_STALL_5, -USB_STALL0_STALL_6, USB_STALL0_STALL_7, USB_STALL1_0, USB_STALL1_1, -USB_STALL1_2, USB_STALL1_3, USB_STALL1_4, USB_STALL1_5, USB_STALL1_6, -USB_STALL1_7, USB_SUSPEND, USB_TRFAIL0_TRFAIL_0, USB_TRFAIL0_TRFAIL_1, -USB_TRFAIL0_TRFAIL_2, USB_TRFAIL0_TRFAIL_3, USB_TRFAIL0_TRFAIL_4, -USB_TRFAIL0_TRFAIL_5, USB_TRFAIL0_TRFAIL_6, USB_TRFAIL0_TRFAIL_7, -USB_TRFAIL1_PERR_0, USB_TRFAIL1_PERR_1, USB_TRFAIL1_PERR_2, -USB_TRFAIL1_PERR_3, USB_TRFAIL1_PERR_4, USB_TRFAIL1_PERR_5, -USB_TRFAIL1_PERR_6, USB_TRFAIL1_PERR_7, USB_UPRSM, USB_WAKEUP */ -void USB_0_Handler(void) { - uint32_t int_status = USB->DEVICE.INTFLAG.reg & USB->DEVICE.INTENSET.reg; - - /*------------- Interrupt Processing -------------*/ - // SAMD doesn't distinguish between Suspend and Disconnect state. - // Both condition will cause SUSPEND interrupt triggered. - // To prevent being triggered when D+/D- are not stable, SUSPEND interrupt is only - // enabled when we received SET_ADDRESS request and cleared on Bus Reset - if ( int_status & USB_DEVICE_INTFLAG_SUSPEND ) - { - USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_SUSPEND; - - // Enable wakeup interrupt - USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_WAKEUP; // clear pending - USB->DEVICE.INTENSET.reg = USB_DEVICE_INTFLAG_WAKEUP; - - dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true); - } - - // Wakeup interrupt is only enabled when we got suspended. - // Wakeup interrupt will disable itself - if ( int_status & USB_DEVICE_INTFLAG_WAKEUP ) - { - USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_WAKEUP; - - // disable wakeup interrupt itself - USB->DEVICE.INTENCLR.reg = USB_DEVICE_INTFLAG_WAKEUP; - dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); - } - - if ( int_status & USB_DEVICE_INTFLAG_EORST ) - { - USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_EORST; - - // Disable both suspend and wakeup interrupt - USB->DEVICE.INTENCLR.reg = USB_DEVICE_INTFLAG_WAKEUP | USB_DEVICE_INTFLAG_SUSPEND; - - bus_reset(); - dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true); - } - - // Setup packet received. - maybe_handle_setup_packet(); -} - -/* USB_SOF_HSOF */ -void USB_1_Handler(void) { - USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_SOF; - dcd_event_bus_signal(0, DCD_EVENT_SOF, true); -} - -void transfer_complete(uint8_t direction) { - uint32_t epints = USB->DEVICE.EPINTSMRY.reg; - for (uint8_t epnum = 0; epnum < USB_EPT_NUM; epnum++) { - if ((epints & (1 << epnum)) == 0) { - continue; - } - - // A SETUP token can occur immediately after an OUT packet - // so make sure we have a valid buffer for the control endpoint. - if (epnum == 0 && direction == TUSB_DIR_OUT ) { - prepare_setup(); - } - - UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum]; - UsbDeviceDescBank* bank = &sram_registers[epnum][direction]; - uint16_t total_transfer_size = bank->PCKSIZE.bit.BYTE_COUNT; - - uint8_t ep_addr = epnum; - if (direction == TUSB_DIR_IN) { - ep_addr |= TUSB_DIR_IN_MASK; - } - dcd_event_xfer_complete(0, ep_addr, total_transfer_size, XFER_RESULT_SUCCESS, true); - - if (direction == TUSB_DIR_IN) { - ep->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT1; - } else { - ep->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT0; - } - } -} - -// Bank zero is for OUT and SETUP transactions. -/* USB_TRCPT0_0, USB_TRCPT0_1, USB_TRCPT0_2, -USB_TRCPT0_3, USB_TRCPT0_4, USB_TRCPT0_5, -USB_TRCPT0_6, USB_TRCPT0_7 */ -void USB_2_Handler(void) { - transfer_complete(TUSB_DIR_OUT); -} - -// Bank one is used for IN transactions. -/* USB_TRCPT1_0, USB_TRCPT1_1, USB_TRCPT1_2, -USB_TRCPT1_3, USB_TRCPT1_4, USB_TRCPT1_5, -USB_TRCPT1_6, USB_TRCPT1_7 */ -void USB_3_Handler(void) { - transfer_complete(TUSB_DIR_IN); -} - -#endif -- cgit v1.3.1 From 0da45c2e78598dc311bfe08fa845e8276e299418 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 26 Dec 2019 22:31:29 +0700 Subject: clean up --- src/portable/microchip/samd/dcd_samd.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'src/portable') diff --git a/src/portable/microchip/samd/dcd_samd.c b/src/portable/microchip/samd/dcd_samd.c index eccc9c097..5f904b313 100644 --- a/src/portable/microchip/samd/dcd_samd.c +++ b/src/portable/microchip/samd/dcd_samd.c @@ -93,33 +93,40 @@ void dcd_init (uint8_t rhport) USB->DEVICE.INTENSET.reg = /* USB_DEVICE_INTENSET_SOF | */ USB_DEVICE_INTENSET_EORST; } +#if CFG_TUSB_MCU == OPT_MCU_SAMD51 + void dcd_int_enable(uint8_t rhport) { (void) rhport; - -#if CFG_TUSB_MCU == OPT_MCU_SAMD51 NVIC_EnableIRQ(USB_0_IRQn); NVIC_EnableIRQ(USB_1_IRQn); NVIC_EnableIRQ(USB_2_IRQn); NVIC_EnableIRQ(USB_3_IRQn); -#elif CFG_TUSB_MCU == OPT_MCU_SAMD21 - NVIC_EnableIRQ(USB_IRQn); -#endif } void dcd_int_disable(uint8_t rhport) { (void) rhport; - -#if CFG_TUSB_MCU == OPT_MCU_SAMD51 NVIC_DisableIRQ(USB_3_IRQn); NVIC_DisableIRQ(USB_2_IRQn); NVIC_DisableIRQ(USB_1_IRQn); NVIC_DisableIRQ(USB_0_IRQn); +} + #elif CFG_TUSB_MCU == OPT_MCU_SAMD21 + +void dcd_int_enable(uint8_t rhport) +{ + (void) rhport; + NVIC_EnableIRQ(USB_IRQn); +} + +void dcd_int_disable(uint8_t rhport) +{ + (void) rhport; NVIC_DisableIRQ(USB_IRQn); -#endif } +#endif void dcd_set_address (uint8_t rhport, uint8_t dev_addr) { -- cgit v1.3.1 From 89f3ad59da093823cd48e6ca1ae2e09bb3e9309a Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 27 Dec 2019 09:42:57 +0700 Subject: fix #247 --- src/portable/microchip/samd/dcd_samd.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'src/portable') diff --git a/src/portable/microchip/samd/dcd_samd.c b/src/portable/microchip/samd/dcd_samd.c index 5f904b313..148995520 100644 --- a/src/portable/microchip/samd/dcd_samd.c +++ b/src/portable/microchip/samd/dcd_samd.c @@ -42,8 +42,9 @@ static TU_ATTR_ALIGNED(4) uint8_t _setup_packet[8]; static inline void prepare_setup(void) { // Only make sure the EP0 OUT buffer is ready - // SETUP token doesn't check any other parameters sram_registers[0][0].ADDR.reg = (uint32_t) _setup_packet; + sram_registers[0][0].PCKSIZE.bit.MULTI_PACKET_SIZE = sizeof(_setup_packet); + sram_registers[0][0].PCKSIZE.bit.BYTE_COUNT = 0; } // Setup the control endpoint 0. @@ -292,8 +293,7 @@ void maybe_transfer_complete(void) { UsbDeviceDescBank* bank = &sram_registers[epnum][TUSB_DIR_IN]; uint16_t total_transfer_size = bank->PCKSIZE.bit.BYTE_COUNT; - uint8_t ep_addr = epnum | TUSB_DIR_IN_MASK; - dcd_event_xfer_complete(0, ep_addr, total_transfer_size, XFER_RESULT_SUCCESS, true); + dcd_event_xfer_complete(0, epnum | TUSB_DIR_IN_MASK, total_transfer_size, XFER_RESULT_SUCCESS, true); ep->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT1; } @@ -301,17 +301,16 @@ void maybe_transfer_complete(void) { // Handle OUT completions if ((epintflag & USB_DEVICE_EPINTFLAG_TRCPT0) != 0) { + UsbDeviceDescBank* bank = &sram_registers[epnum][TUSB_DIR_OUT]; + uint16_t total_transfer_size = bank->PCKSIZE.bit.BYTE_COUNT; + // A SETUP token can occur immediately after an OUT packet // so make sure we have a valid buffer for the control endpoint. if (epnum == 0) { prepare_setup(); } - UsbDeviceDescBank* bank = &sram_registers[epnum][TUSB_DIR_OUT]; - uint16_t total_transfer_size = bank->PCKSIZE.bit.BYTE_COUNT; - - uint8_t ep_addr = epnum; - dcd_event_xfer_complete(0, ep_addr, total_transfer_size, XFER_RESULT_SUCCESS, true); + dcd_event_xfer_complete(0, epnum, total_transfer_size, XFER_RESULT_SUCCESS, true); ep->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT0; } -- cgit v1.3.1 From f4371e79b10f85b0f8f6112c8e81b92072bacea0 Mon Sep 17 00:00:00 2001 From: Peter Lawrence <12226419+majbthrd@users.noreply.github.com> Date: Sat, 4 Jan 2020 10:31:55 -0600 Subject: add Nuvoton NUC121/NUC125/NUC126 --- .gitmodules | 3 + README.md | 1 + hw/bsp/nutiny_nuc121s/board.mk | 54 ++++ hw/bsp/nutiny_nuc121s/nuc121_flash.ld | 195 ++++++++++++++ hw/bsp/nutiny_nuc121s/nutiny_nuc121.c | 102 ++++++++ hw/bsp/nutiny_nuc125s/board.mk | 37 +++ hw/bsp/nutiny_nuc125s/nuc125_flash.ld | 195 ++++++++++++++ hw/bsp/nutiny_nuc125s/nutiny_nuc125.c | 102 ++++++++ hw/bsp/nutiny_nuc126v/board.mk | 61 +++++ hw/bsp/nutiny_nuc126v/nuc126_flash.ld | 195 ++++++++++++++ hw/bsp/nutiny_nuc126v/nutiny_nuc126.c | 133 ++++++++++ hw/mcu/nuvoton | 1 + src/portable/nuvoton/nuc121/dcd_nuc121.c | 434 +++++++++++++++++++++++++++++++ src/tusb_option.h | 3 + 14 files changed, 1516 insertions(+) create mode 100644 hw/bsp/nutiny_nuc121s/board.mk create mode 100644 hw/bsp/nutiny_nuc121s/nuc121_flash.ld create mode 100644 hw/bsp/nutiny_nuc121s/nutiny_nuc121.c create mode 100644 hw/bsp/nutiny_nuc125s/board.mk create mode 100644 hw/bsp/nutiny_nuc125s/nuc125_flash.ld create mode 100644 hw/bsp/nutiny_nuc125s/nutiny_nuc125.c create mode 100644 hw/bsp/nutiny_nuc126v/board.mk create mode 100644 hw/bsp/nutiny_nuc126v/nuc126_flash.ld create mode 100644 hw/bsp/nutiny_nuc126v/nutiny_nuc126.c create mode 160000 hw/mcu/nuvoton create mode 100644 src/portable/nuvoton/nuc121/dcd_nuc121.c (limited to 'src/portable') diff --git a/.gitmodules b/.gitmodules index c7b396187..7565f199c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,6 @@ [submodule "hw/mcu/nxp"] path = hw/mcu/nxp url = https://github.com/hathach/nxp_driver.git +[submodule "hw/mcu/nuvoton"] + path = hw/mcu/nuvoton + url = https://github.com/majbthrd/nuc_driver.git diff --git a/README.md b/README.md index 3f7556cf1..f06b5c417 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ The stack supports the following MCUs - **Sony:** CXD56 - **ST:** STM32 series: L0, F0, F1, F2, F3, F4, F7, H7 (device only) - **[ValentyUSB](https://github.com/im-tomu/valentyusb)** eptri +- **Nuvoton:** NUC121/NUC125, NUC126 [Here is the list of supported Boards](docs/boards.md) that can be used with provided examples. diff --git a/hw/bsp/nutiny_nuc121s/board.mk b/hw/bsp/nutiny_nuc121s/board.mk new file mode 100644 index 000000000..dfd0e80cd --- /dev/null +++ b/hw/bsp/nutiny_nuc121s/board.mk @@ -0,0 +1,54 @@ +CFLAGS += \ + -flto \ + -mthumb \ + -mabi=aapcs-linux \ + -mcpu=cortex-m0 \ + -D__ARM_FEATURE_DSP=0 \ + -DUSE_ASSERT=0 \ + -DCFG_TUSB_MCU=OPT_MCU_NUC121 + +# All source paths should be relative to the top level. +LD_FILE = hw/bsp/nutiny_nuc121s/nuc121_flash.ld + +SRC_C += \ + hw/mcu/nuvoton/nuc121_125/Device/Nuvoton/NUC121/Source/system_NUC121.c \ + hw/mcu/nuvoton/nuc121_125/StdDriver/src/adc.c \ + hw/mcu/nuvoton/nuc121_125/StdDriver/src/bpwm.c \ + hw/mcu/nuvoton/nuc121_125/StdDriver/src/clk.c \ + hw/mcu/nuvoton/nuc121_125/StdDriver/src/fmc.c \ + hw/mcu/nuvoton/nuc121_125/StdDriver/src/gpio.c \ + hw/mcu/nuvoton/nuc121_125/StdDriver/src/i2c.c \ + hw/mcu/nuvoton/nuc121_125/StdDriver/src/pdma.c \ + hw/mcu/nuvoton/nuc121_125/StdDriver/src/pwm.c \ + hw/mcu/nuvoton/nuc121_125/StdDriver/src/spi_i2s.c \ + hw/mcu/nuvoton/nuc121_125/StdDriver/src/sys.c \ + hw/mcu/nuvoton/nuc121_125/StdDriver/src/timer.c \ + hw/mcu/nuvoton/nuc121_125/StdDriver/src/uart.c \ + hw/mcu/nuvoton/nuc121_125/StdDriver/src/usbd.c \ + hw/mcu/nuvoton/nuc121_125/StdDriver/src/usci_i2c.c \ + hw/mcu/nuvoton/nuc121_125/StdDriver/src/usci_spi.c \ + hw/mcu/nuvoton/nuc121_125/StdDriver/src/usci_uart.c \ + hw/mcu/nuvoton/nuc121_125/StdDriver/src/wdt.c \ + hw/mcu/nuvoton/nuc121_125/StdDriver/src/wwdt.c + +SRC_S += \ + hw/mcu/nuvoton/nuc121_125/Device/Nuvoton/NUC121/Source/GCC/startup_NUC121.S + +INC += \ + $(TOP)/hw/mcu/nuvoton/nuc121_125/Device/Nuvoton/NUC121/Include \ + $(TOP)/hw/mcu/nuvoton/nuc121_125/StdDriver/inc \ + $(TOP)/hw/mcu/nuvoton/nuc121_125/CMSIS/Include + +# For TinyUSB port source +VENDOR = nuvoton +CHIP_FAMILY = nuc121 + +# For freeRTOS port source +FREERTOS_PORT = ARM_CM0 + +# For flash-jlink target +JLINK_DEVICE = NUC121SC2AE +JLINK_IF = swd + +# flash using jlink +flash: flash-jlink diff --git a/hw/bsp/nutiny_nuc121s/nuc121_flash.ld b/hw/bsp/nutiny_nuc121s/nuc121_flash.ld new file mode 100644 index 000000000..3966b276e --- /dev/null +++ b/hw/bsp/nutiny_nuc121s/nuc121_flash.ld @@ -0,0 +1,195 @@ +/* Linker script to configure memory regions. */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x8000 /* 32k */ + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x2000 /* 8k */ +} + +/* Library configurations */ +GROUP(libgcc.a libc.a libm.a libnosys.a) + +/* Linker script to place sections and symbol values. Should be used together + * with other linker script that defines memory regions FLASH and RAM. + * It references following symbols, which must be defined in code: + * Reset_Handler : Entry of reset handler + * + * It defines following symbols, which code can use without definition: + * __exidx_start + * __exidx_end + * __copy_table_start__ + * __copy_table_end__ + * __zero_table_start__ + * __zero_table_end__ + * __etext + * __data_start__ + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __data_end__ + * __bss_start__ + * __bss_end__ + * __end__ + * end + * __HeapLimit + * __StackLimit + * __StackTop + * __stack + * __Vectors_End + * __Vectors_Size + */ +ENTRY(Reset_Handler) + +SECTIONS +{ + .text : + { + KEEP(*(.vectors)) + __Vectors_End = .; + __Vectors_Size = __Vectors_End - __Vectors; + __end__ = .; + + *(.text*) + + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.rodata*) + + KEEP(*(.eh_frame*)) + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* To copy multiple ROM to RAM sections, + * uncomment .copy.table section and, + * define __STARTUP_COPY_MULTIPLE in startup_ARMCMx.S */ + /* + .copy.table : + { + . = ALIGN(4); + __copy_table_start__ = .; + LONG (__etext) + LONG (__data_start__) + LONG (__data_end__ - __data_start__) + LONG (__etext2) + LONG (__data2_start__) + LONG (__data2_end__ - __data2_start__) + __copy_table_end__ = .; + } > FLASH + */ + + /* To clear multiple BSS sections, + * uncomment .zero.table section and, + * define __STARTUP_CLEAR_BSS_MULTIPLE in startup_ARMCMx.S */ + /* + .zero.table : + { + . = ALIGN(4); + __zero_table_start__ = .; + LONG (__bss_start__) + LONG (__bss_end__ - __bss_start__) + LONG (__bss2_start__) + LONG (__bss2_end__ - __bss2_start__) + __zero_table_end__ = .; + } > FLASH + */ + + __etext = .; + + .data : AT (__etext) + { + __data_start__ = .; + *(vtable) + *(.data*) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(4); + /* All data end */ + __data_end__ = .; + + } > RAM + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (COPY): + { + __HeapBase = .; + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + __HeapLimit = .; + } > RAM + + /* .stack_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later */ + .stack_dummy (COPY): + { + KEEP(*(.stack*)) + } > RAM + + /* Set stack top to end of RAM, and stack limit move down by + * size of stack_dummy section */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") +} diff --git a/hw/bsp/nutiny_nuc121s/nutiny_nuc121.c b/hw/bsp/nutiny_nuc121s/nutiny_nuc121.c new file mode 100644 index 000000000..51ca28a82 --- /dev/null +++ b/hw/bsp/nutiny_nuc121s/nutiny_nuc121.c @@ -0,0 +1,102 @@ +/* + * 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 "bsp/board.h" +#include "NuMicro.h" +#include "clk.h" +#include "sys.h" + +void board_init(void) +{ + /* Unlock protected registers */ + SYS_UnlockReg(); + + /*---------------------------------------------------------------------------------------------------------*/ + /* Init System Clock */ + /*---------------------------------------------------------------------------------------------------------*/ + + /* Enable Internal HIRC 48 MHz clock */ + CLK_EnableXtalRC(CLK_PWRCTL_HIRCEN); + + /* Waiting for Internal RC clock ready */ + CLK_WaitClockReady(CLK_STATUS_HIRCSTB_Msk); + + /* Switch HCLK clock source to Internal HIRC and HCLK source divide 1 */ + CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_HIRC, CLK_CLKDIV0_HCLK(1)); + + /* Enable module clock */ + CLK_EnableModuleClock(USBD_MODULE); + + /* Select module clock source */ + CLK_SetModuleClock(USBD_MODULE, CLK_CLKSEL3_USBDSEL_HIRC, CLK_CLKDIV0_USB(1)); + + /* Enable module clock */ + CLK_EnableModuleClock(USBD_MODULE); + +#if CFG_TUSB_OS == OPT_OS_NONE + // 1ms tick timer + SysTick_Config(48000000 / 1000); +#endif +} + +#if CFG_TUSB_OS == OPT_OS_NONE +volatile uint32_t system_ticks = 0; +void SysTick_Handler (void) +{ + system_ticks++; +} + +uint32_t board_millis(void) +{ + return system_ticks; +} +#endif + +//--------------------------------------------------------------------+ +// Board porting API +//--------------------------------------------------------------------+ + +void board_led_write(bool state) +{ + (void)(state); +} + +uint32_t board_button_read(void) +{ + return 0; +} + +int board_uart_read(uint8_t* buf, int len) +{ + (void) buf; (void) len; + return 0; +} + +int board_uart_write(void const * buf, int len) +{ + (void) buf; (void) len; + return 0; +} diff --git a/hw/bsp/nutiny_nuc125s/board.mk b/hw/bsp/nutiny_nuc125s/board.mk new file mode 100644 index 000000000..8e4d78ceb --- /dev/null +++ b/hw/bsp/nutiny_nuc125s/board.mk @@ -0,0 +1,37 @@ +CFLAGS += \ + -flto \ + -mthumb \ + -mabi=aapcs-linux \ + -mcpu=cortex-m0 \ + -D__ARM_FEATURE_DSP=0 \ + -DUSE_ASSERT=0 \ + -DCFG_TUSB_MCU=OPT_MCU_NUC121 + +# All source paths should be relative to the top level. +LD_FILE = hw/bsp/nutiny_nuc125s/nuc125_flash.ld + +SRC_C += \ + hw/mcu/nuvoton/nuc121_125/Device/Nuvoton/NUC121/Source/system_NUC121.c \ + hw/mcu/nuvoton/nuc121_125/StdDriver/src/clk.c + +SRC_S += \ + hw/mcu/nuvoton/nuc121_125/Device/Nuvoton/NUC121/Source/GCC/startup_NUC121.S + +INC += \ + $(TOP)/hw/mcu/nuvoton/nuc121_125/Device/Nuvoton/NUC121/Include \ + $(TOP)/hw/mcu/nuvoton/nuc121_125/StdDriver/inc \ + $(TOP)/hw/mcu/nuvoton/nuc121_125/CMSIS/Include + +# For TinyUSB port source +VENDOR = nuvoton +CHIP_FAMILY = nuc121 + +# For freeRTOS port source +FREERTOS_PORT = ARM_CM0 + +# For flash-jlink target +JLINK_DEVICE = NUC125SC2AE +JLINK_IF = swd + +# flash using jlink +flash: flash-jlink diff --git a/hw/bsp/nutiny_nuc125s/nuc125_flash.ld b/hw/bsp/nutiny_nuc125s/nuc125_flash.ld new file mode 100644 index 000000000..3966b276e --- /dev/null +++ b/hw/bsp/nutiny_nuc125s/nuc125_flash.ld @@ -0,0 +1,195 @@ +/* Linker script to configure memory regions. */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x8000 /* 32k */ + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x2000 /* 8k */ +} + +/* Library configurations */ +GROUP(libgcc.a libc.a libm.a libnosys.a) + +/* Linker script to place sections and symbol values. Should be used together + * with other linker script that defines memory regions FLASH and RAM. + * It references following symbols, which must be defined in code: + * Reset_Handler : Entry of reset handler + * + * It defines following symbols, which code can use without definition: + * __exidx_start + * __exidx_end + * __copy_table_start__ + * __copy_table_end__ + * __zero_table_start__ + * __zero_table_end__ + * __etext + * __data_start__ + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __data_end__ + * __bss_start__ + * __bss_end__ + * __end__ + * end + * __HeapLimit + * __StackLimit + * __StackTop + * __stack + * __Vectors_End + * __Vectors_Size + */ +ENTRY(Reset_Handler) + +SECTIONS +{ + .text : + { + KEEP(*(.vectors)) + __Vectors_End = .; + __Vectors_Size = __Vectors_End - __Vectors; + __end__ = .; + + *(.text*) + + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.rodata*) + + KEEP(*(.eh_frame*)) + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* To copy multiple ROM to RAM sections, + * uncomment .copy.table section and, + * define __STARTUP_COPY_MULTIPLE in startup_ARMCMx.S */ + /* + .copy.table : + { + . = ALIGN(4); + __copy_table_start__ = .; + LONG (__etext) + LONG (__data_start__) + LONG (__data_end__ - __data_start__) + LONG (__etext2) + LONG (__data2_start__) + LONG (__data2_end__ - __data2_start__) + __copy_table_end__ = .; + } > FLASH + */ + + /* To clear multiple BSS sections, + * uncomment .zero.table section and, + * define __STARTUP_CLEAR_BSS_MULTIPLE in startup_ARMCMx.S */ + /* + .zero.table : + { + . = ALIGN(4); + __zero_table_start__ = .; + LONG (__bss_start__) + LONG (__bss_end__ - __bss_start__) + LONG (__bss2_start__) + LONG (__bss2_end__ - __bss2_start__) + __zero_table_end__ = .; + } > FLASH + */ + + __etext = .; + + .data : AT (__etext) + { + __data_start__ = .; + *(vtable) + *(.data*) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(4); + /* All data end */ + __data_end__ = .; + + } > RAM + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (COPY): + { + __HeapBase = .; + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + __HeapLimit = .; + } > RAM + + /* .stack_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later */ + .stack_dummy (COPY): + { + KEEP(*(.stack*)) + } > RAM + + /* Set stack top to end of RAM, and stack limit move down by + * size of stack_dummy section */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") +} diff --git a/hw/bsp/nutiny_nuc125s/nutiny_nuc125.c b/hw/bsp/nutiny_nuc125s/nutiny_nuc125.c new file mode 100644 index 000000000..51ca28a82 --- /dev/null +++ b/hw/bsp/nutiny_nuc125s/nutiny_nuc125.c @@ -0,0 +1,102 @@ +/* + * 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 "bsp/board.h" +#include "NuMicro.h" +#include "clk.h" +#include "sys.h" + +void board_init(void) +{ + /* Unlock protected registers */ + SYS_UnlockReg(); + + /*---------------------------------------------------------------------------------------------------------*/ + /* Init System Clock */ + /*---------------------------------------------------------------------------------------------------------*/ + + /* Enable Internal HIRC 48 MHz clock */ + CLK_EnableXtalRC(CLK_PWRCTL_HIRCEN); + + /* Waiting for Internal RC clock ready */ + CLK_WaitClockReady(CLK_STATUS_HIRCSTB_Msk); + + /* Switch HCLK clock source to Internal HIRC and HCLK source divide 1 */ + CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_HIRC, CLK_CLKDIV0_HCLK(1)); + + /* Enable module clock */ + CLK_EnableModuleClock(USBD_MODULE); + + /* Select module clock source */ + CLK_SetModuleClock(USBD_MODULE, CLK_CLKSEL3_USBDSEL_HIRC, CLK_CLKDIV0_USB(1)); + + /* Enable module clock */ + CLK_EnableModuleClock(USBD_MODULE); + +#if CFG_TUSB_OS == OPT_OS_NONE + // 1ms tick timer + SysTick_Config(48000000 / 1000); +#endif +} + +#if CFG_TUSB_OS == OPT_OS_NONE +volatile uint32_t system_ticks = 0; +void SysTick_Handler (void) +{ + system_ticks++; +} + +uint32_t board_millis(void) +{ + return system_ticks; +} +#endif + +//--------------------------------------------------------------------+ +// Board porting API +//--------------------------------------------------------------------+ + +void board_led_write(bool state) +{ + (void)(state); +} + +uint32_t board_button_read(void) +{ + return 0; +} + +int board_uart_read(uint8_t* buf, int len) +{ + (void) buf; (void) len; + return 0; +} + +int board_uart_write(void const * buf, int len) +{ + (void) buf; (void) len; + return 0; +} diff --git a/hw/bsp/nutiny_nuc126v/board.mk b/hw/bsp/nutiny_nuc126v/board.mk new file mode 100644 index 000000000..6d87c9d9d --- /dev/null +++ b/hw/bsp/nutiny_nuc126v/board.mk @@ -0,0 +1,61 @@ +CFLAGS += \ + -flto \ + -mthumb \ + -mabi=aapcs-linux \ + -mcpu=cortex-m0 \ + -D__ARM_FEATURE_DSP=0 \ + -DUSE_ASSERT=0 \ + -D__CORTEX_SC=0 \ + -DCFG_TUSB_MCU=OPT_MCU_NUC126 + +# All source paths should be relative to the top level. +LD_FILE = hw/bsp/nutiny_nuc126v/nuc126_flash.ld + +SRC_C += \ + hw/mcu/nuvoton/nuc126/Device/Nuvoton/NUC126/Source/system_NUC126.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/acmp.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/adc.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/clk.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/crc.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/ebi.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/fmc.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/gpio.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/i2c.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/pdma.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/pwm.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/rtc.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/sc.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/scuart.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/spi.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/sys.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/timer.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/timer_pwm.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/uart.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/usbd.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/usci_i2c.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/usci_spi.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/usci_uart.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/wdt.c \ + hw/mcu/nuvoton/nuc126/StdDriver/src/wwdt.c + +SRC_S += \ + hw/mcu/nuvoton/nuc126/Device/Nuvoton/NUC126/Source/GCC/startup_NUC126.S + +INC += \ + $(TOP)/hw/mcu/nuvoton/nuc126/Device/Nuvoton/NUC126/Include \ + $(TOP)/hw/mcu/nuvoton/nuc126/StdDriver/inc \ + $(TOP)/hw/mcu/nuvoton/nuc126/CMSIS/Include + +# For TinyUSB port source +VENDOR = nuvoton +CHIP_FAMILY = nuc121 + +# For freeRTOS port source +FREERTOS_PORT = ARM_CM0 + +# For flash-jlink target +JLINK_DEVICE = NUC126VG4AE +JLINK_IF = swd + +# flash using jlink +flash: flash-jlink diff --git a/hw/bsp/nutiny_nuc126v/nuc126_flash.ld b/hw/bsp/nutiny_nuc126v/nuc126_flash.ld new file mode 100644 index 000000000..b23890b4b --- /dev/null +++ b/hw/bsp/nutiny_nuc126v/nuc126_flash.ld @@ -0,0 +1,195 @@ +/* Linker script to configure memory regions. */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x40000 /* 256k */ + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x5000 /* 20k */ +} + +/* Library configurations */ +GROUP(libgcc.a libc.a libm.a libnosys.a) + +/* Linker script to place sections and symbol values. Should be used together + * with other linker script that defines memory regions FLASH and RAM. + * It references following symbols, which must be defined in code: + * Reset_Handler : Entry of reset handler + * + * It defines following symbols, which code can use without definition: + * __exidx_start + * __exidx_end + * __copy_table_start__ + * __copy_table_end__ + * __zero_table_start__ + * __zero_table_end__ + * __etext + * __data_start__ + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __data_end__ + * __bss_start__ + * __bss_end__ + * __end__ + * end + * __HeapLimit + * __StackLimit + * __StackTop + * __stack + * __Vectors_End + * __Vectors_Size + */ +ENTRY(Reset_Handler) + +SECTIONS +{ + .text : + { + KEEP(*(.vectors)) + __Vectors_End = .; + __Vectors_Size = __Vectors_End - __Vectors; + __end__ = .; + + *(.text*) + + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.rodata*) + + KEEP(*(.eh_frame*)) + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* To copy multiple ROM to RAM sections, + * uncomment .copy.table section and, + * define __STARTUP_COPY_MULTIPLE in startup_ARMCMx.S */ + /* + .copy.table : + { + . = ALIGN(4); + __copy_table_start__ = .; + LONG (__etext) + LONG (__data_start__) + LONG (__data_end__ - __data_start__) + LONG (__etext2) + LONG (__data2_start__) + LONG (__data2_end__ - __data2_start__) + __copy_table_end__ = .; + } > FLASH + */ + + /* To clear multiple BSS sections, + * uncomment .zero.table section and, + * define __STARTUP_CLEAR_BSS_MULTIPLE in startup_ARMCMx.S */ + /* + .zero.table : + { + . = ALIGN(4); + __zero_table_start__ = .; + LONG (__bss_start__) + LONG (__bss_end__ - __bss_start__) + LONG (__bss2_start__) + LONG (__bss2_end__ - __bss2_start__) + __zero_table_end__ = .; + } > FLASH + */ + + __etext = .; + + .data : AT (__etext) + { + __data_start__ = .; + *(vtable) + *(.data*) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(4); + /* All data end */ + __data_end__ = .; + + } > RAM + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (COPY): + { + __HeapBase = .; + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + __HeapLimit = .; + } > RAM + + /* .stack_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later */ + .stack_dummy (COPY): + { + KEEP(*(.stack*)) + } > RAM + + /* Set stack top to end of RAM, and stack limit move down by + * size of stack_dummy section */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") +} diff --git a/hw/bsp/nutiny_nuc126v/nutiny_nuc126.c b/hw/bsp/nutiny_nuc126v/nutiny_nuc126.c new file mode 100644 index 000000000..0ce787d3e --- /dev/null +++ b/hw/bsp/nutiny_nuc126v/nutiny_nuc126.c @@ -0,0 +1,133 @@ +/* + * 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 "bsp/board.h" +#include "NuMicro.h" +#include "clk.h" +#include "sys.h" + +#define CRYSTAL_LESS /* system will be 48MHz when defined, otherwise, system is 72MHz */ +#define HIRC48_AUTO_TRIM SYS_IRCTCTL1_REFCKSEL_Msk | (1UL << SYS_IRCTCTL1_LOOPSEL_Pos) | (2UL << SYS_IRCTCTL1_FREQSEL_Pos) +#define TRIM_INIT (SYS_BASE+0x118) + +void board_init(void) +{ + /* Unlock protected registers */ + SYS_UnlockReg(); + + /*---------------------------------------------------------------------------------------------------------*/ + /* Init System Clock */ + /*---------------------------------------------------------------------------------------------------------*/ + + /* Enable Internal RC 22.1184 MHz clock */ + CLK_EnableXtalRC(CLK_PWRCTL_HIRCEN_Msk); + + /* Waiting for Internal RC clock ready */ + CLK_WaitClockReady(CLK_STATUS_HIRCSTB_Msk); + + /* Switch HCLK clock source to Internal RC and HCLK source divide 1 */ + CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_HIRC, CLK_CLKDIV0_HCLK(1)); + +#ifndef CRYSTAL_LESS + /* Enable external XTAL 12 MHz clock */ + CLK_EnableXtalRC(CLK_PWRCTL_HXTEN_Msk); + + /* Waiting for external XTAL clock ready */ + CLK_WaitClockReady(CLK_STATUS_HXTSTB_Msk); + + /* Set core clock */ + CLK_SetCoreClock(72000000); + + /* Use HIRC as UART clock source */ + CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL1_UARTSEL_HIRC, CLK_CLKDIV0_UART(1)); + + /* Use PLL as USB clock source */ + CLK_SetModuleClock(USBD_MODULE, CLK_CLKSEL3_USBDSEL_PLL, CLK_CLKDIV0_USB(3)); + +#else + /* Enable Internal RC 48MHz clock */ + CLK_EnableXtalRC(CLK_PWRCTL_HIRC48EN_Msk); + + /* Waiting for Internal RC clock ready */ + CLK_WaitClockReady(CLK_STATUS_HIRC48STB_Msk); + + /* Switch HCLK clock source to Internal RC and HCLK source divide 1 */ + CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_HIRC48, CLK_CLKDIV0_HCLK(1)); + + /* Use HIRC as UART clock source */ + CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL1_UARTSEL_HIRC, CLK_CLKDIV0_UART(1)); + + /* Use HIRC48 as USB clock source */ + CLK_SetModuleClock(USBD_MODULE, CLK_CLKSEL3_USBDSEL_HIRC48, CLK_CLKDIV0_USB(1)); +#endif + + /* Enable module clock */ + CLK_EnableModuleClock(USBD_MODULE); + +#if CFG_TUSB_OS == OPT_OS_NONE + // 1ms tick timer + SysTick_Config(48000000 / 1000); +#endif +} + +#if CFG_TUSB_OS == OPT_OS_NONE +volatile uint32_t system_ticks = 0; +void SysTick_Handler (void) +{ + system_ticks++; +} + +uint32_t board_millis(void) +{ + return system_ticks; +} +#endif + +//--------------------------------------------------------------------+ +// Board porting API +//--------------------------------------------------------------------+ + +void board_led_write(bool state) +{ + (void)(state); +} + +uint32_t board_button_read(void) +{ + return 0; +} + +int board_uart_read(uint8_t* buf, int len) +{ + (void) buf; (void) len; + return 0; +} + +int board_uart_write(void const * buf, int len) +{ + (void) buf; (void) len; + return 0; +} diff --git a/hw/mcu/nuvoton b/hw/mcu/nuvoton new file mode 160000 index 000000000..dc96fff79 --- /dev/null +++ b/hw/mcu/nuvoton @@ -0,0 +1 @@ +Subproject commit dc96fff794d14818c93ea1d4d760d51a014d70c5 diff --git a/src/portable/nuvoton/nuc121/dcd_nuc121.c b/src/portable/nuvoton/nuc121/dcd_nuc121.c new file mode 100644 index 000000000..57db5231d --- /dev/null +++ b/src/portable/nuvoton/nuc121/dcd_nuc121.c @@ -0,0 +1,434 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Peter Lawrence + * + * 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. + */ + +/* + Theory of operation: + + The NUC121/NUC125/NUC126 USBD peripheral has eight "EP"s, but each is simplex, + so two collectively (peripheral nomenclature of "EP0" and "EP1") are needed to + implement USB EP0. PERIPH_EP0 and PERIPH_EP1 are used by this driver for + EP0_IN and EP0_OUT respectively. This leaves up to six for user usage. +*/ + +#include "tusb_option.h" + +#if TUSB_OPT_DEVICE_ENABLED && ( (CFG_TUSB_MCU == OPT_MCU_NUC121) || (CFG_TUSB_MCU == OPT_MCU_NUC126) ) + +#include "device/dcd.h" +#include "NuMicro.h" + +/* allocation of USBD RAM for Setup, EP0_IN, and and EP_OUT */ +#define PERIPH_SETUP_BUF_BASE 0 +#define PERIPH_SETUP_BUF_LEN 8 +#define PERIPH_EP0_BUF_BASE (PERIPH_SETUP_BUF_BASE + PERIPH_SETUP_BUF_LEN) +#define PERIPH_EP0_BUF_LEN CFG_TUD_ENDPOINT0_SIZE +#define PERIPH_EP1_BUF_BASE (PERIPH_EP0_BUF_BASE + PERIPH_EP0_BUF_LEN) +#define PERIPH_EP1_BUF_LEN CFG_TUD_ENDPOINT0_SIZE +#define PERIPH_EP2_BUF_BASE (PERIPH_EP1_BUF_BASE + PERIPH_EP1_BUF_LEN) + +/* rather important info unfortunately not provided by device include files: how much there is */ +#define USBD_BUF_SIZE ((CFG_TUSB_MCU == OPT_MCU_NUC121) ? 768 : 512) + +enum ep_enum +{ + PERIPH_EP0 = 0, + PERIPH_EP1 = 1, + PERIPH_EP2 = 2, + PERIPH_EP3 = 3, + PERIPH_EP4 = 4, + PERIPH_EP5 = 5, + PERIPH_EP6 = 6, + PERIPH_EP7 = 7, + PERIPH_MAX_EP, +}; + +/* set by dcd_set_address() */ +static volatile uint8_t assigned_address; + +/* reset by dcd_init(), this is used by dcd_edpt_open() to assign USBD peripheral buffer addresses */ +static uint32_t bufseg_addr; + +/* used by dcd_edpt_xfer() and the ISR to reset the data sync (DATA0/DATA1) in an EP0_IN transfer */ +static bool active_ep0_xfer; + +/* RAM table needed to track ongoing transfers performed by dcd_edpt_xfer(), dcd_in_xfer(), and the ISR */ +static struct xfer_ctl_t +{ + uint8_t *data_ptr; /* collectively, data_ptr and remaining_bytes track progress of endpoint transfers */ + uint16_t remaining_bytes; + uint16_t max_packet_size; /* needed since device driver only finds out this at runtime */ + uint16_t total_bytes; /* quantity needed to pass as argument to dcd_event_xfer_complete() (for IN endpoints) */ +} xfer_table[PERIPH_MAX_EP]; + +/* + local helper functions +*/ + +static void usb_attach(void) +{ + USBD->SE0 &= ~USBD_SE0_SE0_Msk; +} + +static void usb_detach(void) +{ + USBD->SE0 |= USBD_SE0_SE0_Msk; +} + +static void usb_control_send_zlp(void) +{ + USBD->EP[PERIPH_EP0].CFG |= USBD_CFG_DSQSYNC_Msk; + USBD->EP[PERIPH_EP0].MXPLD = 0; +} + +/* reconstruct ep_addr from particular USB Configuration Register */ +static uint8_t decode_ep_addr(USBD_EP_T *ep) +{ + uint8_t ep_addr = ep->CFG & USBD_CFG_EPNUM_Msk; + if ( USBD_CFG_EPMODE_IN == (ep->CFG & USBD_CFG_STATE_Msk) ) + ep_addr |= TUSB_DIR_IN_MASK; + return ep_addr; +} + +/* map 8-bit ep_addr into peripheral endpoint index (PERIPH_EP0...) */ +static USBD_EP_T *ep_entry(uint8_t ep_addr, bool add) +{ + USBD_EP_T *ep; + enum ep_enum ep_index; + + for (ep_index = PERIPH_EP0, ep = USBD->EP; ep_index < PERIPH_MAX_EP; ep_index++, ep++) + { + if (add) + { + /* take first peripheral endpoint that is unused */ + if (0 == (ep->CFG & USBD_CFG_STATE_Msk)) return ep; + } + else + { + /* find a peripheral endpoint that matches ep_addr */ + uint8_t candidate_ep_addr = decode_ep_addr(ep); + if (candidate_ep_addr == ep_addr) return ep; + } + } + + return NULL; +} + +/* perform an IN endpoint transfer; this is called by dcd_edpt_xfer() and the ISR */ +static void dcd_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep) +{ + uint16_t bytes_now = tu_min16(xfer->remaining_bytes, xfer->max_packet_size); + + memcpy((uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), xfer->data_ptr, bytes_now); + ep->MXPLD = bytes_now; +} + +/* centralized location for USBD interrupt enable bit mask */ +static const uint32_t enabled_irqs = USBD_INTSTS_VBDETIF_Msk | USBD_INTSTS_BUSIF_Msk | USBD_INTSTS_SETUP_Msk | USBD_INTSTS_USBIF_Msk | USBD_INTSTS_SOFIF_Msk; + +/* + NUC121/NUC125/NUC126 TinyUSB API driver implementation +*/ + +void dcd_init(uint8_t rhport) +{ + (void) rhport; + +#ifdef SUPPORT_LPM + USBD->ATTR = 0x7D0 | USBD_LPMACK; +#else + USBD->ATTR = 0x7D0; +#endif + + usb_detach(); + + USBD->STBUFSEG = PERIPH_SETUP_BUF_BASE; + + for (enum ep_enum ep_index = PERIPH_EP0; ep_index < PERIPH_MAX_EP; ep_index++) + { + USBD->EP[ep_index].CFGP &= ~USBD_CFG_STATE_Msk; + } + + /* allocate the default EP0 endpoints */ + + USBD->EP[PERIPH_EP0].CFG = USBD_CFG_CSTALL_Msk | USBD_CFG_EPMODE_IN; + USBD->EP[PERIPH_EP0].BUFSEG = PERIPH_EP0_BUF_BASE; + xfer_table[PERIPH_EP0].max_packet_size = PERIPH_EP0_BUF_LEN; + + USBD->EP[PERIPH_EP1].CFG = USBD_CFG_CSTALL_Msk | USBD_CFG_EPMODE_OUT; + USBD->EP[PERIPH_EP1].BUFSEG = PERIPH_EP1_BUF_BASE; + xfer_table[PERIPH_EP1].max_packet_size = PERIPH_EP1_BUF_LEN; + + /* USB RAM beyond what we've allocated above is available to the user */ + bufseg_addr = PERIPH_EP2_BUF_BASE; + + usb_attach(); + + USBD->INTSTS = enabled_irqs; + USBD->INTEN = enabled_irqs; +} + +void dcd_int_enable(uint8_t rhport) +{ + (void) rhport; + NVIC_EnableIRQ(USBD_IRQn); +} + +void dcd_int_disable(uint8_t rhport) +{ + (void) rhport; + NVIC_DisableIRQ(USBD_IRQn); +} + +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) +{ + (void) rhport; + usb_control_send_zlp(); /* SET_ADDRESS is the one exception where TinyUSB doesn't use dcd_edpt_xfer() to generate a ZLP */ + assigned_address = dev_addr; +} + +void dcd_set_config(uint8_t rhport, uint8_t config_num) +{ + (void) rhport; + (void) config_num; +} + +void dcd_remote_wakeup(uint8_t rhport) +{ + (void) rhport; + USBD->ATTR = USBD_ATTR_RWAKEUP_Msk; +} + +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) +{ + (void) rhport; + + USBD_EP_T *ep = ep_entry(p_endpoint_desc->bEndpointAddress, true); + TU_ASSERT(ep); + + /* mine the data for the information we need */ + int const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); + int const size = p_endpoint_desc->wMaxPacketSize.size; + tusb_xfer_type_t const type = p_endpoint_desc->bmAttributes.xfer; + struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP]; + + /* allocate buffer from USB RAM */ + ep->BUFSEG = bufseg_addr; + bufseg_addr += size; + TU_ASSERT(bufseg_addr <= USBD_BUF_SIZE); + + /* construct USB Configuration Register value and then write it */ + uint32_t cfg = tu_edpt_number(p_endpoint_desc->bEndpointAddress); + cfg |= (TUSB_DIR_IN == dir) ? USBD_CFG_EPMODE_IN : USBD_CFG_EPMODE_OUT; + if (TUSB_XFER_ISOCHRONOUS == type) + cfg |= USBD_CFG_TYPE_ISO; + ep->CFG = cfg; + + /* make a note of the endpoint size */ + xfer->max_packet_size = size; + + return true; +} + +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) +{ + (void) rhport; + + /* mine the data for the information we need */ + tusb_dir_t dir = tu_edpt_dir(ep_addr); + USBD_EP_T *ep = ep_entry(ep_addr, false); + struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP]; + + /* store away the information we'll needing now and later */ + xfer->data_ptr = buffer; + xfer->remaining_bytes = total_bytes; + xfer->total_bytes = total_bytes; + + /* for the first of one or more EP0_IN packets in a message, the first must be DATA1 */ + if ( (0x80 == ep_addr) && !active_ep0_xfer ) ep->CFG |= USBD_CFG_DSQSYNC_Msk; + + if (TUSB_DIR_IN == dir) + dcd_in_xfer(xfer, ep); + else + ep->MXPLD = xfer->max_packet_size; + + return true; +} + +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + USBD_EP_T *ep = ep_entry(ep_addr, false); + ep->CFGP |= USBD_CFGP_SSTALL_Msk; +} + +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + USBD_EP_T *ep = ep_entry(ep_addr, false); + ep->CFG |= USBD_CFG_CSTALL_Msk; +} + +void USBD_IRQHandler(void) +{ + uint32_t status = USBD->INTSTS; +#ifdef SUPPORT_LPM + uint32_t state = USBD->ATTR & 0x300f; +#else + uint32_t state = USBD->ATTR & 0xf; +#endif + + if(status & USBD_INTSTS_VBDETIF_Msk) + { + if(USBD->VBUSDET & USBD_VBUSDET_VBUSDET_Msk) + { + /* USB connect */ + USBD->ATTR |= USBD_ATTR_USBEN_Msk | USBD_ATTR_PHYEN_Msk; + } + else + { + /* USB disconnect */ + USBD->ATTR &= ~USBD_ATTR_USBEN_Msk; + } + } + + if(status & USBD_INTSTS_BUSIF_Msk) + { + if(state & USBD_ATTR_USBRST_Msk) + { + /* USB bus reset */ + USBD->ATTR |= USBD_ATTR_USBEN_Msk | USBD_ATTR_PHYEN_Msk; + + /* Reset all endpoints to DATA0 */ + for(enum ep_enum ep_index = PERIPH_EP0; ep_index < PERIPH_MAX_EP; ep_index++) + USBD->EP[ep_index].CFG &= ~USBD_CFG_DSQSYNC_Msk; + + /* Reset USB device address */ + USBD->FADDR = 0; + + /* reset EP0_IN flag */ + active_ep0_xfer = false; + + dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true); + } + + if(state & USBD_ATTR_SUSPEND_Msk) + { + /* Enable USB but disable PHY */ + USBD->ATTR &= ~USBD_ATTR_PHYEN_Msk; + dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true); + } + + if(state & USBD_ATTR_RESUME_Msk) + { + /* Enable USB and enable PHY */ + USBD->ATTR |= USBD_ATTR_USBEN_Msk | USBD_ATTR_PHYEN_Msk; + dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); + } + } + + if(status & USBD_INTSTS_SETUP_Msk) + { + /* clear the data ready flag of control endpoints */ + USBD->EP[PERIPH_EP0].CFGP |= USBD_CFGP_CLRRDY_Msk; + USBD->EP[PERIPH_EP1].CFGP |= USBD_CFGP_CLRRDY_Msk; + + /* get SETUP packet from USB buffer */ + dcd_event_setup_received(0, (uint8_t *)USBD_BUF_BASE, true); + } + + if(status & USBD_INTSTS_USBIF_Msk) + { + if (status & USBD_INTSTS_EPEVT0_Msk) /* PERIPH_EP0 (EP0_IN) event: this is treated separately from the rest */ + { + /* given ACK from host has happened, we can now set the address (if not already done) */ + if((USBD->FADDR != assigned_address) && (USBD->FADDR == 0)) USBD->FADDR = assigned_address; + + uint16_t const available_bytes = USBD->EP[PERIPH_EP0].MXPLD; + + active_ep0_xfer = (available_bytes == xfer_table[PERIPH_EP0].max_packet_size); + + dcd_event_xfer_complete(0, 0x80, available_bytes, XFER_RESULT_SUCCESS, true); + } + + /* service PERIPH_EP1 through PERIPH_EP7 */ + enum ep_enum ep_index; + uint32_t mask; + struct xfer_ctl_t *xfer; + USBD_EP_T *ep; + for (ep_index = PERIPH_EP1, mask = USBD_INTSTS_EPEVT1_Msk, xfer = &xfer_table[PERIPH_EP1], ep = &USBD->EP[PERIPH_EP1]; ep_index <= PERIPH_EP7; ep_index++, mask <<= 1, xfer++, ep++) + { + if(status & mask) + { + USBD->INTSTS = mask; + + uint16_t const available_bytes = ep->MXPLD; + uint8_t const ep_addr = decode_ep_addr(ep); + bool const out_ep = !(ep_addr & TUSB_DIR_IN_MASK); + + if (out_ep) + { + /* copy the data from the PC to the previously provided buffer */ + memcpy(xfer->data_ptr, (uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), available_bytes); + xfer->remaining_bytes -= available_bytes; + xfer->data_ptr += available_bytes; + + /* when the transfer is finished, alert TinyUSB */ + if ( (0 == xfer->remaining_bytes) || (available_bytes < xfer->max_packet_size) ) + dcd_event_xfer_complete(0, ep_addr, available_bytes, XFER_RESULT_SUCCESS, true); + } + else if (xfer->remaining_bytes) + { + /* update the bookkeeping to reflect the data that has now been sent to the PC */ + xfer->remaining_bytes -= available_bytes; + xfer->data_ptr += available_bytes; + + /* if more data to send, send it; otherwise, alert TinyUSB that we've finished */ + if (xfer->remaining_bytes) + dcd_in_xfer(xfer, ep); + else + dcd_event_xfer_complete(0, ep_addr, xfer->total_bytes, XFER_RESULT_SUCCESS, true); + } + } + } + } + + if(status & USBD_INTSTS_SOFIF_Msk) + { + /* Start-Of-Frame event */ + dcd_event_bus_signal(0, DCD_EVENT_SOF, true); + } + + /* acknowledge all interrupts */ + USBD->INTSTS = status & enabled_irqs; +} + +void dcd_isr(uint8_t rhport) +{ + (void) rhport; + USBD_IRQHandler(); +} + +#endif diff --git a/src/tusb_option.h b/src/tusb_option.h index ae8c02eeb..12b499c0e 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -74,6 +74,9 @@ #define OPT_MCU_MIMXRT10XX 700 ///< NXP iMX RT10xx +#define OPT_MCU_NUC121 800 +#define OPT_MCU_NUC126 801 + /** @} */ /** \defgroup group_supported_os Supported RTOS -- cgit v1.3.1 From 5c5f876461e81ff7c2937978e4e6855e0208ebfb Mon Sep 17 00:00:00 2001 From: Peter Lawrence <12226419+majbthrd@users.noreply.github.com> Date: Sat, 4 Jan 2020 13:36:26 -0600 Subject: dcd_nuc121: allow user ZLP transfers to get a callback --- src/portable/nuvoton/nuc121/dcd_nuc121.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/portable') diff --git a/src/portable/nuvoton/nuc121/dcd_nuc121.c b/src/portable/nuvoton/nuc121/dcd_nuc121.c index 57db5231d..afe2cf001 100644 --- a/src/portable/nuvoton/nuc121/dcd_nuc121.c +++ b/src/portable/nuvoton/nuc121/dcd_nuc121.c @@ -399,7 +399,7 @@ void USBD_IRQHandler(void) if ( (0 == xfer->remaining_bytes) || (available_bytes < xfer->max_packet_size) ) dcd_event_xfer_complete(0, ep_addr, available_bytes, XFER_RESULT_SUCCESS, true); } - else if (xfer->remaining_bytes) + else { /* update the bookkeeping to reflect the data that has now been sent to the PC */ xfer->remaining_bytes -= available_bytes; -- cgit v1.3.1 From c1781e5e7c47e716a558f388beb96454d4bf8627 Mon Sep 17 00:00:00 2001 From: Mateusz Holenko Date: Tue, 14 Jan 2020 13:37:45 +0100 Subject: dcd_eptri: Fix rx_buffer/tx_buffer volatile annotation This makes `rx_buffer` and `tx_buffer` *pointers* volatile in order to avoid caching them in a register. The original notation meant "a pointer to a volatile value" (equivalent of `volatile uint8_t *`). This resulted in `while(rx_buffer[ep_num] != NULL) ;` loop to get stuck forever, even though the IRQ handler set the `rx_buffer[ep_num] = NULL`. --- src/portable/valentyusb/eptri/dcd_eptri.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/portable') diff --git a/src/portable/valentyusb/eptri/dcd_eptri.c b/src/portable/valentyusb/eptri/dcd_eptri.c index d7e1b8a9a..0d388c376 100644 --- a/src/portable/valentyusb/eptri/dcd_eptri.c +++ b/src/portable/valentyusb/eptri/dcd_eptri.c @@ -75,13 +75,13 @@ uint8_t queue_log_offset; #define EP_SIZE 64 uint16_t volatile rx_buffer_offset[16]; -uint8_t volatile * rx_buffer[16]; +uint8_t* volatile rx_buffer[16]; uint16_t volatile rx_buffer_max[16]; volatile uint8_t tx_ep; volatile bool tx_active; volatile uint16_t tx_buffer_offset[16]; -uint8_t volatile * tx_buffer[16]; +uint8_t* volatile tx_buffer[16]; volatile uint16_t tx_buffer_max[16]; volatile uint8_t reset_count; -- cgit v1.3.1 From f2a5335417004cdc9398e3beac1f4861a5e29e0a Mon Sep 17 00:00:00 2001 From: Peter Lawrence <12226419+majbthrd@users.noreply.github.com> Date: Tue, 14 Jan 2020 22:07:38 -0600 Subject: nuc121: fix handling of OUT transfers larger than max packet size --- src/portable/nuvoton/nuc121/dcd_nuc121.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/portable') diff --git a/src/portable/nuvoton/nuc121/dcd_nuc121.c b/src/portable/nuvoton/nuc121/dcd_nuc121.c index afe2cf001..892a20aa4 100644 --- a/src/portable/nuvoton/nuc121/dcd_nuc121.c +++ b/src/portable/nuvoton/nuc121/dcd_nuc121.c @@ -395,9 +395,11 @@ void USBD_IRQHandler(void) xfer->remaining_bytes -= available_bytes; xfer->data_ptr += available_bytes; - /* when the transfer is finished, alert TinyUSB */ + /* when the transfer is finished, alert TinyUSB; otherwise, accept more data */ if ( (0 == xfer->remaining_bytes) || (available_bytes < xfer->max_packet_size) ) dcd_event_xfer_complete(0, ep_addr, available_bytes, XFER_RESULT_SUCCESS, true); + else if (xfer->remaining_bytes) + ep->MXPLD = xfer->max_packet_size; } else { -- cgit v1.3.1 From 7f2db321bf8e1f97d4759cf2efef0f684a461916 Mon Sep 17 00:00:00 2001 From: Peter Lawrence <12226419+majbthrd@users.noreply.github.com> Date: Wed, 15 Jan 2020 10:23:23 -0600 Subject: nuc121: better fix for handling large OUT transfers --- src/portable/nuvoton/nuc121/dcd_nuc121.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/portable') diff --git a/src/portable/nuvoton/nuc121/dcd_nuc121.c b/src/portable/nuvoton/nuc121/dcd_nuc121.c index 892a20aa4..237a5fd9a 100644 --- a/src/portable/nuvoton/nuc121/dcd_nuc121.c +++ b/src/portable/nuvoton/nuc121/dcd_nuc121.c @@ -397,7 +397,7 @@ void USBD_IRQHandler(void) /* when the transfer is finished, alert TinyUSB; otherwise, accept more data */ if ( (0 == xfer->remaining_bytes) || (available_bytes < xfer->max_packet_size) ) - dcd_event_xfer_complete(0, ep_addr, available_bytes, XFER_RESULT_SUCCESS, true); + dcd_event_xfer_complete(0, ep_addr, xfer->total_bytes, XFER_RESULT_SUCCESS, true); else if (xfer->remaining_bytes) ep->MXPLD = xfer->max_packet_size; } -- cgit v1.3.1 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/class/audio/audio.h | 200 +-- src/class/cdc/cdc.h | 810 ++++++------ src/class/cdc/cdc_device.c | 830 ++++++------- src/class/cdc/cdc_device.h | 426 +++---- src/class/cdc/cdc_host.c | 450 +++---- src/class/cdc/cdc_host.h | 246 ++-- src/class/hid/hid.h | 1746 +++++++++++++------------- src/class/hid/hid_device.h | 630 +++++----- src/class/hid/hid_host.c | 578 ++++----- src/class/hid/hid_host.h | 430 +++---- src/class/midi/midi.h | 334 ++--- src/class/midi/midi_device.c | 722 +++++------ src/class/midi/midi_device.h | 276 ++-- src/class/msc/msc.h | 784 ++++++------ src/class/msc/msc_device.c | 1400 ++++++++++----------- src/class/msc/msc_device.h | 330 ++--- src/class/msc/msc_host.c | 826 ++++++------ src/class/msc/msc_host.h | 412 +++--- src/common/tusb_common.h | 550 ++++---- src/common/tusb_compiler.h | 280 ++--- src/common/tusb_error.h | 148 +-- src/common/tusb_fifo.c | 576 ++++----- src/common/tusb_fifo.h | 262 ++-- src/common/tusb_timeout.h | 160 +-- src/common/tusb_types.h | 962 +++++++------- src/device/dcd.h | 302 ++--- src/device/usbd.c | 2002 +++++++++++++++--------------- src/device/usbd.h | 670 +++++----- src/host/ehci/ehci.c | 1712 ++++++++++++------------- src/host/hcd.h | 294 ++--- src/host/hub.c | 500 ++++---- src/host/hub.h | 388 +++--- src/host/ohci/ohci.c | 1302 +++++++++---------- src/host/ohci/ohci.h | 582 ++++----- src/host/usbh.c | 1352 ++++++++++---------- src/osal/osal.h | 196 +-- src/osal/osal_none.h | 420 +++---- src/portable/nuvoton/nuc121/dcd_nuc121.c | 872 ++++++------- src/tusb.c | 296 ++--- src/tusb.h | 236 ++-- src/tusb_option.h | 480 +++---- 41 files changed, 12986 insertions(+), 12986 deletions(-) (limited to 'src/portable') diff --git a/src/class/audio/audio.h b/src/class/audio/audio.h index 83b8906d2..5bec14d88 100644 --- a/src/class/audio/audio.h +++ b/src/class/audio/audio.h @@ -1,100 +1,100 @@ -/* - * 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_class - * \defgroup ClassDriver_Audio Audio - * Currently only MIDI subclass is supported - * @{ */ - -#ifndef _TUSB_AUDIO_H__ -#define _TUSB_AUDIO_H__ - -#include "common/tusb_common.h" - -#ifdef __cplusplus - extern "C" { -#endif - -/// Audio Interface Subclass Codes -typedef enum -{ - AUDIO_SUBCLASS_CONTROL = 0x01 , ///< Audio Control - AUDIO_SUBCLASS_STREAMING , ///< Audio Streaming - AUDIO_SUBCLASS_MIDI_STREAMING , ///< MIDI Streaming -} audio_subclass_type_t; - -/// Audio Protocol Codes -typedef enum -{ - AUDIO_PROTOCOL_V1 = 0x00, ///< Version 1.0 - AUDIO_PROTOCOL_V2 = 0x20, ///< Version 2.0 - AUDIO_PROTOCOL_V3 = 0x30, ///< Version 3.0 -} audio_protocol_type_t; - -/// Audio Function Category Codes -typedef enum -{ - AUDIO_FUNC_DESKTOP_SPEAKER = 0x01, - AUDIO_FUNC_HOME_THEATER = 0x02, - AUDIO_FUNC_MICROPHONE = 0x03, - AUDIO_FUNC_HEADSET = 0x04, - AUDIO_FUNC_TELEPHONE = 0x05, - AUDIO_FUNC_CONVERTER = 0x06, - AUDIO_FUNC_SOUND_RECODER = 0x07, - AUDIO_FUNC_IO_BOX = 0x08, - AUDIO_FUNC_MUSICAL_INSTRUMENT = 0x09, - AUDIO_FUNC_PRO_AUDIO = 0x0A, - AUDIO_FUNC_AUDIO_VIDEO = 0x0B, - AUDIO_FUNC_CONTROL_PANEL = 0x0C -} audio_function_t; - -/// Audio Class-Specific AC Interface Descriptor Subtypes -typedef enum -{ - AUDIO_CS_INTERFACE_HEADER = 0x01, - AUDIO_CS_INTERFACE_INPUT_TERMINAL = 0x02, - AUDIO_CS_INTERFACE_OUTPUT_TERMINAL = 0x03, - AUDIO_CS_INTERFACE_MIXER_UNIT = 0x04, - AUDIO_CS_INTERFACE_SELECTOR_UNIT = 0x05, - AUDIO_CS_INTERFACE_FEATURE_UNIT = 0x06, - AUDIO_CS_INTERFACE_EFFECT_UNIT = 0x07, - AUDIO_CS_INTERFACE_PROCESSING_UNIT = 0x08, - AUDIO_CS_INTERFACE_EXTENSION_UNIT = 0x09, - AUDIO_CS_INTERFACE_CLOCK_SOURCE = 0x0A, - AUDIO_CS_INTERFACE_CLOCK_SELECTOR = 0x0B, - AUDIO_CS_INTERFACE_CLOCK_MULTIPLIER = 0x0C, - AUDIO_CS_INTERFACE_SAMPLE_RATE_CONVERTER = 0x0D, -} audio_cs_interface_subtype_t; - -/** @} */ - -#ifdef __cplusplus - } -#endif - -#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. + */ + +/** \ingroup group_class + * \defgroup ClassDriver_Audio Audio + * Currently only MIDI subclass is supported + * @{ */ + +#ifndef _TUSB_AUDIO_H__ +#define _TUSB_AUDIO_H__ + +#include "common/tusb_common.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/// Audio Interface Subclass Codes +typedef enum +{ + AUDIO_SUBCLASS_CONTROL = 0x01 , ///< Audio Control + AUDIO_SUBCLASS_STREAMING , ///< Audio Streaming + AUDIO_SUBCLASS_MIDI_STREAMING , ///< MIDI Streaming +} audio_subclass_type_t; + +/// Audio Protocol Codes +typedef enum +{ + AUDIO_PROTOCOL_V1 = 0x00, ///< Version 1.0 + AUDIO_PROTOCOL_V2 = 0x20, ///< Version 2.0 + AUDIO_PROTOCOL_V3 = 0x30, ///< Version 3.0 +} audio_protocol_type_t; + +/// Audio Function Category Codes +typedef enum +{ + AUDIO_FUNC_DESKTOP_SPEAKER = 0x01, + AUDIO_FUNC_HOME_THEATER = 0x02, + AUDIO_FUNC_MICROPHONE = 0x03, + AUDIO_FUNC_HEADSET = 0x04, + AUDIO_FUNC_TELEPHONE = 0x05, + AUDIO_FUNC_CONVERTER = 0x06, + AUDIO_FUNC_SOUND_RECODER = 0x07, + AUDIO_FUNC_IO_BOX = 0x08, + AUDIO_FUNC_MUSICAL_INSTRUMENT = 0x09, + AUDIO_FUNC_PRO_AUDIO = 0x0A, + AUDIO_FUNC_AUDIO_VIDEO = 0x0B, + AUDIO_FUNC_CONTROL_PANEL = 0x0C +} audio_function_t; + +/// Audio Class-Specific AC Interface Descriptor Subtypes +typedef enum +{ + AUDIO_CS_INTERFACE_HEADER = 0x01, + AUDIO_CS_INTERFACE_INPUT_TERMINAL = 0x02, + AUDIO_CS_INTERFACE_OUTPUT_TERMINAL = 0x03, + AUDIO_CS_INTERFACE_MIXER_UNIT = 0x04, + AUDIO_CS_INTERFACE_SELECTOR_UNIT = 0x05, + AUDIO_CS_INTERFACE_FEATURE_UNIT = 0x06, + AUDIO_CS_INTERFACE_EFFECT_UNIT = 0x07, + AUDIO_CS_INTERFACE_PROCESSING_UNIT = 0x08, + AUDIO_CS_INTERFACE_EXTENSION_UNIT = 0x09, + AUDIO_CS_INTERFACE_CLOCK_SOURCE = 0x0A, + AUDIO_CS_INTERFACE_CLOCK_SELECTOR = 0x0B, + AUDIO_CS_INTERFACE_CLOCK_MULTIPLIER = 0x0C, + AUDIO_CS_INTERFACE_SAMPLE_RATE_CONVERTER = 0x0D, +} audio_cs_interface_subtype_t; + +/** @} */ + +#ifdef __cplusplus + } +#endif + +#endif + +/** @} */ diff --git a/src/class/cdc/cdc.h b/src/class/cdc/cdc.h index 9cc8b82e3..f0488acbb 100644 --- a/src/class/cdc/cdc.h +++ b/src/class/cdc/cdc.h @@ -1,405 +1,405 @@ -/* - * 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_class - * \defgroup ClassDriver_CDC Communication Device Class (CDC) - * Currently only Abstract Control Model subclass is supported - * @{ */ - -#ifndef _TUSB_CDC_H__ -#define _TUSB_CDC_H__ - -#include "common/tusb_common.h" - -#ifdef __cplusplus - extern "C" { -#endif - -/** \defgroup ClassDriver_CDC_Common Common Definitions - * @{ */ - -// TODO remove -/// CDC Pipe ID, used to indicate which pipe the API is addressing to (Notification, Out, In) -typedef enum -{ - CDC_PIPE_NOTIFICATION , ///< Notification pipe - CDC_PIPE_DATA_IN , ///< Data in pipe - CDC_PIPE_DATA_OUT , ///< Data out pipe - CDC_PIPE_ERROR , ///< Invalid Pipe ID -}cdc_pipeid_t; - -//--------------------------------------------------------------------+ -// CDC Communication Interface Class -//--------------------------------------------------------------------+ - -/// Communication Interface Subclass Codes -typedef enum -{ - CDC_COMM_SUBCLASS_DIRECT_LINE_CONTROL_MODEL = 0x01 , ///< Direct Line Control Model [USBPSTN1.2] - CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL , ///< Abstract Control Model [USBPSTN1.2] - CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL , ///< Telephone Control Model [USBPSTN1.2] - CDC_COMM_SUBCLASS_MULTICHANNEL_CONTROL_MODEL , ///< Multi-Channel Control Model [USBISDN1.2] - CDC_COMM_SUBCLASS_CAPI_CONTROL_MODEL , ///< CAPI Control Model [USBISDN1.2] - CDC_COMM_SUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL , ///< Ethernet Networking Control Model [USBECM1.2] - CDC_COMM_SUBCLASS_ATM_NETWORKING_CONTROL_MODEL , ///< ATM Networking Control Model [USBATM1.2] - CDC_COMM_SUBCLASS_WIRELESS_HANDSET_CONTROL_MODEL , ///< Wireless Handset Control Model [USBWMC1.1] - CDC_COMM_SUBCLASS_DEVICE_MANAGEMENT , ///< Device Management [USBWMC1.1] - CDC_COMM_SUBCLASS_MOBILE_DIRECT_LINE_MODEL , ///< Mobile Direct Line Model [USBWMC1.1] - CDC_COMM_SUBCLASS_OBEX , ///< OBEX [USBWMC1.1] - CDC_COMM_SUBCLASS_ETHERNET_EMULATION_MODEL ///< Ethernet Emulation Model [USBEEM1.0] -} cdc_comm_sublcass_type_t; - -/// Communication Interface Protocol Codes -typedef enum -{ - CDC_COMM_PROTOCOL_NONE = 0x00 , ///< No specific protocol - CDC_COMM_PROTOCOL_ATCOMMAND , ///< AT Commands: V.250 etc - CDC_COMM_PROTOCOL_ATCOMMAND_PCCA_101 , ///< AT Commands defined by PCCA-101 - CDC_COMM_PROTOCOL_ATCOMMAND_PCCA_101_AND_ANNEXO , ///< AT Commands defined by PCCA-101 & Annex O - CDC_COMM_PROTOCOL_ATCOMMAND_GSM_707 , ///< AT Commands defined by GSM 07.07 - CDC_COMM_PROTOCOL_ATCOMMAND_3GPP_27007 , ///< AT Commands defined by 3GPP 27.007 - CDC_COMM_PROTOCOL_ATCOMMAND_CDMA , ///< AT Commands defined by TIA for CDMA - CDC_COMM_PROTOCOL_ETHERNET_EMULATION_MODEL ///< Ethernet Emulation Model -} cdc_comm_protocol_type_t; - -//------------- SubType Descriptor in COMM Functional Descriptor -------------// -/// Communication Interface SubType Descriptor -typedef enum -{ - CDC_FUNC_DESC_HEADER = 0x00 , ///< Header Functional Descriptor, which marks the beginning of the concatenated set of functional descriptors for the interface. - CDC_FUNC_DESC_CALL_MANAGEMENT = 0x01 , ///< Call Management Functional Descriptor. - CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT = 0x02 , ///< Abstract Control Management Functional Descriptor. - CDC_FUNC_DESC_DIRECT_LINE_MANAGEMENT = 0x03 , ///< Direct Line Management Functional Descriptor. - CDC_FUNC_DESC_TELEPHONE_RINGER = 0x04 , ///< Telephone Ringer Functional Descriptor. - CDC_FUNC_DESC_TELEPHONE_CALL_AND_LINE_STATE_REPORTING_CAPACITY = 0x05 , ///< Telephone Call and Line State Reporting Capabilities Functional Descriptor. - CDC_FUNC_DESC_UNION = 0x06 , ///< Union Functional Descriptor - CDC_FUNC_DESC_COUNTRY_SELECTION = 0x07 , ///< Country Selection Functional Descriptor - CDC_FUNC_DESC_TELEPHONE_OPERATIONAL_MODES = 0x08 , ///< Telephone Operational ModesFunctional Descriptor - CDC_FUNC_DESC_USB_TERMINAL = 0x09 , ///< USB Terminal Functional Descriptor - CDC_FUNC_DESC_NETWORK_CHANNEL_TERMINAL = 0x0A , ///< Network Channel Terminal Descriptor - CDC_FUNC_DESC_PROTOCOL_UNIT = 0x0B , ///< Protocol Unit Functional Descriptor - CDC_FUNC_DESC_EXTENSION_UNIT = 0x0C , ///< Extension Unit Functional Descriptor - CDC_FUNC_DESC_MULTICHANEL_MANAGEMENT = 0x0D , ///< Multi-Channel Management Functional Descriptor - CDC_FUNC_DESC_CAPI_CONTROL_MANAGEMENT = 0x0E , ///< CAPI Control Management Functional Descriptor - CDC_FUNC_DESC_ETHERNET_NETWORKING = 0x0F , ///< Ethernet Networking Functional Descriptor - CDC_FUNC_DESC_ATM_NETWORKING = 0x10 , ///< ATM Networking Functional Descriptor - CDC_FUNC_DESC_WIRELESS_HANDSET_CONTROL_MODEL = 0x11 , ///< Wireless Handset Control Model Functional Descriptor - CDC_FUNC_DESC_MOBILE_DIRECT_LINE_MODEL = 0x12 , ///< Mobile Direct Line Model Functional Descriptor - CDC_FUNC_DESC_MOBILE_DIRECT_LINE_MODEL_DETAIL = 0x13 , ///< MDLM Detail Functional Descriptor - CDC_FUNC_DESC_DEVICE_MANAGEMENT_MODEL = 0x14 , ///< Device Management Model Functional Descriptor - CDC_FUNC_DESC_OBEX = 0x15 , ///< OBEX Functional Descriptor - CDC_FUNC_DESC_COMMAND_SET = 0x16 , ///< Command Set Functional Descriptor - CDC_FUNC_DESC_COMMAND_SET_DETAIL = 0x17 , ///< Command Set Detail Functional Descriptor - CDC_FUNC_DESC_TELEPHONE_CONTROL_MODEL = 0x18 , ///< Telephone Control Model Functional Descriptor - CDC_FUNC_DESC_OBEX_SERVICE_IDENTIFIER = 0x19 ///< OBEX Service Identifier Functional Descriptor -}cdc_func_desc_type_t; - -//--------------------------------------------------------------------+ -// CDC Data Interface Class -//--------------------------------------------------------------------+ - -// SUBCLASS code of Data Interface is not used and should/must be zero -/// Data Interface Protocol Codes -typedef enum{ - CDC_DATA_PROTOCOL_ISDN_BRI = 0x30, ///< Physical interface protocol for ISDN BRI - CDC_DATA_PROTOCOL_HDLC = 0x31, ///< HDLC - CDC_DATA_PROTOCOL_TRANSPARENT = 0x32, ///< Transparent - CDC_DATA_PROTOCOL_Q921_MANAGEMENT = 0x50, ///< Management protocol for Q.921 data link protocol - CDC_DATA_PROTOCOL_Q921_DATA_LINK = 0x51, ///< Data link protocol for Q.931 - CDC_DATA_PROTOCOL_Q921_TEI_MULTIPLEXOR = 0x52, ///< TEI-multiplexor for Q.921 data link protocol - CDC_DATA_PROTOCOL_V42BIS_DATA_COMPRESSION = 0x90, ///< Data compression procedures - CDC_DATA_PROTOCOL_EURO_ISDN = 0x91, ///< Euro-ISDN protocol control - CDC_DATA_PROTOCOL_V24_RATE_ADAPTION_TO_ISDN = 0x92, ///< V.24 rate adaptation to ISDN - CDC_DATA_PROTOCOL_CAPI_COMMAND = 0x93, ///< CAPI Commands - CDC_DATA_PROTOCOL_HOST_BASED_DRIVER = 0xFD, ///< Host based driver. Note: This protocol code should only be used in messages between host and device to identify the host driver portion of a protocol stack. - CDC_DATA_PROTOCOL_IN_PROTOCOL_UNIT_FUNCTIONAL_DESCRIPTOR = 0xFE ///< The protocol(s) are described using a ProtocolUnit Functional Descriptors on Communications Class Interface -}cdc_data_protocol_type_t; - -//--------------------------------------------------------------------+ -// Management Element Request (Control Endpoint) -//--------------------------------------------------------------------+ - -/// Communication Interface Management Element Request Codes -typedef enum -{ - CDC_REQUEST_SEND_ENCAPSULATED_COMMAND = 0x00, ///< is used to issue a command in the format of the supported control protocol of the Communications Class interface - CDC_REQUEST_GET_ENCAPSULATED_RESPONSE = 0x01, ///< is used to request a response in the format of the supported control protocol of the Communications Class interface. - - CDC_REQUEST_SET_COMM_FEATURE = 0x02, - CDC_REQUEST_GET_COMM_FEATURE = 0x03, - CDC_REQUEST_CLEAR_COMM_FEATURE = 0x04, - - CDC_REQUEST_SET_AUX_LINE_STATE = 0x10, - CDC_REQUEST_SET_HOOK_STATE = 0x11, - CDC_REQUEST_PULSE_SETUP = 0x12, - CDC_REQUEST_SEND_PULSE = 0x13, - CDC_REQUEST_SET_PULSE_TIME = 0x14, - CDC_REQUEST_RING_AUX_JACK = 0x15, - - CDC_REQUEST_SET_LINE_CODING = 0x20, - CDC_REQUEST_GET_LINE_CODING = 0x21, - CDC_REQUEST_SET_CONTROL_LINE_STATE = 0x22, - CDC_REQUEST_SEND_BREAK = 0x23, - - CDC_REQUEST_SET_RINGER_PARMS = 0x30, - CDC_REQUEST_GET_RINGER_PARMS = 0x31, - CDC_REQUEST_SET_OPERATION_PARMS = 0x32, - CDC_REQUEST_GET_OPERATION_PARMS = 0x33, - CDC_REQUEST_SET_LINE_PARMS = 0x34, - CDC_REQUEST_GET_LINE_PARMS = 0x35, - CDC_REQUEST_DIAL_DIGITS = 0x36, - CDC_REQUEST_SET_UNIT_PARAMETER = 0x37, - CDC_REQUEST_GET_UNIT_PARAMETER = 0x38, - CDC_REQUEST_CLEAR_UNIT_PARAMETER = 0x39, - CDC_REQUEST_GET_PROFILE = 0x3A, - - CDC_REQUEST_SET_ETHERNET_MULTICAST_FILTERS = 0x40, - CDC_REQUEST_SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x41, - CDC_REQUEST_GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x42, - CDC_REQUEST_SET_ETHERNET_PACKET_FILTER = 0x43, - CDC_REQUEST_GET_ETHERNET_STATISTIC = 0x44, - - CDC_REQUEST_SET_ATM_DATA_FORMAT = 0x50, - CDC_REQUEST_GET_ATM_DEVICE_STATISTICS = 0x51, - CDC_REQUEST_SET_ATM_DEFAULT_VC = 0x52, - CDC_REQUEST_GET_ATM_VC_STATISTICS = 0x53, - - CDC_REQUEST_MDLM_SEMANTIC_MODEL = 0x60, -}cdc_management_request_t; - -//--------------------------------------------------------------------+ -// Management Elemenent Notification (Notification Endpoint) -//--------------------------------------------------------------------+ - -/// Communication Interface Management Element Notification Codes -typedef enum -{ - NETWORK_CONNECTION = 0x00, ///< This notification allows the device to notify the host about network connection status. - RESPONSE_AVAILABLE = 0x01, ///< This notification allows the device to notify the hostthat a response is available. This response can be retrieved with a subsequent \ref CDC_REQUEST_GET_ENCAPSULATED_RESPONSE request. - - AUX_JACK_HOOK_STATE = 0x08, - RING_DETECT = 0x09, - - SERIAL_STATE = 0x20, - - CALL_STATE_CHANGE = 0x28, - LINE_STATE_CHANGE = 0x29, - CONNECTION_SPEED_CHANGE = 0x2A, ///< This notification allows the device to inform the host-networking driver that a change in either the upstream or the downstream bit rate of the connection has occurred - MDLM_SEMANTIC_MODEL_NOTIFICATION = 0x40, -}cdc_notification_request_t; - -//--------------------------------------------------------------------+ -// Class Specific Functional Descriptor (Communication Interface) -//--------------------------------------------------------------------+ - -/// Header Functional Descriptor (Communication Interface) -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength ; ///< Size of this descriptor in bytes. - uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific - uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUNC_DESC_ - uint16_t bcdCDC ; ///< CDC release number in Binary-Coded Decimal -}cdc_desc_func_header_t; - -/// Union Functional Descriptor (Communication Interface) -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength ; ///< Size of this descriptor in bytes. - uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific - uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ - uint8_t bControlInterface ; ///< Interface number of Communication Interface - uint8_t bSubordinateInterface ; ///< Array of Interface number of Data Interface -}cdc_desc_func_union_t; - -#define cdc_desc_func_union_n_t(no_slave)\ - struct TU_ATTR_PACKED { \ - uint8_t bLength ;\ - uint8_t bDescriptorType ;\ - uint8_t bDescriptorSubType ;\ - uint8_t bControlInterface ;\ - uint8_t bSubordinateInterface[no_slave] ;\ -} - -/// Country Selection Functional Descriptor (Communication Interface) -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength ; ///< Size of this descriptor in bytes. - uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific - uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ - uint8_t iCountryCodeRelDate ; ///< Index of a string giving the release date for the implemented ISO 3166 Country Codes. - uint16_t wCountryCode ; ///< Country code in the format as defined in [ISO3166], release date as specified inoffset 3 for the first supported country. -}cdc_desc_func_country_selection_t; - -#define cdc_desc_func_country_selection_n_t(no_country) \ - struct TU_ATTR_PACKED {\ - uint8_t bLength ;\ - uint8_t bDescriptorType ;\ - uint8_t bDescriptorSubType ;\ - uint8_t iCountryCodeRelDate ;\ - uint16_t wCountryCode[no_country] ;\ -} - -//--------------------------------------------------------------------+ -// PUBLIC SWITCHED TELEPHONE NETWORK (PSTN) SUBCLASS -//--------------------------------------------------------------------+ - -/// \brief Call Management Functional Descriptor -/// \details This functional descriptor describes the processing of calls for the Communications Class interface. -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength ; ///< Size of this descriptor in bytes. - uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific - uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ - - struct { - uint8_t handle_call : 1; ///< 0 - Device sends/receives call management information only over the Communications Class interface. 1 - Device can send/receive call management information over a Data Class interface. - uint8_t send_recv_call : 1; ///< 0 - Device does not handle call management itself. 1 - Device handles call management itself. - uint8_t : 0; - } bmCapabilities; - - uint8_t bDataInterface; -}cdc_desc_func_call_management_t; - - -typedef struct TU_ATTR_PACKED -{ - uint8_t support_comm_request : 1; ///< Device supports the request combination of Set_Comm_Feature, Clear_Comm_Feature, and Get_Comm_Feature. - uint8_t support_line_request : 1; ///< Device supports the request combination of Set_Line_Coding, Set_Control_Line_State, Get_Line_Coding, and the notification Serial_State. - uint8_t support_send_break : 1; ///< Device supports the request Send_Break - uint8_t support_notification_network_connection : 1; ///< Device supports the notification Network_Connection. - uint8_t : 0; -}cdc_acm_capability_t; - -TU_VERIFY_STATIC(sizeof(cdc_acm_capability_t) == 1, "mostly problem with compiler"); - -/// \brief Abstract Control Management Functional Descriptor -/// \details This functional descriptor describes the commands supported by by the Communications Class interface with SubClass code of \ref CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength ; ///< Size of this descriptor in bytes. - uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific - uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ - cdc_acm_capability_t bmCapabilities ; -}cdc_desc_func_acm_t; - -/// \brief Direct Line Management Functional Descriptor -/// \details This functional descriptor describes the commands supported by the Communications Class interface with SubClass code of \ref CDC_FUNC_DESC_DIRECT_LINE_MANAGEMENT -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength ; ///< Size of this descriptor in bytes. - uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific - uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ - struct { - uint8_t require_pulse_setup : 1; ///< Device requires extra Pulse_Setup request during pulse dialing sequence to disengage holding circuit. - uint8_t support_aux_request : 1; ///< Device supports the request combination of Set_Aux_Line_State, Ring_Aux_Jack, and notification Aux_Jack_Hook_State. - uint8_t support_pulse_request : 1; ///< Device supports the request combination of Pulse_Setup, Send_Pulse, and Set_Pulse_Time. - uint8_t : 0; - } bmCapabilities; -}cdc_desc_func_direct_line_management_t; - -/// \brief Telephone Ringer Functional Descriptor -/// \details The Telephone Ringer functional descriptor describes the ringer capabilities supported by the Communications Class interface, -/// with the SubClass code of \ref CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength ; ///< Size of this descriptor in bytes. - uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific - uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ - uint8_t bRingerVolSteps ; - uint8_t bNumRingerPatterns ; -}cdc_desc_func_telephone_ringer_t; - -/// \brief Telephone Operational Modes Functional Descriptor -/// \details The Telephone Operational Modes functional descriptor describes the operational modes supported by -/// the Communications Class interface, with the SubClass code of \ref CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength ; ///< Size of this descriptor in bytes. - uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific - uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ - struct { - uint8_t simple_mode : 1; - uint8_t standalone_mode : 1; - uint8_t computer_centric_mode : 1; - uint8_t : 0; - } bmCapabilities; -}cdc_desc_func_telephone_operational_modes_t; - -/// \brief Telephone Call and Line State Reporting Capabilities Descriptor -/// \details The Telephone Call and Line State Reporting Capabilities functional descriptor describes the abilities of a -/// telephone device to report optional call and line states. -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength ; ///< Size of this descriptor in bytes. - uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific - uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ - struct { - uint32_t interrupted_dialtone : 1; ///< 0 : Reports only dialtone (does not differentiate between normal and interrupted dialtone). 1 : Reports interrupted dialtone in addition to normal dialtone - uint32_t ringback_busy_fastbusy : 1; ///< 0 : Reports only dialing state. 1 : Reports ringback, busy, and fast busy states. - uint32_t caller_id : 1; ///< 0 : Does not report caller ID. 1 : Reports caller ID information. - uint32_t incoming_distinctive : 1; ///< 0 : Reports only incoming ringing. 1 : Reports incoming distinctive ringing patterns. - uint32_t dual_tone_multi_freq : 1; ///< 0 : Cannot report dual tone multi-frequency (DTMF) digits input remotely over the telephone line. 1 : Can report DTMF digits input remotely over the telephone line. - uint32_t line_state_change : 1; ///< 0 : Does not support line state change notification. 1 : Does support line state change notification - uint32_t : 0; - } bmCapabilities; -}cdc_desc_func_telephone_call_state_reporting_capabilities_t; - -static inline uint8_t cdc_functional_desc_typeof(uint8_t const * p_desc) -{ - return p_desc[2]; -} - -//--------------------------------------------------------------------+ -// Requests -//--------------------------------------------------------------------+ -typedef struct TU_ATTR_PACKED -{ - uint32_t bit_rate; - uint8_t stop_bits; ///< 0: 1 stop bit - 1: 1.5 stop bits - 2: 2 stop bits - uint8_t parity; ///< 0: None - 1: Odd - 2: Even - 3: Mark - 4: Space - uint8_t data_bits; ///< can be 5, 6, 7, 8 or 16 -} cdc_line_coding_t; - -TU_VERIFY_STATIC(sizeof(cdc_line_coding_t) == 7, "size is not correct"); - -typedef struct TU_ATTR_PACKED -{ - uint16_t dte_is_present : 1; ///< Indicates to DCE if DTE is presentor not. This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR. - uint16_t half_duplex_carrier_control : 1; - uint16_t : 14; -} cdc_line_control_state_t; - -TU_VERIFY_STATIC(sizeof(cdc_line_control_state_t) == 2, "size is not correct"); - -/** @} */ - -#ifdef __cplusplus - } -#endif - -#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. + */ + +/** \ingroup group_class + * \defgroup ClassDriver_CDC Communication Device Class (CDC) + * Currently only Abstract Control Model subclass is supported + * @{ */ + +#ifndef _TUSB_CDC_H__ +#define _TUSB_CDC_H__ + +#include "common/tusb_common.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/** \defgroup ClassDriver_CDC_Common Common Definitions + * @{ */ + +// TODO remove +/// CDC Pipe ID, used to indicate which pipe the API is addressing to (Notification, Out, In) +typedef enum +{ + CDC_PIPE_NOTIFICATION , ///< Notification pipe + CDC_PIPE_DATA_IN , ///< Data in pipe + CDC_PIPE_DATA_OUT , ///< Data out pipe + CDC_PIPE_ERROR , ///< Invalid Pipe ID +}cdc_pipeid_t; + +//--------------------------------------------------------------------+ +// CDC Communication Interface Class +//--------------------------------------------------------------------+ + +/// Communication Interface Subclass Codes +typedef enum +{ + CDC_COMM_SUBCLASS_DIRECT_LINE_CONTROL_MODEL = 0x01 , ///< Direct Line Control Model [USBPSTN1.2] + CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL , ///< Abstract Control Model [USBPSTN1.2] + CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL , ///< Telephone Control Model [USBPSTN1.2] + CDC_COMM_SUBCLASS_MULTICHANNEL_CONTROL_MODEL , ///< Multi-Channel Control Model [USBISDN1.2] + CDC_COMM_SUBCLASS_CAPI_CONTROL_MODEL , ///< CAPI Control Model [USBISDN1.2] + CDC_COMM_SUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL , ///< Ethernet Networking Control Model [USBECM1.2] + CDC_COMM_SUBCLASS_ATM_NETWORKING_CONTROL_MODEL , ///< ATM Networking Control Model [USBATM1.2] + CDC_COMM_SUBCLASS_WIRELESS_HANDSET_CONTROL_MODEL , ///< Wireless Handset Control Model [USBWMC1.1] + CDC_COMM_SUBCLASS_DEVICE_MANAGEMENT , ///< Device Management [USBWMC1.1] + CDC_COMM_SUBCLASS_MOBILE_DIRECT_LINE_MODEL , ///< Mobile Direct Line Model [USBWMC1.1] + CDC_COMM_SUBCLASS_OBEX , ///< OBEX [USBWMC1.1] + CDC_COMM_SUBCLASS_ETHERNET_EMULATION_MODEL ///< Ethernet Emulation Model [USBEEM1.0] +} cdc_comm_sublcass_type_t; + +/// Communication Interface Protocol Codes +typedef enum +{ + CDC_COMM_PROTOCOL_NONE = 0x00 , ///< No specific protocol + CDC_COMM_PROTOCOL_ATCOMMAND , ///< AT Commands: V.250 etc + CDC_COMM_PROTOCOL_ATCOMMAND_PCCA_101 , ///< AT Commands defined by PCCA-101 + CDC_COMM_PROTOCOL_ATCOMMAND_PCCA_101_AND_ANNEXO , ///< AT Commands defined by PCCA-101 & Annex O + CDC_COMM_PROTOCOL_ATCOMMAND_GSM_707 , ///< AT Commands defined by GSM 07.07 + CDC_COMM_PROTOCOL_ATCOMMAND_3GPP_27007 , ///< AT Commands defined by 3GPP 27.007 + CDC_COMM_PROTOCOL_ATCOMMAND_CDMA , ///< AT Commands defined by TIA for CDMA + CDC_COMM_PROTOCOL_ETHERNET_EMULATION_MODEL ///< Ethernet Emulation Model +} cdc_comm_protocol_type_t; + +//------------- SubType Descriptor in COMM Functional Descriptor -------------// +/// Communication Interface SubType Descriptor +typedef enum +{ + CDC_FUNC_DESC_HEADER = 0x00 , ///< Header Functional Descriptor, which marks the beginning of the concatenated set of functional descriptors for the interface. + CDC_FUNC_DESC_CALL_MANAGEMENT = 0x01 , ///< Call Management Functional Descriptor. + CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT = 0x02 , ///< Abstract Control Management Functional Descriptor. + CDC_FUNC_DESC_DIRECT_LINE_MANAGEMENT = 0x03 , ///< Direct Line Management Functional Descriptor. + CDC_FUNC_DESC_TELEPHONE_RINGER = 0x04 , ///< Telephone Ringer Functional Descriptor. + CDC_FUNC_DESC_TELEPHONE_CALL_AND_LINE_STATE_REPORTING_CAPACITY = 0x05 , ///< Telephone Call and Line State Reporting Capabilities Functional Descriptor. + CDC_FUNC_DESC_UNION = 0x06 , ///< Union Functional Descriptor + CDC_FUNC_DESC_COUNTRY_SELECTION = 0x07 , ///< Country Selection Functional Descriptor + CDC_FUNC_DESC_TELEPHONE_OPERATIONAL_MODES = 0x08 , ///< Telephone Operational ModesFunctional Descriptor + CDC_FUNC_DESC_USB_TERMINAL = 0x09 , ///< USB Terminal Functional Descriptor + CDC_FUNC_DESC_NETWORK_CHANNEL_TERMINAL = 0x0A , ///< Network Channel Terminal Descriptor + CDC_FUNC_DESC_PROTOCOL_UNIT = 0x0B , ///< Protocol Unit Functional Descriptor + CDC_FUNC_DESC_EXTENSION_UNIT = 0x0C , ///< Extension Unit Functional Descriptor + CDC_FUNC_DESC_MULTICHANEL_MANAGEMENT = 0x0D , ///< Multi-Channel Management Functional Descriptor + CDC_FUNC_DESC_CAPI_CONTROL_MANAGEMENT = 0x0E , ///< CAPI Control Management Functional Descriptor + CDC_FUNC_DESC_ETHERNET_NETWORKING = 0x0F , ///< Ethernet Networking Functional Descriptor + CDC_FUNC_DESC_ATM_NETWORKING = 0x10 , ///< ATM Networking Functional Descriptor + CDC_FUNC_DESC_WIRELESS_HANDSET_CONTROL_MODEL = 0x11 , ///< Wireless Handset Control Model Functional Descriptor + CDC_FUNC_DESC_MOBILE_DIRECT_LINE_MODEL = 0x12 , ///< Mobile Direct Line Model Functional Descriptor + CDC_FUNC_DESC_MOBILE_DIRECT_LINE_MODEL_DETAIL = 0x13 , ///< MDLM Detail Functional Descriptor + CDC_FUNC_DESC_DEVICE_MANAGEMENT_MODEL = 0x14 , ///< Device Management Model Functional Descriptor + CDC_FUNC_DESC_OBEX = 0x15 , ///< OBEX Functional Descriptor + CDC_FUNC_DESC_COMMAND_SET = 0x16 , ///< Command Set Functional Descriptor + CDC_FUNC_DESC_COMMAND_SET_DETAIL = 0x17 , ///< Command Set Detail Functional Descriptor + CDC_FUNC_DESC_TELEPHONE_CONTROL_MODEL = 0x18 , ///< Telephone Control Model Functional Descriptor + CDC_FUNC_DESC_OBEX_SERVICE_IDENTIFIER = 0x19 ///< OBEX Service Identifier Functional Descriptor +}cdc_func_desc_type_t; + +//--------------------------------------------------------------------+ +// CDC Data Interface Class +//--------------------------------------------------------------------+ + +// SUBCLASS code of Data Interface is not used and should/must be zero +/// Data Interface Protocol Codes +typedef enum{ + CDC_DATA_PROTOCOL_ISDN_BRI = 0x30, ///< Physical interface protocol for ISDN BRI + CDC_DATA_PROTOCOL_HDLC = 0x31, ///< HDLC + CDC_DATA_PROTOCOL_TRANSPARENT = 0x32, ///< Transparent + CDC_DATA_PROTOCOL_Q921_MANAGEMENT = 0x50, ///< Management protocol for Q.921 data link protocol + CDC_DATA_PROTOCOL_Q921_DATA_LINK = 0x51, ///< Data link protocol for Q.931 + CDC_DATA_PROTOCOL_Q921_TEI_MULTIPLEXOR = 0x52, ///< TEI-multiplexor for Q.921 data link protocol + CDC_DATA_PROTOCOL_V42BIS_DATA_COMPRESSION = 0x90, ///< Data compression procedures + CDC_DATA_PROTOCOL_EURO_ISDN = 0x91, ///< Euro-ISDN protocol control + CDC_DATA_PROTOCOL_V24_RATE_ADAPTION_TO_ISDN = 0x92, ///< V.24 rate adaptation to ISDN + CDC_DATA_PROTOCOL_CAPI_COMMAND = 0x93, ///< CAPI Commands + CDC_DATA_PROTOCOL_HOST_BASED_DRIVER = 0xFD, ///< Host based driver. Note: This protocol code should only be used in messages between host and device to identify the host driver portion of a protocol stack. + CDC_DATA_PROTOCOL_IN_PROTOCOL_UNIT_FUNCTIONAL_DESCRIPTOR = 0xFE ///< The protocol(s) are described using a ProtocolUnit Functional Descriptors on Communications Class Interface +}cdc_data_protocol_type_t; + +//--------------------------------------------------------------------+ +// Management Element Request (Control Endpoint) +//--------------------------------------------------------------------+ + +/// Communication Interface Management Element Request Codes +typedef enum +{ + CDC_REQUEST_SEND_ENCAPSULATED_COMMAND = 0x00, ///< is used to issue a command in the format of the supported control protocol of the Communications Class interface + CDC_REQUEST_GET_ENCAPSULATED_RESPONSE = 0x01, ///< is used to request a response in the format of the supported control protocol of the Communications Class interface. + + CDC_REQUEST_SET_COMM_FEATURE = 0x02, + CDC_REQUEST_GET_COMM_FEATURE = 0x03, + CDC_REQUEST_CLEAR_COMM_FEATURE = 0x04, + + CDC_REQUEST_SET_AUX_LINE_STATE = 0x10, + CDC_REQUEST_SET_HOOK_STATE = 0x11, + CDC_REQUEST_PULSE_SETUP = 0x12, + CDC_REQUEST_SEND_PULSE = 0x13, + CDC_REQUEST_SET_PULSE_TIME = 0x14, + CDC_REQUEST_RING_AUX_JACK = 0x15, + + CDC_REQUEST_SET_LINE_CODING = 0x20, + CDC_REQUEST_GET_LINE_CODING = 0x21, + CDC_REQUEST_SET_CONTROL_LINE_STATE = 0x22, + CDC_REQUEST_SEND_BREAK = 0x23, + + CDC_REQUEST_SET_RINGER_PARMS = 0x30, + CDC_REQUEST_GET_RINGER_PARMS = 0x31, + CDC_REQUEST_SET_OPERATION_PARMS = 0x32, + CDC_REQUEST_GET_OPERATION_PARMS = 0x33, + CDC_REQUEST_SET_LINE_PARMS = 0x34, + CDC_REQUEST_GET_LINE_PARMS = 0x35, + CDC_REQUEST_DIAL_DIGITS = 0x36, + CDC_REQUEST_SET_UNIT_PARAMETER = 0x37, + CDC_REQUEST_GET_UNIT_PARAMETER = 0x38, + CDC_REQUEST_CLEAR_UNIT_PARAMETER = 0x39, + CDC_REQUEST_GET_PROFILE = 0x3A, + + CDC_REQUEST_SET_ETHERNET_MULTICAST_FILTERS = 0x40, + CDC_REQUEST_SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x41, + CDC_REQUEST_GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x42, + CDC_REQUEST_SET_ETHERNET_PACKET_FILTER = 0x43, + CDC_REQUEST_GET_ETHERNET_STATISTIC = 0x44, + + CDC_REQUEST_SET_ATM_DATA_FORMAT = 0x50, + CDC_REQUEST_GET_ATM_DEVICE_STATISTICS = 0x51, + CDC_REQUEST_SET_ATM_DEFAULT_VC = 0x52, + CDC_REQUEST_GET_ATM_VC_STATISTICS = 0x53, + + CDC_REQUEST_MDLM_SEMANTIC_MODEL = 0x60, +}cdc_management_request_t; + +//--------------------------------------------------------------------+ +// Management Elemenent Notification (Notification Endpoint) +//--------------------------------------------------------------------+ + +/// Communication Interface Management Element Notification Codes +typedef enum +{ + NETWORK_CONNECTION = 0x00, ///< This notification allows the device to notify the host about network connection status. + RESPONSE_AVAILABLE = 0x01, ///< This notification allows the device to notify the hostthat a response is available. This response can be retrieved with a subsequent \ref CDC_REQUEST_GET_ENCAPSULATED_RESPONSE request. + + AUX_JACK_HOOK_STATE = 0x08, + RING_DETECT = 0x09, + + SERIAL_STATE = 0x20, + + CALL_STATE_CHANGE = 0x28, + LINE_STATE_CHANGE = 0x29, + CONNECTION_SPEED_CHANGE = 0x2A, ///< This notification allows the device to inform the host-networking driver that a change in either the upstream or the downstream bit rate of the connection has occurred + MDLM_SEMANTIC_MODEL_NOTIFICATION = 0x40, +}cdc_notification_request_t; + +//--------------------------------------------------------------------+ +// Class Specific Functional Descriptor (Communication Interface) +//--------------------------------------------------------------------+ + +/// Header Functional Descriptor (Communication Interface) +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUNC_DESC_ + uint16_t bcdCDC ; ///< CDC release number in Binary-Coded Decimal +}cdc_desc_func_header_t; + +/// Union Functional Descriptor (Communication Interface) +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + uint8_t bControlInterface ; ///< Interface number of Communication Interface + uint8_t bSubordinateInterface ; ///< Array of Interface number of Data Interface +}cdc_desc_func_union_t; + +#define cdc_desc_func_union_n_t(no_slave)\ + struct TU_ATTR_PACKED { \ + uint8_t bLength ;\ + uint8_t bDescriptorType ;\ + uint8_t bDescriptorSubType ;\ + uint8_t bControlInterface ;\ + uint8_t bSubordinateInterface[no_slave] ;\ +} + +/// Country Selection Functional Descriptor (Communication Interface) +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + uint8_t iCountryCodeRelDate ; ///< Index of a string giving the release date for the implemented ISO 3166 Country Codes. + uint16_t wCountryCode ; ///< Country code in the format as defined in [ISO3166], release date as specified inoffset 3 for the first supported country. +}cdc_desc_func_country_selection_t; + +#define cdc_desc_func_country_selection_n_t(no_country) \ + struct TU_ATTR_PACKED {\ + uint8_t bLength ;\ + uint8_t bDescriptorType ;\ + uint8_t bDescriptorSubType ;\ + uint8_t iCountryCodeRelDate ;\ + uint16_t wCountryCode[no_country] ;\ +} + +//--------------------------------------------------------------------+ +// PUBLIC SWITCHED TELEPHONE NETWORK (PSTN) SUBCLASS +//--------------------------------------------------------------------+ + +/// \brief Call Management Functional Descriptor +/// \details This functional descriptor describes the processing of calls for the Communications Class interface. +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + + struct { + uint8_t handle_call : 1; ///< 0 - Device sends/receives call management information only over the Communications Class interface. 1 - Device can send/receive call management information over a Data Class interface. + uint8_t send_recv_call : 1; ///< 0 - Device does not handle call management itself. 1 - Device handles call management itself. + uint8_t : 0; + } bmCapabilities; + + uint8_t bDataInterface; +}cdc_desc_func_call_management_t; + + +typedef struct TU_ATTR_PACKED +{ + uint8_t support_comm_request : 1; ///< Device supports the request combination of Set_Comm_Feature, Clear_Comm_Feature, and Get_Comm_Feature. + uint8_t support_line_request : 1; ///< Device supports the request combination of Set_Line_Coding, Set_Control_Line_State, Get_Line_Coding, and the notification Serial_State. + uint8_t support_send_break : 1; ///< Device supports the request Send_Break + uint8_t support_notification_network_connection : 1; ///< Device supports the notification Network_Connection. + uint8_t : 0; +}cdc_acm_capability_t; + +TU_VERIFY_STATIC(sizeof(cdc_acm_capability_t) == 1, "mostly problem with compiler"); + +/// \brief Abstract Control Management Functional Descriptor +/// \details This functional descriptor describes the commands supported by by the Communications Class interface with SubClass code of \ref CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + cdc_acm_capability_t bmCapabilities ; +}cdc_desc_func_acm_t; + +/// \brief Direct Line Management Functional Descriptor +/// \details This functional descriptor describes the commands supported by the Communications Class interface with SubClass code of \ref CDC_FUNC_DESC_DIRECT_LINE_MANAGEMENT +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + struct { + uint8_t require_pulse_setup : 1; ///< Device requires extra Pulse_Setup request during pulse dialing sequence to disengage holding circuit. + uint8_t support_aux_request : 1; ///< Device supports the request combination of Set_Aux_Line_State, Ring_Aux_Jack, and notification Aux_Jack_Hook_State. + uint8_t support_pulse_request : 1; ///< Device supports the request combination of Pulse_Setup, Send_Pulse, and Set_Pulse_Time. + uint8_t : 0; + } bmCapabilities; +}cdc_desc_func_direct_line_management_t; + +/// \brief Telephone Ringer Functional Descriptor +/// \details The Telephone Ringer functional descriptor describes the ringer capabilities supported by the Communications Class interface, +/// with the SubClass code of \ref CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + uint8_t bRingerVolSteps ; + uint8_t bNumRingerPatterns ; +}cdc_desc_func_telephone_ringer_t; + +/// \brief Telephone Operational Modes Functional Descriptor +/// \details The Telephone Operational Modes functional descriptor describes the operational modes supported by +/// the Communications Class interface, with the SubClass code of \ref CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + struct { + uint8_t simple_mode : 1; + uint8_t standalone_mode : 1; + uint8_t computer_centric_mode : 1; + uint8_t : 0; + } bmCapabilities; +}cdc_desc_func_telephone_operational_modes_t; + +/// \brief Telephone Call and Line State Reporting Capabilities Descriptor +/// \details The Telephone Call and Line State Reporting Capabilities functional descriptor describes the abilities of a +/// telephone device to report optional call and line states. +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + struct { + uint32_t interrupted_dialtone : 1; ///< 0 : Reports only dialtone (does not differentiate between normal and interrupted dialtone). 1 : Reports interrupted dialtone in addition to normal dialtone + uint32_t ringback_busy_fastbusy : 1; ///< 0 : Reports only dialing state. 1 : Reports ringback, busy, and fast busy states. + uint32_t caller_id : 1; ///< 0 : Does not report caller ID. 1 : Reports caller ID information. + uint32_t incoming_distinctive : 1; ///< 0 : Reports only incoming ringing. 1 : Reports incoming distinctive ringing patterns. + uint32_t dual_tone_multi_freq : 1; ///< 0 : Cannot report dual tone multi-frequency (DTMF) digits input remotely over the telephone line. 1 : Can report DTMF digits input remotely over the telephone line. + uint32_t line_state_change : 1; ///< 0 : Does not support line state change notification. 1 : Does support line state change notification + uint32_t : 0; + } bmCapabilities; +}cdc_desc_func_telephone_call_state_reporting_capabilities_t; + +static inline uint8_t cdc_functional_desc_typeof(uint8_t const * p_desc) +{ + return p_desc[2]; +} + +//--------------------------------------------------------------------+ +// Requests +//--------------------------------------------------------------------+ +typedef struct TU_ATTR_PACKED +{ + uint32_t bit_rate; + uint8_t stop_bits; ///< 0: 1 stop bit - 1: 1.5 stop bits - 2: 2 stop bits + uint8_t parity; ///< 0: None - 1: Odd - 2: Even - 3: Mark - 4: Space + uint8_t data_bits; ///< can be 5, 6, 7, 8 or 16 +} cdc_line_coding_t; + +TU_VERIFY_STATIC(sizeof(cdc_line_coding_t) == 7, "size is not correct"); + +typedef struct TU_ATTR_PACKED +{ + uint16_t dte_is_present : 1; ///< Indicates to DCE if DTE is presentor not. This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR. + uint16_t half_duplex_carrier_control : 1; + uint16_t : 14; +} cdc_line_control_state_t; + +TU_VERIFY_STATIC(sizeof(cdc_line_control_state_t) == 2, "size is not correct"); + +/** @} */ + +#ifdef __cplusplus + } +#endif + +#endif + +/** @} */ diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c index 3ff72b00c..c0de0cadd 100644 --- a/src/class/cdc/cdc_device.c +++ b/src/class/cdc/cdc_device.c @@ -1,415 +1,415 @@ -/* - * 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 && CFG_TUD_CDC) - -#include "cdc_device.h" -#include "device/usbd_pvt.h" - -//--------------------------------------------------------------------+ -// MACRO CONSTANT TYPEDEF -//--------------------------------------------------------------------+ -typedef struct -{ - uint8_t itf_num; - uint8_t ep_notif; - uint8_t ep_in; - uint8_t ep_out; - - // Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send) - uint8_t line_state; - - /*------------- From this point, data is not cleared by bus reset -------------*/ - char wanted_char; - cdc_line_coding_t line_coding; - - // FIFO - tu_fifo_t rx_ff; - tu_fifo_t tx_ff; - - uint8_t rx_ff_buf[CFG_TUD_CDC_RX_BUFSIZE]; - uint8_t tx_ff_buf[CFG_TUD_CDC_TX_BUFSIZE]; - -#if CFG_FIFO_MUTEX - osal_mutex_def_t rx_ff_mutex; - osal_mutex_def_t tx_ff_mutex; -#endif - - // Endpoint Transfer buffer - CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CDC_EPSIZE]; - CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CDC_EPSIZE]; - -}cdcd_interface_t; - -#define ITF_MEM_RESET_SIZE offsetof(cdcd_interface_t, wanted_char) - -//--------------------------------------------------------------------+ -// INTERNAL OBJECT & FUNCTION DECLARATION -//--------------------------------------------------------------------+ -CFG_TUSB_MEM_SECTION static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC]; - -static void _prep_out_transaction (uint8_t itf) -{ - cdcd_interface_t* p_cdc = &_cdcd_itf[itf]; - - // skip if previous transfer not complete - if ( usbd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_out) ) return; - - // Prepare for incoming data but only allow what we can store in the ring buffer. - uint16_t max_read = tu_fifo_remaining(&p_cdc->rx_ff); - if ( max_read >= CFG_TUD_CDC_EPSIZE ) - { - usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_out, p_cdc->epout_buf, CFG_TUD_CDC_EPSIZE); - } -} - -//--------------------------------------------------------------------+ -// APPLICATION API -//--------------------------------------------------------------------+ -bool tud_cdc_n_connected(uint8_t itf) -{ - // DTR (bit 0) active is considered as connected - return tud_ready() && tu_bit_test(_cdcd_itf[itf].line_state, 0); -} - -uint8_t tud_cdc_n_get_line_state (uint8_t itf) -{ - return _cdcd_itf[itf].line_state; -} - -void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding) -{ - (*coding) = _cdcd_itf[itf].line_coding; -} - -void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted) -{ - _cdcd_itf[itf].wanted_char = wanted; -} - - -//--------------------------------------------------------------------+ -// READ API -//--------------------------------------------------------------------+ -uint32_t tud_cdc_n_available(uint8_t itf) -{ - return tu_fifo_count(&_cdcd_itf[itf].rx_ff); -} - -uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize) -{ - uint32_t num_read = tu_fifo_read_n(&_cdcd_itf[itf].rx_ff, buffer, bufsize); - _prep_out_transaction(itf); - return num_read; -} - -bool tud_cdc_n_peek(uint8_t itf, int pos, uint8_t* chr) -{ - return tu_fifo_peek_at(&_cdcd_itf[itf].rx_ff, pos, chr); -} - -void tud_cdc_n_read_flush (uint8_t itf) -{ - tu_fifo_clear(&_cdcd_itf[itf].rx_ff); - _prep_out_transaction(itf); -} - -//--------------------------------------------------------------------+ -// WRITE API -//--------------------------------------------------------------------+ -uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize) -{ - uint16_t ret = tu_fifo_write_n(&_cdcd_itf[itf].tx_ff, buffer, bufsize); - -#if 0 // TODO issue with circuitpython's REPL - // flush if queue more than endpoint size - if ( tu_fifo_count(&_cdcd_itf[itf].tx_ff) >= CFG_TUD_CDC_EPSIZE ) - { - tud_cdc_n_write_flush(itf); - } -#endif - - return ret; -} - -bool tud_cdc_n_write_flush (uint8_t itf) -{ - cdcd_interface_t* p_cdc = &_cdcd_itf[itf]; - TU_VERIFY( !usbd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_in) ); // skip if previous transfer not complete - - uint16_t count = tu_fifo_read_n(&_cdcd_itf[itf].tx_ff, p_cdc->epin_buf, CFG_TUD_CDC_EPSIZE); - if ( count ) - { - TU_VERIFY( tud_cdc_n_connected(itf) ); // fifo is empty if not connected - TU_ASSERT( usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_in, p_cdc->epin_buf, count) ); - } - - return true; -} - -uint32_t tud_cdc_n_write_available (uint8_t itf) -{ - return tu_fifo_remaining(&_cdcd_itf[itf].tx_ff); -} - - -//--------------------------------------------------------------------+ -// USBD Driver API -//--------------------------------------------------------------------+ -void cdcd_init(void) -{ - tu_memclr(_cdcd_itf, sizeof(_cdcd_itf)); - - for(uint8_t i=0; iwanted_char = -1; - - // default line coding is : stop bit = 1, parity = none, data bits = 8 - p_cdc->line_coding.bit_rate = 115200; - p_cdc->line_coding.stop_bits = 0; - p_cdc->line_coding.parity = 0; - p_cdc->line_coding.data_bits = 8; - - // config fifo - tu_fifo_config(&p_cdc->rx_ff, p_cdc->rx_ff_buf, CFG_TUD_CDC_RX_BUFSIZE, 1, false); - tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, CFG_TUD_CDC_TX_BUFSIZE, 1, false); - -#if CFG_FIFO_MUTEX - tu_fifo_config_mutex(&p_cdc->rx_ff, osal_mutex_create(&p_cdc->rx_ff_mutex)); - tu_fifo_config_mutex(&p_cdc->tx_ff, osal_mutex_create(&p_cdc->tx_ff_mutex)); -#endif - } -} - -void cdcd_reset(uint8_t rhport) -{ - (void) rhport; - - for(uint8_t i=0; ibInterfaceSubClass); - - // Only support AT commands, no protocol and vendor specific commands. - TU_ASSERT(tu_within(CDC_COMM_PROTOCOL_NONE, itf_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA) || - itf_desc->bInterfaceProtocol == 0xff); - - // Find available interface - cdcd_interface_t * p_cdc = NULL; - uint8_t cdc_id; - for(cdc_id=0; cdc_iditf_num = itf_desc->bInterfaceNumber; - - uint8_t const * p_desc = tu_desc_next( itf_desc ); - (*p_length) = sizeof(tusb_desc_interface_t); - - // 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); - } - - if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) ) - { - // notification endpoint if any - TU_ASSERT( dcd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc) ); - - p_cdc->ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress; - - (*p_length) += p_desc[DESC_OFFSET_LEN]; - p_desc = tu_desc_next(p_desc); - } - - //------------- Data Interface (if any) -------------// - 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); - - // Open endpoint pair - TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in) ); - - (*p_length) += sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t); - } - - // Prepare for incoming data - _prep_out_transaction(cdc_id); - - return true; -} - -// Invoked when class request DATA stage is finished. -// return false to stall control endpoint (e.g Host send non-sense DATA) -bool cdcd_control_complete(uint8_t rhport, tusb_control_request_t const * request) -{ - (void) rhport; - - //------------- Class Specific Request -------------// - TU_VERIFY (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); - - uint8_t itf = 0; - cdcd_interface_t* p_cdc = _cdcd_itf; - - // Identify which interface to use - for ( ; ; itf++, p_cdc++) - { - if (itf >= TU_ARRAY_SIZE(_cdcd_itf)) return false; - - if ( p_cdc->itf_num == request->wIndex ) break; - } - - // Invoke callback - if ( CDC_REQUEST_SET_LINE_CODING == request->bRequest ) - { - if ( tud_cdc_line_coding_cb ) tud_cdc_line_coding_cb(itf, &p_cdc->line_coding); - } - - return true; -} - -// Handle class control request -// return false to stall control endpoint (e.g unsupported request) -bool cdcd_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); - - uint8_t itf = 0; - cdcd_interface_t* p_cdc = _cdcd_itf; - - // Identify which interface to use - for ( ; ; itf++, p_cdc++) - { - if (itf >= TU_ARRAY_SIZE(_cdcd_itf)) return false; - - if ( p_cdc->itf_num == request->wIndex ) break; - } - - switch ( request->bRequest ) - { - case CDC_REQUEST_SET_LINE_CODING: - tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t)); - break; - - case CDC_REQUEST_GET_LINE_CODING: - 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) - p_cdc->line_state = (uint8_t) request->wValue; - - 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)); - break; - - default: return false; // stall unsupported request - } - - return true; -} - -bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) -{ - (void) rhport; - (void) result; - - uint8_t itf = 0; - cdcd_interface_t* p_cdc = _cdcd_itf; - - // Identify which interface to use - for ( ; ; itf++, p_cdc++) - { - if (itf >= TU_ARRAY_SIZE(_cdcd_itf)) return false; - - if ( ( ep_addr == p_cdc->ep_out ) || ( ep_addr == p_cdc->ep_in ) ) break; - } - - // Received new data - if ( ep_addr == p_cdc->ep_out ) - { - for(uint32_t i=0; irx_ff, &p_cdc->epout_buf[i]); - - // Check for wanted char and invoke callback if needed - if ( tud_cdc_rx_wanted_cb && ( ((signed char) p_cdc->wanted_char) != -1 ) && ( p_cdc->wanted_char == p_cdc->epout_buf[i] ) ) - { - tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char); - } - } - - // invoke receive callback (if there is still data) - if (tud_cdc_rx_cb && tu_fifo_count(&p_cdc->rx_ff) ) tud_cdc_rx_cb(itf); - - // prepare for OUT transaction - _prep_out_transaction(itf); - } - - // Data sent to host, we could continue to fetch data tx fifo to send. - // But it will cause incorrect baudrate set in line coding. - // Though maybe the baudrate is not really important !!! -// if ( ep_addr == p_cdc->ep_in ) -// { -// -// } - - // nothing to do with notif endpoint for now - - return true; -} - -#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 && CFG_TUD_CDC) + +#include "cdc_device.h" +#include "device/usbd_pvt.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +typedef struct +{ + uint8_t itf_num; + uint8_t ep_notif; + uint8_t ep_in; + uint8_t ep_out; + + // Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send) + uint8_t line_state; + + /*------------- From this point, data is not cleared by bus reset -------------*/ + char wanted_char; + cdc_line_coding_t line_coding; + + // FIFO + tu_fifo_t rx_ff; + tu_fifo_t tx_ff; + + uint8_t rx_ff_buf[CFG_TUD_CDC_RX_BUFSIZE]; + uint8_t tx_ff_buf[CFG_TUD_CDC_TX_BUFSIZE]; + +#if CFG_FIFO_MUTEX + osal_mutex_def_t rx_ff_mutex; + osal_mutex_def_t tx_ff_mutex; +#endif + + // Endpoint Transfer buffer + CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CDC_EPSIZE]; + CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CDC_EPSIZE]; + +}cdcd_interface_t; + +#define ITF_MEM_RESET_SIZE offsetof(cdcd_interface_t, wanted_char) + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ +CFG_TUSB_MEM_SECTION static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC]; + +static void _prep_out_transaction (uint8_t itf) +{ + cdcd_interface_t* p_cdc = &_cdcd_itf[itf]; + + // skip if previous transfer not complete + if ( usbd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_out) ) return; + + // Prepare for incoming data but only allow what we can store in the ring buffer. + uint16_t max_read = tu_fifo_remaining(&p_cdc->rx_ff); + if ( max_read >= CFG_TUD_CDC_EPSIZE ) + { + usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_out, p_cdc->epout_buf, CFG_TUD_CDC_EPSIZE); + } +} + +//--------------------------------------------------------------------+ +// APPLICATION API +//--------------------------------------------------------------------+ +bool tud_cdc_n_connected(uint8_t itf) +{ + // DTR (bit 0) active is considered as connected + return tud_ready() && tu_bit_test(_cdcd_itf[itf].line_state, 0); +} + +uint8_t tud_cdc_n_get_line_state (uint8_t itf) +{ + return _cdcd_itf[itf].line_state; +} + +void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding) +{ + (*coding) = _cdcd_itf[itf].line_coding; +} + +void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted) +{ + _cdcd_itf[itf].wanted_char = wanted; +} + + +//--------------------------------------------------------------------+ +// READ API +//--------------------------------------------------------------------+ +uint32_t tud_cdc_n_available(uint8_t itf) +{ + return tu_fifo_count(&_cdcd_itf[itf].rx_ff); +} + +uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize) +{ + uint32_t num_read = tu_fifo_read_n(&_cdcd_itf[itf].rx_ff, buffer, bufsize); + _prep_out_transaction(itf); + return num_read; +} + +bool tud_cdc_n_peek(uint8_t itf, int pos, uint8_t* chr) +{ + return tu_fifo_peek_at(&_cdcd_itf[itf].rx_ff, pos, chr); +} + +void tud_cdc_n_read_flush (uint8_t itf) +{ + tu_fifo_clear(&_cdcd_itf[itf].rx_ff); + _prep_out_transaction(itf); +} + +//--------------------------------------------------------------------+ +// WRITE API +//--------------------------------------------------------------------+ +uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize) +{ + uint16_t ret = tu_fifo_write_n(&_cdcd_itf[itf].tx_ff, buffer, bufsize); + +#if 0 // TODO issue with circuitpython's REPL + // flush if queue more than endpoint size + if ( tu_fifo_count(&_cdcd_itf[itf].tx_ff) >= CFG_TUD_CDC_EPSIZE ) + { + tud_cdc_n_write_flush(itf); + } +#endif + + return ret; +} + +bool tud_cdc_n_write_flush (uint8_t itf) +{ + cdcd_interface_t* p_cdc = &_cdcd_itf[itf]; + TU_VERIFY( !usbd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_in) ); // skip if previous transfer not complete + + uint16_t count = tu_fifo_read_n(&_cdcd_itf[itf].tx_ff, p_cdc->epin_buf, CFG_TUD_CDC_EPSIZE); + if ( count ) + { + TU_VERIFY( tud_cdc_n_connected(itf) ); // fifo is empty if not connected + TU_ASSERT( usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_in, p_cdc->epin_buf, count) ); + } + + return true; +} + +uint32_t tud_cdc_n_write_available (uint8_t itf) +{ + return tu_fifo_remaining(&_cdcd_itf[itf].tx_ff); +} + + +//--------------------------------------------------------------------+ +// USBD Driver API +//--------------------------------------------------------------------+ +void cdcd_init(void) +{ + tu_memclr(_cdcd_itf, sizeof(_cdcd_itf)); + + for(uint8_t i=0; iwanted_char = -1; + + // default line coding is : stop bit = 1, parity = none, data bits = 8 + p_cdc->line_coding.bit_rate = 115200; + p_cdc->line_coding.stop_bits = 0; + p_cdc->line_coding.parity = 0; + p_cdc->line_coding.data_bits = 8; + + // config fifo + tu_fifo_config(&p_cdc->rx_ff, p_cdc->rx_ff_buf, CFG_TUD_CDC_RX_BUFSIZE, 1, false); + tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, CFG_TUD_CDC_TX_BUFSIZE, 1, false); + +#if CFG_FIFO_MUTEX + tu_fifo_config_mutex(&p_cdc->rx_ff, osal_mutex_create(&p_cdc->rx_ff_mutex)); + tu_fifo_config_mutex(&p_cdc->tx_ff, osal_mutex_create(&p_cdc->tx_ff_mutex)); +#endif + } +} + +void cdcd_reset(uint8_t rhport) +{ + (void) rhport; + + for(uint8_t i=0; ibInterfaceSubClass); + + // Only support AT commands, no protocol and vendor specific commands. + TU_ASSERT(tu_within(CDC_COMM_PROTOCOL_NONE, itf_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA) || + itf_desc->bInterfaceProtocol == 0xff); + + // Find available interface + cdcd_interface_t * p_cdc = NULL; + uint8_t cdc_id; + for(cdc_id=0; cdc_iditf_num = itf_desc->bInterfaceNumber; + + uint8_t const * p_desc = tu_desc_next( itf_desc ); + (*p_length) = sizeof(tusb_desc_interface_t); + + // 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); + } + + if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) ) + { + // notification endpoint if any + TU_ASSERT( dcd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc) ); + + p_cdc->ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress; + + (*p_length) += p_desc[DESC_OFFSET_LEN]; + p_desc = tu_desc_next(p_desc); + } + + //------------- Data Interface (if any) -------------// + 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); + + // Open endpoint pair + TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in) ); + + (*p_length) += sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t); + } + + // Prepare for incoming data + _prep_out_transaction(cdc_id); + + return true; +} + +// Invoked when class request DATA stage is finished. +// return false to stall control endpoint (e.g Host send non-sense DATA) +bool cdcd_control_complete(uint8_t rhport, tusb_control_request_t const * request) +{ + (void) rhport; + + //------------- Class Specific Request -------------// + TU_VERIFY (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); + + uint8_t itf = 0; + cdcd_interface_t* p_cdc = _cdcd_itf; + + // Identify which interface to use + for ( ; ; itf++, p_cdc++) + { + if (itf >= TU_ARRAY_SIZE(_cdcd_itf)) return false; + + if ( p_cdc->itf_num == request->wIndex ) break; + } + + // Invoke callback + if ( CDC_REQUEST_SET_LINE_CODING == request->bRequest ) + { + if ( tud_cdc_line_coding_cb ) tud_cdc_line_coding_cb(itf, &p_cdc->line_coding); + } + + return true; +} + +// Handle class control request +// return false to stall control endpoint (e.g unsupported request) +bool cdcd_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); + + uint8_t itf = 0; + cdcd_interface_t* p_cdc = _cdcd_itf; + + // Identify which interface to use + for ( ; ; itf++, p_cdc++) + { + if (itf >= TU_ARRAY_SIZE(_cdcd_itf)) return false; + + if ( p_cdc->itf_num == request->wIndex ) break; + } + + switch ( request->bRequest ) + { + case CDC_REQUEST_SET_LINE_CODING: + tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t)); + break; + + case CDC_REQUEST_GET_LINE_CODING: + 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) + p_cdc->line_state = (uint8_t) request->wValue; + + 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)); + break; + + default: return false; // stall unsupported request + } + + return true; +} + +bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) +{ + (void) rhport; + (void) result; + + uint8_t itf = 0; + cdcd_interface_t* p_cdc = _cdcd_itf; + + // Identify which interface to use + for ( ; ; itf++, p_cdc++) + { + if (itf >= TU_ARRAY_SIZE(_cdcd_itf)) return false; + + if ( ( ep_addr == p_cdc->ep_out ) || ( ep_addr == p_cdc->ep_in ) ) break; + } + + // Received new data + if ( ep_addr == p_cdc->ep_out ) + { + for(uint32_t i=0; irx_ff, &p_cdc->epout_buf[i]); + + // Check for wanted char and invoke callback if needed + if ( tud_cdc_rx_wanted_cb && ( ((signed char) p_cdc->wanted_char) != -1 ) && ( p_cdc->wanted_char == p_cdc->epout_buf[i] ) ) + { + tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char); + } + } + + // invoke receive callback (if there is still data) + if (tud_cdc_rx_cb && tu_fifo_count(&p_cdc->rx_ff) ) tud_cdc_rx_cb(itf); + + // prepare for OUT transaction + _prep_out_transaction(itf); + } + + // Data sent to host, we could continue to fetch data tx fifo to send. + // But it will cause incorrect baudrate set in line coding. + // Though maybe the baudrate is not really important !!! +// if ( ep_addr == p_cdc->ep_in ) +// { +// +// } + + // nothing to do with notif endpoint for now + + return true; +} + +#endif diff --git a/src/class/cdc/cdc_device.h b/src/class/cdc/cdc_device.h index b8b3c864d..969ec7f99 100644 --- a/src/class/cdc/cdc_device.h +++ b/src/class/cdc/cdc_device.h @@ -1,213 +1,213 @@ -/* - * 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. - */ - -#ifndef _TUSB_CDC_DEVICE_H_ -#define _TUSB_CDC_DEVICE_H_ - -#include "common/tusb_common.h" -#include "device/usbd.h" -#include "cdc.h" - -//--------------------------------------------------------------------+ -// Class Driver Configuration -//--------------------------------------------------------------------+ -#ifndef CFG_TUD_CDC_EPSIZE -#define CFG_TUD_CDC_EPSIZE 64 -#endif - -#ifdef __cplusplus - extern "C" { -#endif - -/** \addtogroup CDC_Serial Serial - * @{ - * \defgroup CDC_Serial_Device Device - * @{ */ - -//--------------------------------------------------------------------+ -// Application API (Multiple Interfaces) -// CFG_TUD_CDC > 1 -//--------------------------------------------------------------------+ -bool tud_cdc_n_connected (uint8_t itf); -uint8_t tud_cdc_n_get_line_state (uint8_t itf); -void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding); -void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted); - -uint32_t tud_cdc_n_available (uint8_t itf); -uint32_t tud_cdc_n_read (uint8_t itf, void* buffer, uint32_t bufsize); -void tud_cdc_n_read_flush (uint8_t itf); -bool tud_cdc_n_peek (uint8_t itf, int pos, uint8_t* u8); -static inline int32_t tud_cdc_n_read_char (uint8_t itf); - -uint32_t tud_cdc_n_write (uint8_t itf, void const* buffer, uint32_t bufsize); -bool tud_cdc_n_write_flush (uint8_t itf); -uint32_t tud_cdc_n_write_available (uint8_t itf); -static inline uint32_t tud_cdc_n_write_char (uint8_t itf, char ch); -static inline uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str); - -//--------------------------------------------------------------------+ -// Application API (Interface0) -//--------------------------------------------------------------------+ -static inline bool tud_cdc_connected (void); -static inline uint8_t tud_cdc_get_line_state (void); -static inline void tud_cdc_get_line_coding (cdc_line_coding_t* coding); -static inline void tud_cdc_set_wanted_char (char wanted); - -static inline uint32_t tud_cdc_available (void); -static inline int32_t tud_cdc_read_char (void); -static inline uint32_t tud_cdc_read (void* buffer, uint32_t bufsize); -static inline void tud_cdc_read_flush (void); -static inline bool tud_cdc_peek (int pos, uint8_t* u8); - -static inline uint32_t tud_cdc_write_char (char ch); -static inline uint32_t tud_cdc_write (void const* buffer, uint32_t bufsize); -static inline uint32_t tud_cdc_write_str (char const* str); -static inline bool tud_cdc_write_flush (void); -static inline uint32_t tud_cdc_write_available (void); - -//--------------------------------------------------------------------+ -// Application Callback API (weak is optional) -//--------------------------------------------------------------------+ - -// Invoked when received new data -TU_ATTR_WEAK void tud_cdc_rx_cb(uint8_t itf); - -// Invoked when received `wanted_char` -TU_ATTR_WEAK void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char); - -// Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE -TU_ATTR_WEAK void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts); - -// Invoked when line coding is change via SET_LINE_CODING -TU_ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding); - -//--------------------------------------------------------------------+ -// Inline Functions -//--------------------------------------------------------------------+ -static inline int32_t tud_cdc_n_read_char (uint8_t itf) -{ - uint8_t ch; - return tud_cdc_n_read(itf, &ch, 1) ? (int32_t) ch : -1; -} - -static inline uint32_t tud_cdc_n_write_char(uint8_t itf, char ch) -{ - return tud_cdc_n_write(itf, &ch, 1); -} - -static inline uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str) -{ - return tud_cdc_n_write(itf, str, strlen(str)); -} - -static inline bool tud_cdc_connected (void) -{ - return tud_cdc_n_connected(0); -} - -static inline uint8_t tud_cdc_get_line_state (void) -{ - return tud_cdc_n_get_line_state(0); -} - -static inline void tud_cdc_get_line_coding (cdc_line_coding_t* coding) -{ - tud_cdc_n_get_line_coding(0, coding); -} - -static inline void tud_cdc_set_wanted_char (char wanted) -{ - tud_cdc_n_set_wanted_char(0, wanted); -} - -static inline uint32_t tud_cdc_available (void) -{ - return tud_cdc_n_available(0); -} - -static inline int32_t tud_cdc_read_char (void) -{ - return tud_cdc_n_read_char(0); -} - -static inline uint32_t tud_cdc_read (void* buffer, uint32_t bufsize) -{ - return tud_cdc_n_read(0, buffer, bufsize); -} - -static inline void tud_cdc_read_flush (void) -{ - tud_cdc_n_read_flush(0); -} - -static inline bool tud_cdc_peek (int pos, uint8_t* u8) -{ - return tud_cdc_n_peek(0, pos, u8); -} - -static inline uint32_t tud_cdc_write_char (char ch) -{ - return tud_cdc_n_write_char(0, ch); -} - -static inline uint32_t tud_cdc_write (void const* buffer, uint32_t bufsize) -{ - return tud_cdc_n_write(0, buffer, bufsize); -} - -static inline uint32_t tud_cdc_write_str (char const* str) -{ - return tud_cdc_n_write_str(0, str); -} - -static inline bool tud_cdc_write_flush (void) -{ - return tud_cdc_n_write_flush(0); -} - -static inline uint32_t tud_cdc_write_available(void) -{ - return tud_cdc_n_write_available(0); -} - -/** @} */ -/** @} */ - -//--------------------------------------------------------------------+ -// INTERNAL USBD-CLASS DRIVER API -//--------------------------------------------------------------------+ -void cdcd_init (void); -void cdcd_reset (uint8_t rhport); -bool cdcd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length); -bool cdcd_control_request (uint8_t rhport, tusb_control_request_t const * request); -bool cdcd_control_complete (uint8_t rhport, tusb_control_request_t const * request); -bool cdcd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_CDC_DEVICE_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. + */ + +#ifndef _TUSB_CDC_DEVICE_H_ +#define _TUSB_CDC_DEVICE_H_ + +#include "common/tusb_common.h" +#include "device/usbd.h" +#include "cdc.h" + +//--------------------------------------------------------------------+ +// Class Driver Configuration +//--------------------------------------------------------------------+ +#ifndef CFG_TUD_CDC_EPSIZE +#define CFG_TUD_CDC_EPSIZE 64 +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/** \addtogroup CDC_Serial Serial + * @{ + * \defgroup CDC_Serial_Device Device + * @{ */ + +//--------------------------------------------------------------------+ +// Application API (Multiple Interfaces) +// CFG_TUD_CDC > 1 +//--------------------------------------------------------------------+ +bool tud_cdc_n_connected (uint8_t itf); +uint8_t tud_cdc_n_get_line_state (uint8_t itf); +void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding); +void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted); + +uint32_t tud_cdc_n_available (uint8_t itf); +uint32_t tud_cdc_n_read (uint8_t itf, void* buffer, uint32_t bufsize); +void tud_cdc_n_read_flush (uint8_t itf); +bool tud_cdc_n_peek (uint8_t itf, int pos, uint8_t* u8); +static inline int32_t tud_cdc_n_read_char (uint8_t itf); + +uint32_t tud_cdc_n_write (uint8_t itf, void const* buffer, uint32_t bufsize); +bool tud_cdc_n_write_flush (uint8_t itf); +uint32_t tud_cdc_n_write_available (uint8_t itf); +static inline uint32_t tud_cdc_n_write_char (uint8_t itf, char ch); +static inline uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str); + +//--------------------------------------------------------------------+ +// Application API (Interface0) +//--------------------------------------------------------------------+ +static inline bool tud_cdc_connected (void); +static inline uint8_t tud_cdc_get_line_state (void); +static inline void tud_cdc_get_line_coding (cdc_line_coding_t* coding); +static inline void tud_cdc_set_wanted_char (char wanted); + +static inline uint32_t tud_cdc_available (void); +static inline int32_t tud_cdc_read_char (void); +static inline uint32_t tud_cdc_read (void* buffer, uint32_t bufsize); +static inline void tud_cdc_read_flush (void); +static inline bool tud_cdc_peek (int pos, uint8_t* u8); + +static inline uint32_t tud_cdc_write_char (char ch); +static inline uint32_t tud_cdc_write (void const* buffer, uint32_t bufsize); +static inline uint32_t tud_cdc_write_str (char const* str); +static inline bool tud_cdc_write_flush (void); +static inline uint32_t tud_cdc_write_available (void); + +//--------------------------------------------------------------------+ +// Application Callback API (weak is optional) +//--------------------------------------------------------------------+ + +// Invoked when received new data +TU_ATTR_WEAK void tud_cdc_rx_cb(uint8_t itf); + +// Invoked when received `wanted_char` +TU_ATTR_WEAK void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char); + +// Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE +TU_ATTR_WEAK void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts); + +// Invoked when line coding is change via SET_LINE_CODING +TU_ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding); + +//--------------------------------------------------------------------+ +// Inline Functions +//--------------------------------------------------------------------+ +static inline int32_t tud_cdc_n_read_char (uint8_t itf) +{ + uint8_t ch; + return tud_cdc_n_read(itf, &ch, 1) ? (int32_t) ch : -1; +} + +static inline uint32_t tud_cdc_n_write_char(uint8_t itf, char ch) +{ + return tud_cdc_n_write(itf, &ch, 1); +} + +static inline uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str) +{ + return tud_cdc_n_write(itf, str, strlen(str)); +} + +static inline bool tud_cdc_connected (void) +{ + return tud_cdc_n_connected(0); +} + +static inline uint8_t tud_cdc_get_line_state (void) +{ + return tud_cdc_n_get_line_state(0); +} + +static inline void tud_cdc_get_line_coding (cdc_line_coding_t* coding) +{ + tud_cdc_n_get_line_coding(0, coding); +} + +static inline void tud_cdc_set_wanted_char (char wanted) +{ + tud_cdc_n_set_wanted_char(0, wanted); +} + +static inline uint32_t tud_cdc_available (void) +{ + return tud_cdc_n_available(0); +} + +static inline int32_t tud_cdc_read_char (void) +{ + return tud_cdc_n_read_char(0); +} + +static inline uint32_t tud_cdc_read (void* buffer, uint32_t bufsize) +{ + return tud_cdc_n_read(0, buffer, bufsize); +} + +static inline void tud_cdc_read_flush (void) +{ + tud_cdc_n_read_flush(0); +} + +static inline bool tud_cdc_peek (int pos, uint8_t* u8) +{ + return tud_cdc_n_peek(0, pos, u8); +} + +static inline uint32_t tud_cdc_write_char (char ch) +{ + return tud_cdc_n_write_char(0, ch); +} + +static inline uint32_t tud_cdc_write (void const* buffer, uint32_t bufsize) +{ + return tud_cdc_n_write(0, buffer, bufsize); +} + +static inline uint32_t tud_cdc_write_str (char const* str) +{ + return tud_cdc_n_write_str(0, str); +} + +static inline bool tud_cdc_write_flush (void) +{ + return tud_cdc_n_write_flush(0); +} + +static inline uint32_t tud_cdc_write_available(void) +{ + return tud_cdc_n_write_available(0); +} + +/** @} */ +/** @} */ + +//--------------------------------------------------------------------+ +// INTERNAL USBD-CLASS DRIVER API +//--------------------------------------------------------------------+ +void cdcd_init (void); +void cdcd_reset (uint8_t rhport); +bool cdcd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length); +bool cdcd_control_request (uint8_t rhport, tusb_control_request_t const * request); +bool cdcd_control_complete (uint8_t rhport, tusb_control_request_t const * request); +bool cdcd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CDC_DEVICE_H_ */ diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 6a6f508b3..595e7fd62 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -1,225 +1,225 @@ -/* - * 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_HOST_ENABLED && CFG_TUH_CDC) - -#include "common/tusb_common.h" -#include "cdc_host.h" - -//--------------------------------------------------------------------+ -// MACRO CONSTANT TYPEDEF -//--------------------------------------------------------------------+ -typedef struct { - uint8_t itf_num; - uint8_t itf_protocol; - - uint8_t ep_notif; - uint8_t ep_in; - uint8_t ep_out; - - cdc_acm_capability_t acm_capability; - -} cdch_data_t; - -//--------------------------------------------------------------------+ -// INTERNAL OBJECT & FUNCTION DECLARATION -//--------------------------------------------------------------------+ -static cdch_data_t cdch_data[CFG_TUSB_HOST_DEVICE_MAX]; - -bool tuh_cdc_mounted(uint8_t dev_addr) -{ - cdch_data_t* cdc = &cdch_data[dev_addr-1]; - return cdc->ep_in && cdc->ep_out; -} - -bool tuh_cdc_is_busy(uint8_t dev_addr, cdc_pipeid_t pipeid) -{ - if ( !tuh_cdc_mounted(dev_addr) ) return false; - - cdch_data_t const * p_cdc = &cdch_data[dev_addr-1]; - - switch (pipeid) - { - case CDC_PIPE_NOTIFICATION: - return hcd_edpt_busy(dev_addr, p_cdc->ep_notif ); - - case CDC_PIPE_DATA_IN: - return hcd_edpt_busy(dev_addr, p_cdc->ep_in ); - - case CDC_PIPE_DATA_OUT: - return hcd_edpt_busy(dev_addr, p_cdc->ep_out ); - - default: - return false; - } -} - -//--------------------------------------------------------------------+ -// APPLICATION API (parameter validation needed) -//--------------------------------------------------------------------+ -bool tuh_cdc_serial_is_mounted(uint8_t dev_addr) -{ - // TODO consider all AT Command as serial candidate - return tuh_cdc_mounted(dev_addr) && - (CDC_COMM_PROTOCOL_ATCOMMAND <= cdch_data[dev_addr-1].itf_protocol) && - (cdch_data[dev_addr-1].itf_protocol <= CDC_COMM_PROTOCOL_ATCOMMAND_CDMA); -} - -bool tuh_cdc_send(uint8_t dev_addr, void const * p_data, uint32_t length, bool is_notify) -{ - TU_VERIFY( tuh_cdc_mounted(dev_addr) ); - TU_VERIFY( p_data != NULL && length, TUSB_ERROR_INVALID_PARA); - - uint8_t const ep_out = cdch_data[dev_addr-1].ep_out; - if ( hcd_edpt_busy(dev_addr, ep_out) ) return false; - - return hcd_pipe_xfer(dev_addr, ep_out, (void *) p_data, length, is_notify); -} - -bool tuh_cdc_receive(uint8_t dev_addr, void * p_buffer, uint32_t length, bool is_notify) -{ - TU_VERIFY( tuh_cdc_mounted(dev_addr) ); - TU_VERIFY( p_buffer != NULL && length, TUSB_ERROR_INVALID_PARA); - - uint8_t const ep_in = cdch_data[dev_addr-1].ep_in; - if ( hcd_edpt_busy(dev_addr, ep_in) ) return false; - - return hcd_pipe_xfer(dev_addr, ep_in, p_buffer, length, is_notify); -} - -//--------------------------------------------------------------------+ -// USBH-CLASS DRIVER API -//--------------------------------------------------------------------+ -void cdch_init(void) -{ - tu_memclr(cdch_data, sizeof(cdch_data_t)*CFG_TUSB_HOST_DEVICE_MAX); -} - -bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length) -{ - // Only support ACM - TU_VERIFY( CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass); - - // Only support AT commands, no protocol and vendor specific commands. - TU_VERIFY(tu_within(CDC_COMM_PROTOCOL_NONE, itf_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA) || - 0xff == itf_desc->bInterfaceProtocol); - - uint8_t const * p_desc; - cdch_data_t * p_cdc; - - p_desc = tu_desc_next(itf_desc); - p_cdc = &cdch_data[dev_addr-1]; - - p_cdc->itf_num = itf_desc->bInterfaceNumber; - p_cdc->itf_protocol = itf_desc->bInterfaceProtocol; // TODO 0xff is consider as rndis candidate, other is virtual Com - - //------------- Communication Interface -------------// - (*p_length) = sizeof(tusb_desc_interface_t); - - // Communication Functional Descriptors - while( TUSB_DESC_CS_INTERFACE == p_desc[DESC_OFFSET_TYPE] ) - { - if ( CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc) ) - { - // save ACM bmCapabilities - p_cdc->acm_capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities; - } - - (*p_length) += p_desc[DESC_OFFSET_LEN]; - p_desc = tu_desc_next(p_desc); - } - - if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE]) - { - // notification endpoint - tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) p_desc; - - TU_ASSERT( hcd_edpt_open(rhport, dev_addr, ep_desc) ); - p_cdc->ep_notif = ep_desc->bEndpointAddress; - - (*p_length) += p_desc[DESC_OFFSET_LEN]; - p_desc = tu_desc_next(p_desc); - } - - //------------- Data Interface (if any) -------------// - if ( (TUSB_DESC_INTERFACE == p_desc[DESC_OFFSET_TYPE]) && - (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) ) - { - (*p_length) += p_desc[DESC_OFFSET_LEN]; - p_desc = tu_desc_next(p_desc); - - // data endpoints expected to be in pairs - for(uint32_t i=0; i<2; i++) - { - tusb_desc_endpoint_t const *ep_desc = (tusb_desc_endpoint_t const *) p_desc; - TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType); - TU_ASSERT(TUSB_XFER_BULK == ep_desc->bmAttributes.xfer); - - TU_ASSERT(hcd_edpt_open(rhport, dev_addr, ep_desc)); - - if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN ) - { - p_cdc->ep_in = ep_desc->bEndpointAddress; - }else - { - p_cdc->ep_out = ep_desc->bEndpointAddress; - } - - (*p_length) += p_desc[DESC_OFFSET_LEN]; - p_desc = tu_desc_next( p_desc ); - } - } - - // FIXME move to seperate API : connect - tusb_control_request_t request = - { - .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT }, - .bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE, - .wValue = 0x03, // dtr on, cst on - .wIndex = p_cdc->itf_num, - .wLength = 0 - }; - - TU_ASSERT( usbh_control_xfer(dev_addr, &request, NULL) ); - - return true; -} - -void cdch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) -{ - (void) ep_addr; - tuh_cdc_xfer_isr( dev_addr, event, 0, xferred_bytes ); -} - -void cdch_close(uint8_t dev_addr) -{ - cdch_data_t * p_cdc = &cdch_data[dev_addr-1]; - tu_memclr(p_cdc, sizeof(cdch_data_t)); -} - -#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_HOST_ENABLED && CFG_TUH_CDC) + +#include "common/tusb_common.h" +#include "cdc_host.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +typedef struct { + uint8_t itf_num; + uint8_t itf_protocol; + + uint8_t ep_notif; + uint8_t ep_in; + uint8_t ep_out; + + cdc_acm_capability_t acm_capability; + +} cdch_data_t; + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ +static cdch_data_t cdch_data[CFG_TUSB_HOST_DEVICE_MAX]; + +bool tuh_cdc_mounted(uint8_t dev_addr) +{ + cdch_data_t* cdc = &cdch_data[dev_addr-1]; + return cdc->ep_in && cdc->ep_out; +} + +bool tuh_cdc_is_busy(uint8_t dev_addr, cdc_pipeid_t pipeid) +{ + if ( !tuh_cdc_mounted(dev_addr) ) return false; + + cdch_data_t const * p_cdc = &cdch_data[dev_addr-1]; + + switch (pipeid) + { + case CDC_PIPE_NOTIFICATION: + return hcd_edpt_busy(dev_addr, p_cdc->ep_notif ); + + case CDC_PIPE_DATA_IN: + return hcd_edpt_busy(dev_addr, p_cdc->ep_in ); + + case CDC_PIPE_DATA_OUT: + return hcd_edpt_busy(dev_addr, p_cdc->ep_out ); + + default: + return false; + } +} + +//--------------------------------------------------------------------+ +// APPLICATION API (parameter validation needed) +//--------------------------------------------------------------------+ +bool tuh_cdc_serial_is_mounted(uint8_t dev_addr) +{ + // TODO consider all AT Command as serial candidate + return tuh_cdc_mounted(dev_addr) && + (CDC_COMM_PROTOCOL_ATCOMMAND <= cdch_data[dev_addr-1].itf_protocol) && + (cdch_data[dev_addr-1].itf_protocol <= CDC_COMM_PROTOCOL_ATCOMMAND_CDMA); +} + +bool tuh_cdc_send(uint8_t dev_addr, void const * p_data, uint32_t length, bool is_notify) +{ + TU_VERIFY( tuh_cdc_mounted(dev_addr) ); + TU_VERIFY( p_data != NULL && length, TUSB_ERROR_INVALID_PARA); + + uint8_t const ep_out = cdch_data[dev_addr-1].ep_out; + if ( hcd_edpt_busy(dev_addr, ep_out) ) return false; + + return hcd_pipe_xfer(dev_addr, ep_out, (void *) p_data, length, is_notify); +} + +bool tuh_cdc_receive(uint8_t dev_addr, void * p_buffer, uint32_t length, bool is_notify) +{ + TU_VERIFY( tuh_cdc_mounted(dev_addr) ); + TU_VERIFY( p_buffer != NULL && length, TUSB_ERROR_INVALID_PARA); + + uint8_t const ep_in = cdch_data[dev_addr-1].ep_in; + if ( hcd_edpt_busy(dev_addr, ep_in) ) return false; + + return hcd_pipe_xfer(dev_addr, ep_in, p_buffer, length, is_notify); +} + +//--------------------------------------------------------------------+ +// USBH-CLASS DRIVER API +//--------------------------------------------------------------------+ +void cdch_init(void) +{ + tu_memclr(cdch_data, sizeof(cdch_data_t)*CFG_TUSB_HOST_DEVICE_MAX); +} + +bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length) +{ + // Only support ACM + TU_VERIFY( CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass); + + // Only support AT commands, no protocol and vendor specific commands. + TU_VERIFY(tu_within(CDC_COMM_PROTOCOL_NONE, itf_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA) || + 0xff == itf_desc->bInterfaceProtocol); + + uint8_t const * p_desc; + cdch_data_t * p_cdc; + + p_desc = tu_desc_next(itf_desc); + p_cdc = &cdch_data[dev_addr-1]; + + p_cdc->itf_num = itf_desc->bInterfaceNumber; + p_cdc->itf_protocol = itf_desc->bInterfaceProtocol; // TODO 0xff is consider as rndis candidate, other is virtual Com + + //------------- Communication Interface -------------// + (*p_length) = sizeof(tusb_desc_interface_t); + + // Communication Functional Descriptors + while( TUSB_DESC_CS_INTERFACE == p_desc[DESC_OFFSET_TYPE] ) + { + if ( CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc) ) + { + // save ACM bmCapabilities + p_cdc->acm_capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities; + } + + (*p_length) += p_desc[DESC_OFFSET_LEN]; + p_desc = tu_desc_next(p_desc); + } + + if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE]) + { + // notification endpoint + tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) p_desc; + + TU_ASSERT( hcd_edpt_open(rhport, dev_addr, ep_desc) ); + p_cdc->ep_notif = ep_desc->bEndpointAddress; + + (*p_length) += p_desc[DESC_OFFSET_LEN]; + p_desc = tu_desc_next(p_desc); + } + + //------------- Data Interface (if any) -------------// + if ( (TUSB_DESC_INTERFACE == p_desc[DESC_OFFSET_TYPE]) && + (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) ) + { + (*p_length) += p_desc[DESC_OFFSET_LEN]; + p_desc = tu_desc_next(p_desc); + + // data endpoints expected to be in pairs + for(uint32_t i=0; i<2; i++) + { + tusb_desc_endpoint_t const *ep_desc = (tusb_desc_endpoint_t const *) p_desc; + TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType); + TU_ASSERT(TUSB_XFER_BULK == ep_desc->bmAttributes.xfer); + + TU_ASSERT(hcd_edpt_open(rhport, dev_addr, ep_desc)); + + if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN ) + { + p_cdc->ep_in = ep_desc->bEndpointAddress; + }else + { + p_cdc->ep_out = ep_desc->bEndpointAddress; + } + + (*p_length) += p_desc[DESC_OFFSET_LEN]; + p_desc = tu_desc_next( p_desc ); + } + } + + // FIXME move to seperate API : connect + tusb_control_request_t request = + { + .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT }, + .bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE, + .wValue = 0x03, // dtr on, cst on + .wIndex = p_cdc->itf_num, + .wLength = 0 + }; + + TU_ASSERT( usbh_control_xfer(dev_addr, &request, NULL) ); + + return true; +} + +void cdch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) +{ + (void) ep_addr; + tuh_cdc_xfer_isr( dev_addr, event, 0, xferred_bytes ); +} + +void cdch_close(uint8_t dev_addr) +{ + cdch_data_t * p_cdc = &cdch_data[dev_addr-1]; + tu_memclr(p_cdc, sizeof(cdch_data_t)); +} + +#endif diff --git a/src/class/cdc/cdc_host.h b/src/class/cdc/cdc_host.h index 59ad2bfa8..306420ce4 100644 --- a/src/class/cdc/cdc_host.h +++ b/src/class/cdc/cdc_host.h @@ -1,123 +1,123 @@ -/* - * 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. - */ - -#ifndef _TUSB_CDC_HOST_H_ -#define _TUSB_CDC_HOST_H_ - -#include "common/tusb_common.h" -#include "host/usbh.h" -#include "cdc.h" - -#ifdef __cplusplus - extern "C" { -#endif - -//--------------------------------------------------------------------+ -// CDC APPLICATION PUBLIC API -//--------------------------------------------------------------------+ -/** \ingroup ClassDriver_CDC Communication Device Class (CDC) - * \addtogroup CDC_Serial Serial - * @{ - * \defgroup CDC_Serial_Host Host - * @{ */ - -/** \brief Check if device support CDC Serial interface or not - * \param[in] dev_addr device address - * \retval true if device supports - * \retval false if device does not support or is not mounted - */ -bool tuh_cdc_serial_is_mounted(uint8_t dev_addr); - -/** \brief Check if the interface is currently busy or not - * \param[in] dev_addr device address - * \param[in] pipeid value from \ref cdc_pipeid_t to indicate target pipe. - * \retval true if the interface is busy, meaning the stack is still transferring/waiting data from/to device - * \retval false if the interface is not busy, meaning the stack successfully transferred data from/to device - * \note This function is used to check if previous transfer is complete (success or error), so that the next transfer - * can be scheduled. User needs to make sure the corresponding interface is mounted - * (by \ref tuh_cdc_serial_is_mounted) before calling this function. - */ -bool tuh_cdc_is_busy(uint8_t dev_addr, cdc_pipeid_t pipeid); - -/** \brief Perform USB OUT transfer to device - * \param[in] dev_addr device address - * \param[in] p_data Buffer containing data. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION) - * \param[in] length Number of bytes to be transferred via USB bus - * \retval TUSB_ERROR_NONE on success - * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device - * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) - * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct - * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the - * interface's callback function. \a p_data must be declared with \ref CFG_TUSB_MEM_SECTION. - */ -bool tuh_cdc_send(uint8_t dev_addr, void const * p_data, uint32_t length, bool is_notify); - -/** \brief Perform USB IN transfer to get data from device - * \param[in] dev_addr device address - * \param[in] p_buffer Buffer containing received data. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION) - * \param[in] length Number of bytes to be transferred via USB bus - * \retval TUSB_ERROR_NONE on success - * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device - * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) - * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct - * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the - * interface's callback function. \a p_data must be declared with \ref CFG_TUSB_MEM_SECTION. - */ -bool tuh_cdc_receive(uint8_t dev_addr, void * p_buffer, uint32_t length, bool is_notify); - -//--------------------------------------------------------------------+ -// CDC APPLICATION CALLBACKS -//--------------------------------------------------------------------+ - -/** \brief Callback function that is invoked when an transferring event occurred - * \param[in] dev_addr Address of device - * \param[in] event an value from \ref xfer_result_t - * \param[in] pipe_id value from \ref cdc_pipeid_t indicate the pipe - * \param[in] xferred_bytes Number of bytes transferred via USB bus - * \note event can be one of following - * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully. - * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error. - * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device. - * \note - */ -void tuh_cdc_xfer_isr(uint8_t dev_addr, xfer_result_t event, cdc_pipeid_t pipe_id, uint32_t xferred_bytes); - -/// @} // group CDC_Serial_Host -/// @} - -//--------------------------------------------------------------------+ -// Internal Class Driver API -//--------------------------------------------------------------------+ -void cdch_init(void); -bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length); -void cdch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); -void cdch_close(uint8_t dev_addr); - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_CDC_HOST_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. + */ + +#ifndef _TUSB_CDC_HOST_H_ +#define _TUSB_CDC_HOST_H_ + +#include "common/tusb_common.h" +#include "host/usbh.h" +#include "cdc.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// CDC APPLICATION PUBLIC API +//--------------------------------------------------------------------+ +/** \ingroup ClassDriver_CDC Communication Device Class (CDC) + * \addtogroup CDC_Serial Serial + * @{ + * \defgroup CDC_Serial_Host Host + * @{ */ + +/** \brief Check if device support CDC Serial interface or not + * \param[in] dev_addr device address + * \retval true if device supports + * \retval false if device does not support or is not mounted + */ +bool tuh_cdc_serial_is_mounted(uint8_t dev_addr); + +/** \brief Check if the interface is currently busy or not + * \param[in] dev_addr device address + * \param[in] pipeid value from \ref cdc_pipeid_t to indicate target pipe. + * \retval true if the interface is busy, meaning the stack is still transferring/waiting data from/to device + * \retval false if the interface is not busy, meaning the stack successfully transferred data from/to device + * \note This function is used to check if previous transfer is complete (success or error), so that the next transfer + * can be scheduled. User needs to make sure the corresponding interface is mounted + * (by \ref tuh_cdc_serial_is_mounted) before calling this function. + */ +bool tuh_cdc_is_busy(uint8_t dev_addr, cdc_pipeid_t pipeid); + +/** \brief Perform USB OUT transfer to device + * \param[in] dev_addr device address + * \param[in] p_data Buffer containing data. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION) + * \param[in] length Number of bytes to be transferred via USB bus + * \retval TUSB_ERROR_NONE on success + * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device + * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) + * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct + * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the + * interface's callback function. \a p_data must be declared with \ref CFG_TUSB_MEM_SECTION. + */ +bool tuh_cdc_send(uint8_t dev_addr, void const * p_data, uint32_t length, bool is_notify); + +/** \brief Perform USB IN transfer to get data from device + * \param[in] dev_addr device address + * \param[in] p_buffer Buffer containing received data. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION) + * \param[in] length Number of bytes to be transferred via USB bus + * \retval TUSB_ERROR_NONE on success + * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device + * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) + * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct + * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the + * interface's callback function. \a p_data must be declared with \ref CFG_TUSB_MEM_SECTION. + */ +bool tuh_cdc_receive(uint8_t dev_addr, void * p_buffer, uint32_t length, bool is_notify); + +//--------------------------------------------------------------------+ +// CDC APPLICATION CALLBACKS +//--------------------------------------------------------------------+ + +/** \brief Callback function that is invoked when an transferring event occurred + * \param[in] dev_addr Address of device + * \param[in] event an value from \ref xfer_result_t + * \param[in] pipe_id value from \ref cdc_pipeid_t indicate the pipe + * \param[in] xferred_bytes Number of bytes transferred via USB bus + * \note event can be one of following + * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully. + * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error. + * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device. + * \note + */ +void tuh_cdc_xfer_isr(uint8_t dev_addr, xfer_result_t event, cdc_pipeid_t pipe_id, uint32_t xferred_bytes); + +/// @} // group CDC_Serial_Host +/// @} + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +void cdch_init(void); +bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length); +void cdch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); +void cdch_close(uint8_t dev_addr); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CDC_HOST_H_ */ diff --git a/src/class/hid/hid.h b/src/class/hid/hid.h index 7fa4aa8ab..848274037 100644 --- a/src/class/hid/hid.h +++ b/src/class/hid/hid.h @@ -1,873 +1,873 @@ -/* - * 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_class - * \defgroup ClassDriver_HID Human Interface Device (HID) - * @{ */ - -#ifndef _TUSB_HID_H_ -#define _TUSB_HID_H_ - -#include "common/tusb_common.h" - -#ifdef __cplusplus - extern "C" { -#endif - -//--------------------------------------------------------------------+ -// Common Definitions -//--------------------------------------------------------------------+ -/** \defgroup ClassDriver_HID_Common Common Definitions - * @{ */ - - /// USB HID Descriptor -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength; /**< Numeric expression that is the total size of the HID descriptor */ - uint8_t bDescriptorType; /**< Constant name specifying type of HID descriptor. */ - - uint16_t bcdHID; /**< Numeric expression identifying the HID Class Specification release */ - uint8_t bCountryCode; /**< Numeric expression identifying country code of the localized hardware. */ - uint8_t bNumDescriptors; /**< Numeric expression specifying the number of class descriptors */ - - uint8_t bReportType; /**< Type of HID class report. */ - uint16_t wReportLength; /**< the total size of the Report descriptor. */ -} tusb_hid_descriptor_hid_t; - -/// HID Subclass -typedef enum -{ - HID_SUBCLASS_NONE = 0, ///< No Subclass - HID_SUBCLASS_BOOT = 1 ///< Boot Interface Subclass -}hid_subclass_type_t; - -/// HID Protocol -typedef enum -{ - HID_PROTOCOL_NONE = 0, ///< None - HID_PROTOCOL_KEYBOARD = 1, ///< Keyboard - HID_PROTOCOL_MOUSE = 2 ///< Mouse -}hid_protocol_type_t; - -/// HID Descriptor Type -typedef enum -{ - HID_DESC_TYPE_HID = 0x21, ///< HID Descriptor - HID_DESC_TYPE_REPORT = 0x22, ///< Report Descriptor - HID_DESC_TYPE_PHYSICAL = 0x23 ///< Physical Descriptor -}hid_descriptor_type_t; - -/// HID Request Report Type -typedef enum -{ - HID_REPORT_TYPE_INVALID = 0, - HID_REPORT_TYPE_INPUT, ///< Input - HID_REPORT_TYPE_OUTPUT, ///< Output - HID_REPORT_TYPE_FEATURE ///< Feature -}hid_report_type_t; - -/// HID Class Specific Control Request -typedef enum -{ - HID_REQ_CONTROL_GET_REPORT = 0x01, ///< Get Report - HID_REQ_CONTROL_GET_IDLE = 0x02, ///< Get Idle - HID_REQ_CONTROL_GET_PROTOCOL = 0x03, ///< Get Protocol - HID_REQ_CONTROL_SET_REPORT = 0x09, ///< Set Report - HID_REQ_CONTROL_SET_IDLE = 0x0a, ///< Set Idle - HID_REQ_CONTROL_SET_PROTOCOL = 0x0b ///< Set Protocol -}hid_request_type_t; - -/// HID Country Code -typedef enum -{ - HID_LOCAL_NotSupported = 0 , ///< NotSupported - HID_LOCAL_Arabic , ///< Arabic - HID_LOCAL_Belgian , ///< Belgian - HID_LOCAL_Canadian_Bilingual , ///< Canadian_Bilingual - HID_LOCAL_Canadian_French , ///< Canadian_French - HID_LOCAL_Czech_Republic , ///< Czech_Republic - HID_LOCAL_Danish , ///< Danish - HID_LOCAL_Finnish , ///< Finnish - HID_LOCAL_French , ///< French - HID_LOCAL_German , ///< German - HID_LOCAL_Greek , ///< Greek - HID_LOCAL_Hebrew , ///< Hebrew - HID_LOCAL_Hungary , ///< Hungary - HID_LOCAL_International , ///< International - HID_LOCAL_Italian , ///< Italian - HID_LOCAL_Japan_Katakana , ///< Japan_Katakana - HID_LOCAL_Korean , ///< Korean - HID_LOCAL_Latin_American , ///< Latin_American - HID_LOCAL_Netherlands_Dutch , ///< Netherlands/Dutch - HID_LOCAL_Norwegian , ///< Norwegian - HID_LOCAL_Persian_Farsi , ///< Persian (Farsi) - HID_LOCAL_Poland , ///< Poland - HID_LOCAL_Portuguese , ///< Portuguese - HID_LOCAL_Russia , ///< Russia - HID_LOCAL_Slovakia , ///< Slovakia - HID_LOCAL_Spanish , ///< Spanish - HID_LOCAL_Swedish , ///< Swedish - HID_LOCAL_Swiss_French , ///< Swiss/French - HID_LOCAL_Swiss_German , ///< Swiss/German - HID_LOCAL_Switzerland , ///< Switzerland - HID_LOCAL_Taiwan , ///< Taiwan - HID_LOCAL_Turkish_Q , ///< Turkish-Q - HID_LOCAL_UK , ///< UK - HID_LOCAL_US , ///< US - HID_LOCAL_Yugoslavia , ///< Yugoslavia - HID_LOCAL_Turkish_F ///< Turkish-F -} hid_country_code_t; - -/** @} */ - -//--------------------------------------------------------------------+ -// MOUSE -//--------------------------------------------------------------------+ -/** \addtogroup ClassDriver_HID_Mouse Mouse - * @{ */ - -/// Standard HID Boot Protocol Mouse Report. -typedef struct TU_ATTR_PACKED -{ - uint8_t buttons; /**< buttons mask for currently pressed buttons in the mouse. */ - int8_t x; /**< Current delta x movement of the mouse. */ - int8_t y; /**< Current delta y movement on the mouse. */ - int8_t wheel; /**< Current delta wheel movement on the mouse. */ - int8_t pan; // using AC Pan -} hid_mouse_report_t; - -/// Standard Mouse Buttons Bitmap -typedef enum -{ - MOUSE_BUTTON_LEFT = TU_BIT(0), ///< Left button - MOUSE_BUTTON_RIGHT = TU_BIT(1), ///< Right button - MOUSE_BUTTON_MIDDLE = TU_BIT(2), ///< Middle button - MOUSE_BUTTON_BACKWARD = TU_BIT(3), ///< Backward button, - MOUSE_BUTTON_FORWARD = TU_BIT(4), ///< Forward button, -}hid_mouse_button_bm_t; - -/// @} - -//--------------------------------------------------------------------+ -// Keyboard -//--------------------------------------------------------------------+ -/** \addtogroup ClassDriver_HID_Keyboard Keyboard - * @{ */ - -/// Standard HID Boot Protocol Keyboard Report. -typedef struct TU_ATTR_PACKED -{ - uint8_t modifier; /**< Keyboard modifier (KEYBOARD_MODIFIER_* masks). */ - uint8_t reserved; /**< Reserved for OEM use, always set to 0. */ - uint8_t keycode[6]; /**< Key codes of the currently pressed keys. */ -} hid_keyboard_report_t; - -/// Keyboard modifier codes bitmap -typedef enum -{ - KEYBOARD_MODIFIER_LEFTCTRL = TU_BIT(0), ///< Left Control - KEYBOARD_MODIFIER_LEFTSHIFT = TU_BIT(1), ///< Left Shift - KEYBOARD_MODIFIER_LEFTALT = TU_BIT(2), ///< Left Alt - KEYBOARD_MODIFIER_LEFTGUI = TU_BIT(3), ///< Left Window - KEYBOARD_MODIFIER_RIGHTCTRL = TU_BIT(4), ///< Right Control - KEYBOARD_MODIFIER_RIGHTSHIFT = TU_BIT(5), ///< Right Shift - KEYBOARD_MODIFIER_RIGHTALT = TU_BIT(6), ///< Right Alt - KEYBOARD_MODIFIER_RIGHTGUI = TU_BIT(7) ///< Right Window -}hid_keyboard_modifier_bm_t; - -typedef enum -{ - KEYBOARD_LED_NUMLOCK = TU_BIT(0), ///< Num Lock LED - KEYBOARD_LED_CAPSLOCK = TU_BIT(1), ///< Caps Lock LED - KEYBOARD_LED_SCROLLLOCK = TU_BIT(2), ///< Scroll Lock LED - KEYBOARD_LED_COMPOSE = TU_BIT(3), ///< Composition Mode - KEYBOARD_LED_KANA = TU_BIT(4) ///< Kana mode -}hid_keyboard_led_bm_t; - -/// @} - -//--------------------------------------------------------------------+ -// HID KEYCODE -//--------------------------------------------------------------------+ -#define HID_KEY_NONE 0x00 -#define HID_KEY_A 0x04 -#define HID_KEY_B 0x05 -#define HID_KEY_C 0x06 -#define HID_KEY_D 0x07 -#define HID_KEY_E 0x08 -#define HID_KEY_F 0x09 -#define HID_KEY_G 0x0A -#define HID_KEY_H 0x0B -#define HID_KEY_I 0x0C -#define HID_KEY_J 0x0D -#define HID_KEY_K 0x0E -#define HID_KEY_L 0x0F -#define HID_KEY_M 0x10 -#define HID_KEY_N 0x11 -#define HID_KEY_O 0x12 -#define HID_KEY_P 0x13 -#define HID_KEY_Q 0x14 -#define HID_KEY_R 0x15 -#define HID_KEY_S 0x16 -#define HID_KEY_T 0x17 -#define HID_KEY_U 0x18 -#define HID_KEY_V 0x19 -#define HID_KEY_W 0x1A -#define HID_KEY_X 0x1B -#define HID_KEY_Y 0x1C -#define HID_KEY_Z 0x1D -#define HID_KEY_1 0x1E -#define HID_KEY_2 0x1F -#define HID_KEY_3 0x20 -#define HID_KEY_4 0x21 -#define HID_KEY_5 0x22 -#define HID_KEY_6 0x23 -#define HID_KEY_7 0x24 -#define HID_KEY_8 0x25 -#define HID_KEY_9 0x26 -#define HID_KEY_0 0x27 -#define HID_KEY_RETURN 0x28 -#define HID_KEY_ESCAPE 0x29 -#define HID_KEY_BACKSPACE 0x2A -#define HID_KEY_TAB 0x2B -#define HID_KEY_SPACE 0x2C -#define HID_KEY_MINUS 0x2D -#define HID_KEY_EQUAL 0x2E -#define HID_KEY_BRACKET_LEFT 0x2F -#define HID_KEY_BRACKET_RIGHT 0x30 -#define HID_KEY_BACKSLASH 0x31 -#define HID_KEY_EUROPE_1 0x32 -#define HID_KEY_SEMICOLON 0x33 -#define HID_KEY_APOSTROPHE 0x34 -#define HID_KEY_GRAVE 0x35 -#define HID_KEY_COMMA 0x36 -#define HID_KEY_PERIOD 0x37 -#define HID_KEY_SLASH 0x38 -#define HID_KEY_CAPS_LOCK 0x39 -#define HID_KEY_F1 0x3A -#define HID_KEY_F2 0x3B -#define HID_KEY_F3 0x3C -#define HID_KEY_F4 0x3D -#define HID_KEY_F5 0x3E -#define HID_KEY_F6 0x3F -#define HID_KEY_F7 0x40 -#define HID_KEY_F8 0x41 -#define HID_KEY_F9 0x42 -#define HID_KEY_F10 0x43 -#define HID_KEY_F11 0x44 -#define HID_KEY_F12 0x45 -#define HID_KEY_PRINT_SCREEN 0x46 -#define HID_KEY_SCROLL_LOCK 0x47 -#define HID_KEY_PAUSE 0x48 -#define HID_KEY_INSERT 0x49 -#define HID_KEY_HOME 0x4A -#define HID_KEY_PAGE_UP 0x4B -#define HID_KEY_DELETE 0x4C -#define HID_KEY_END 0x4D -#define HID_KEY_PAGE_DOWN 0x4E -#define HID_KEY_ARROW_RIGHT 0x4F -#define HID_KEY_ARROW_LEFT 0x50 -#define HID_KEY_ARROW_DOWN 0x51 -#define HID_KEY_ARROW_UP 0x52 -#define HID_KEY_NUM_LOCK 0x53 -#define HID_KEY_KEYPAD_DIVIDE 0x54 -#define HID_KEY_KEYPAD_MULTIPLY 0x55 -#define HID_KEY_KEYPAD_SUBTRACT 0x56 -#define HID_KEY_KEYPAD_ADD 0x57 -#define HID_KEY_KEYPAD_ENTER 0x58 -#define HID_KEY_KEYPAD_1 0x59 -#define HID_KEY_KEYPAD_2 0x5A -#define HID_KEY_KEYPAD_3 0x5B -#define HID_KEY_KEYPAD_4 0x5C -#define HID_KEY_KEYPAD_5 0x5D -#define HID_KEY_KEYPAD_6 0x5E -#define HID_KEY_KEYPAD_7 0x5F -#define HID_KEY_KEYPAD_8 0x60 -#define HID_KEY_KEYPAD_9 0x61 -#define HID_KEY_KEYPAD_0 0x62 -#define HID_KEY_KEYPAD_DECIMAL 0x63 -#define HID_KEY_EUROPE_2 0x64 -#define HID_KEY_APPLICATION 0x65 -#define HID_KEY_POWER 0x66 -#define HID_KEY_KEYPAD_EQUAL 0x67 -#define HID_KEY_F13 0x68 -#define HID_KEY_F14 0x69 -#define HID_KEY_F15 0x6A -#define HID_KEY_CONTROL_LEFT 0xE0 -#define HID_KEY_SHIFT_LEFT 0xE1 -#define HID_KEY_ALT_LEFT 0xE2 -#define HID_KEY_GUI_LEFT 0xE3 -#define HID_KEY_CONTROL_RIGHT 0xE4 -#define HID_KEY_SHIFT_RIGHT 0xE5 -#define HID_KEY_ALT_RIGHT 0xE6 -#define HID_KEY_GUI_RIGHT 0xE7 - - -//--------------------------------------------------------------------+ -// REPORT DESCRIPTOR -//--------------------------------------------------------------------+ -//------------- ITEM & TAG -------------// -#define HID_REPORT_DATA_0(data) -#define HID_REPORT_DATA_1(data) , data -#define HID_REPORT_DATA_2(data) , U16_TO_U8S_LE(data) -#define HID_REPORT_DATA_3(data) , U32_TO_U8S_LE(data) - -#define HID_REPORT_ITEM(data, tag, type, size) \ - (((tag) << 4) | ((type) << 2) | (size)) HID_REPORT_DATA_##size(data) - -#define RI_TYPE_MAIN 0 -#define RI_TYPE_GLOBAL 1 -#define RI_TYPE_LOCAL 2 - -//------------- MAIN ITEMS 6.2.2.4 -------------// -#define HID_INPUT(x) HID_REPORT_ITEM(x, 8, RI_TYPE_MAIN, 1) -#define HID_OUTPUT(x) HID_REPORT_ITEM(x, 9, RI_TYPE_MAIN, 1) -#define HID_COLLECTION(x) HID_REPORT_ITEM(x, 10, RI_TYPE_MAIN, 1) -#define HID_FEATURE(x) HID_REPORT_ITEM(x, 11, RI_TYPE_MAIN, 1) -#define HID_COLLECTION_END HID_REPORT_ITEM(x, 12, RI_TYPE_MAIN, 0) - -//------------- INPUT, OUTPUT, FEATURE 6.2.2.5 -------------// -#define HID_DATA (0<<0) -#define HID_CONSTANT (1<<0) - -#define HID_ARRAY (0<<1) -#define HID_VARIABLE (1<<1) - -#define HID_ABSOLUTE (0<<2) -#define HID_RELATIVE (1<<2) - -#define HID_WRAP_NO (0<<3) -#define HID_WRAP (1<<3) - -#define HID_LINEAR (0<<4) -#define HID_NONLINEAR (1<<4) - -#define HID_PREFERRED_STATE (0<<5) -#define HID_PREFERRED_NO (1<<5) - -#define HID_NO_NULL_POSITION (0<<6) -#define HID_NULL_STATE (1<<6) - -#define HID_NON_VOLATILE (0<<7) -#define HID_VOLATILE (1<<7) - -#define HID_BITFIELD (0<<8) -#define HID_BUFFERED_BYTES (1<<8) - -//------------- COLLECTION ITEM 6.2.2.6 -------------// -enum { - HID_COLLECTION_PHYSICAL = 0, - HID_COLLECTION_APPLICATION, - HID_COLLECTION_LOGICAL, - HID_COLLECTION_REPORT, - HID_COLLECTION_NAMED_ARRAY, - HID_COLLECTION_USAGE_SWITCH, - HID_COLLECTION_USAGE_MODIFIER -}; - -//------------- GLOBAL ITEMS 6.2.2.7 -------------// -#define HID_USAGE_PAGE(x) HID_REPORT_ITEM(x, 0, RI_TYPE_GLOBAL, 1) -#define HID_USAGE_PAGE_N(x, n) HID_REPORT_ITEM(x, 0, RI_TYPE_GLOBAL, n) - -#define HID_LOGICAL_MIN(x) HID_REPORT_ITEM(x, 1, RI_TYPE_GLOBAL, 1) -#define HID_LOGICAL_MIN_N(x, n) HID_REPORT_ITEM(x, 1, RI_TYPE_GLOBAL, n) - -#define HID_LOGICAL_MAX(x) HID_REPORT_ITEM(x, 2, RI_TYPE_GLOBAL, 1) -#define HID_LOGICAL_MAX_N(x, n) HID_REPORT_ITEM(x, 2, RI_TYPE_GLOBAL, n) - -#define HID_PHYSICAL_MIN(x) HID_REPORT_ITEM(x, 3, RI_TYPE_GLOBAL, 1) -#define HID_PHYSICAL_MIN_N(x, n) HID_REPORT_ITEM(x, 3, RI_TYPE_GLOBAL, n) - -#define HID_PHYSICAL_MAX(x) HID_REPORT_ITEM(x, 4, RI_TYPE_GLOBAL, 1) -#define HID_PHYSICAL_MAX_N(x, n) HID_REPORT_ITEM(x, 4, RI_TYPE_GLOBAL, n) - -#define HID_UNIT_EXPONENT(x) HID_REPORT_ITEM(x, 5, RI_TYPE_GLOBAL, 1) -#define HID_UNIT_EXPONENT_N(x, n) HID_REPORT_ITEM(x, 5, RI_TYPE_GLOBAL, n) - -#define HID_UNIT(x) HID_REPORT_ITEM(x, 6, RI_TYPE_GLOBAL, 1) -#define HID_UNIT_N(x, n) HID_REPORT_ITEM(x, 6, RI_TYPE_GLOBAL, n) - -#define HID_REPORT_SIZE(x) HID_REPORT_ITEM(x, 7, RI_TYPE_GLOBAL, 1) -#define HID_REPORT_SIZE_N(x, n) HID_REPORT_ITEM(x, 7, RI_TYPE_GLOBAL, n) - -#define HID_REPORT_ID(x) HID_REPORT_ITEM(x, 8, RI_TYPE_GLOBAL, 1) -#define HID_REPORT_ID_N(x) HID_REPORT_ITEM(x, 8, RI_TYPE_GLOBAL, n) - -#define HID_REPORT_COUNT(x) HID_REPORT_ITEM(x, 9, RI_TYPE_GLOBAL, 1) -#define HID_REPORT_COUNT_N(x, n) HID_REPORT_ITEM(x, 9, RI_TYPE_GLOBAL, n) - -#define HID_PUSH HID_REPORT_ITEM(x, 10, RI_TYPE_GLOBAL, 0) -#define HID_POP HID_REPORT_ITEM(x, 11, RI_TYPE_GLOBAL, 0) - -//------------- LOCAL ITEMS 6.2.2.8 -------------// -#define HID_USAGE(x) HID_REPORT_ITEM(x, 0, RI_TYPE_LOCAL, 1) -#define HID_USAGE_N(x, n) HID_REPORT_ITEM(x, 0, RI_TYPE_LOCAL, n) - -#define HID_USAGE_MIN(x) HID_REPORT_ITEM(x, 1, RI_TYPE_LOCAL, 1) -#define HID_USAGE_MIN_N(x, n) HID_REPORT_ITEM(x, 1, RI_TYPE_LOCAL, n) - -#define HID_USAGE_MAX(x) HID_REPORT_ITEM(x, 2, RI_TYPE_LOCAL, 1) -#define HID_USAGE_MAX_N(x, n) HID_REPORT_ITEM(x, 2, RI_TYPE_LOCAL, n) - -//--------------------------------------------------------------------+ -// Usage Table -//--------------------------------------------------------------------+ - -/// HID Usage Table - Table 1: Usage Page Summary -enum { - HID_USAGE_PAGE_DESKTOP = 0x01, - HID_USAGE_PAGE_SIMULATE = 0x02, - HID_USAGE_PAGE_VIRTUAL_REALITY = 0x03, - HID_USAGE_PAGE_SPORT = 0x04, - HID_USAGE_PAGE_GAME = 0x05, - HID_USAGE_PAGE_GENERIC_DEVICE = 0x06, - HID_USAGE_PAGE_KEYBOARD = 0x07, - HID_USAGE_PAGE_LED = 0x08, - HID_USAGE_PAGE_BUTTON = 0x09, - HID_USAGE_PAGE_ORDINAL = 0x0a, - HID_USAGE_PAGE_TELEPHONY = 0x0b, - HID_USAGE_PAGE_CONSUMER = 0x0c, - HID_USAGE_PAGE_DIGITIZER = 0x0d, - HID_USAGE_PAGE_PID = 0x0f, - HID_USAGE_PAGE_UNICODE = 0x10, - HID_USAGE_PAGE_ALPHA_DISPLAY = 0x14, - HID_USAGE_PAGE_MEDICAL = 0x40, - HID_USAGE_PAGE_MONITOR = 0x80, //0x80 - 0x83 - HID_USAGE_PAGE_POWER = 0x84, // 0x084 - 0x87 - HID_USAGE_PAGE_BARCODE_SCANNER = 0x8c, - HID_USAGE_PAGE_SCALE = 0x8d, - HID_USAGE_PAGE_MSR = 0x8e, - HID_USAGE_PAGE_CAMERA = 0x90, - HID_USAGE_PAGE_ARCADE = 0x91, - HID_USAGE_PAGE_VENDOR = 0xFF00 // 0xFF00 - 0xFFFF -}; - -/// HID Usage Table - Table 6: Generic Desktop Page -enum { - HID_USAGE_DESKTOP_POINTER = 0x01, - HID_USAGE_DESKTOP_MOUSE = 0x02, - HID_USAGE_DESKTOP_JOYSTICK = 0x04, - HID_USAGE_DESKTOP_GAMEPAD = 0x05, - HID_USAGE_DESKTOP_KEYBOARD = 0x06, - HID_USAGE_DESKTOP_KEYPAD = 0x07, - HID_USAGE_DESKTOP_MULTI_AXIS_CONTROLLER = 0x08, - HID_USAGE_DESKTOP_TABLET_PC_SYSTEM = 0x09, - HID_USAGE_DESKTOP_X = 0x30, - HID_USAGE_DESKTOP_Y = 0x31, - HID_USAGE_DESKTOP_Z = 0x32, - HID_USAGE_DESKTOP_RX = 0x33, - HID_USAGE_DESKTOP_RY = 0x34, - HID_USAGE_DESKTOP_RZ = 0x35, - HID_USAGE_DESKTOP_SLIDER = 0x36, - HID_USAGE_DESKTOP_DIAL = 0x37, - HID_USAGE_DESKTOP_WHEEL = 0x38, - HID_USAGE_DESKTOP_HAT_SWITCH = 0x39, - HID_USAGE_DESKTOP_COUNTED_BUFFER = 0x3a, - HID_USAGE_DESKTOP_BYTE_COUNT = 0x3b, - HID_USAGE_DESKTOP_MOTION_WAKEUP = 0x3c, - HID_USAGE_DESKTOP_START = 0x3d, - HID_USAGE_DESKTOP_SELECT = 0x3e, - HID_USAGE_DESKTOP_VX = 0x40, - HID_USAGE_DESKTOP_VY = 0x41, - HID_USAGE_DESKTOP_VZ = 0x42, - HID_USAGE_DESKTOP_VBRX = 0x43, - HID_USAGE_DESKTOP_VBRY = 0x44, - HID_USAGE_DESKTOP_VBRZ = 0x45, - HID_USAGE_DESKTOP_VNO = 0x46, - HID_USAGE_DESKTOP_FEATURE_NOTIFICATION = 0x47, - HID_USAGE_DESKTOP_RESOLUTION_MULTIPLIER = 0x48, - HID_USAGE_DESKTOP_SYSTEM_CONTROL = 0x80, - HID_USAGE_DESKTOP_SYSTEM_POWER_DOWN = 0x81, - HID_USAGE_DESKTOP_SYSTEM_SLEEP = 0x82, - HID_USAGE_DESKTOP_SYSTEM_WAKE_UP = 0x83, - HID_USAGE_DESKTOP_SYSTEM_CONTEXT_MENU = 0x84, - HID_USAGE_DESKTOP_SYSTEM_MAIN_MENU = 0x85, - HID_USAGE_DESKTOP_SYSTEM_APP_MENU = 0x86, - HID_USAGE_DESKTOP_SYSTEM_MENU_HELP = 0x87, - HID_USAGE_DESKTOP_SYSTEM_MENU_EXIT = 0x88, - HID_USAGE_DESKTOP_SYSTEM_MENU_SELECT = 0x89, - HID_USAGE_DESKTOP_SYSTEM_MENU_RIGHT = 0x8A, - HID_USAGE_DESKTOP_SYSTEM_MENU_LEFT = 0x8B, - HID_USAGE_DESKTOP_SYSTEM_MENU_UP = 0x8C, - HID_USAGE_DESKTOP_SYSTEM_MENU_DOWN = 0x8D, - HID_USAGE_DESKTOP_SYSTEM_COLD_RESTART = 0x8E, - HID_USAGE_DESKTOP_SYSTEM_WARM_RESTART = 0x8F, - HID_USAGE_DESKTOP_DPAD_UP = 0x90, - HID_USAGE_DESKTOP_DPAD_DOWN = 0x91, - HID_USAGE_DESKTOP_DPAD_RIGHT = 0x92, - HID_USAGE_DESKTOP_DPAD_LEFT = 0x93, - HID_USAGE_DESKTOP_SYSTEM_DOCK = 0xA0, - HID_USAGE_DESKTOP_SYSTEM_UNDOCK = 0xA1, - HID_USAGE_DESKTOP_SYSTEM_SETUP = 0xA2, - HID_USAGE_DESKTOP_SYSTEM_BREAK = 0xA3, - HID_USAGE_DESKTOP_SYSTEM_DEBUGGER_BREAK = 0xA4, - HID_USAGE_DESKTOP_APPLICATION_BREAK = 0xA5, - HID_USAGE_DESKTOP_APPLICATION_DEBUGGER_BREAK = 0xA6, - HID_USAGE_DESKTOP_SYSTEM_SPEAKER_MUTE = 0xA7, - HID_USAGE_DESKTOP_SYSTEM_HIBERNATE = 0xA8, - HID_USAGE_DESKTOP_SYSTEM_DISPLAY_INVERT = 0xB0, - HID_USAGE_DESKTOP_SYSTEM_DISPLAY_INTERNAL = 0xB1, - HID_USAGE_DESKTOP_SYSTEM_DISPLAY_EXTERNAL = 0xB2, - HID_USAGE_DESKTOP_SYSTEM_DISPLAY_BOTH = 0xB3, - HID_USAGE_DESKTOP_SYSTEM_DISPLAY_DUAL = 0xB4, - HID_USAGE_DESKTOP_SYSTEM_DISPLAY_TOGGLE_INT_EXT = 0xB5, - HID_USAGE_DESKTOP_SYSTEM_DISPLAY_SWAP_PRIMARY_SECONDARY = 0xB6, - HID_USAGE_DESKTOP_SYSTEM_DISPLAY_LCD_AUTOSCALE = 0xB7 -}; - - -/// HID Usage Table: Consumer Page (0x0C) -/// Only contains controls that supported by Windows (whole list is too long) -enum -{ - // Generic Control - HID_USAGE_CONSUMER_CONTROL = 0x0001, - - // Power Control - HID_USAGE_CONSUMER_POWER = 0x0030, - HID_USAGE_CONSUMER_RESET = 0x0031, - HID_USAGE_CONSUMER_SLEEP = 0x0032, - - // Screen Brightness - HID_USAGE_CONSUMER_BRIGHTNESS_INCREMENT = 0x006F, - HID_USAGE_CONSUMER_BRIGHTNESS_DECREMENT = 0x0070, - - // These HID usages operate only on mobile systems (battery powered) and - // require Windows 8 (build 8302 or greater). - HID_USAGE_CONSUMER_WIRELESS_RADIO_CONTROLS = 0x000C, - HID_USAGE_CONSUMER_WIRELESS_RADIO_BUTTONS = 0x00C6, - HID_USAGE_CONSUMER_WIRELESS_RADIO_LED = 0x00C7, - HID_USAGE_CONSUMER_WIRELESS_RADIO_SLIDER_SWITCH = 0x00C8, - - // Media Control - HID_USAGE_CONSUMER_PLAY_PAUSE = 0x00CD, - HID_USAGE_CONSUMER_SCAN_NEXT = 0x00B5, - HID_USAGE_CONSUMER_SCAN_PREVIOUS = 0x00B6, - HID_USAGE_CONSUMER_STOP = 0x00B7, - HID_USAGE_CONSUMER_VOLUME = 0x00E0, - HID_USAGE_CONSUMER_MUTE = 0x00E2, - HID_USAGE_CONSUMER_BASS = 0x00E3, - HID_USAGE_CONSUMER_TREBLE = 0x00E4, - HID_USAGE_CONSUMER_BASS_BOOST = 0x00E5, - HID_USAGE_CONSUMER_VOLUME_INCREMENT = 0x00E9, - HID_USAGE_CONSUMER_VOLUME_DECREMENT = 0x00EA, - HID_USAGE_CONSUMER_BASS_INCREMENT = 0x0152, - HID_USAGE_CONSUMER_BASS_DECREMENT = 0x0153, - HID_USAGE_CONSUMER_TREBLE_INCREMENT = 0x0154, - HID_USAGE_CONSUMER_TREBLE_DECREMENT = 0x0155, - - // Application Launcher - HID_USAGE_CONSUMER_AL_CONSUMER_CONTROL_CONFIGURATION = 0x0183, - HID_USAGE_CONSUMER_AL_EMAIL_READER = 0x018A, - HID_USAGE_CONSUMER_AL_CALCULATOR = 0x0192, - HID_USAGE_CONSUMER_AL_LOCAL_BROWSER = 0x0194, - - // Browser/Explorer Specific - HID_USAGE_CONSUMER_AC_SEARCH = 0x0221, - HID_USAGE_CONSUMER_AC_HOME = 0x0223, - HID_USAGE_CONSUMER_AC_BACK = 0x0224, - HID_USAGE_CONSUMER_AC_FORWARD = 0x0225, - HID_USAGE_CONSUMER_AC_STOP = 0x0226, - HID_USAGE_CONSUMER_AC_REFRESH = 0x0227, - HID_USAGE_CONSUMER_AC_BOOKMARKS = 0x022A, - - // Mouse Horizontal scroll - HID_USAGE_CONSUMER_AC_PAN = 0x0238, -}; - -/*-------------------------------------------------------------------- - * ASCII to KEYCODE Conversion - * Expand to array of [128][2] (shift, keycode) - * - * Usage: example to convert input chr into keyboard report (modifier + keycode) - * - * uint8_t const conv_table[128][2] = { HID_ASCII_TO_KEYCODE }; - * - * uint8_t keycode[6] = { 0 }; - * uint8_t modifier = 0; - * - * if ( conv_table[chr][0] ) modifier = KEYBOARD_MODIFIER_LEFTSHIFT; - * keycode[0] = conv_table[chr][1]; - * tud_hid_keyboard_report(report_id, modifier, keycode); - * - *--------------------------------------------------------------------*/ -#define HID_ASCII_TO_KEYCODE \ - {0, 0 }, /* 0x00 Null */ \ - {0, 0 }, /* 0x01 */ \ - {0, 0 }, /* 0x02 */ \ - {0, 0 }, /* 0x03 */ \ - {0, 0 }, /* 0x04 */ \ - {0, 0 }, /* 0x05 */ \ - {0, 0 }, /* 0x06 */ \ - {0, 0 }, /* 0x07 */ \ - {0, HID_KEY_BACKSPACE }, /* 0x08 Backspace */ \ - {0, HID_KEY_TAB }, /* 0x09 Tab */ \ - {0, HID_KEY_RETURN }, /* 0x0A Line Feed */ \ - {0, 0 }, /* 0x0B */ \ - {0, 0 }, /* 0x0C */ \ - {0, HID_KEY_RETURN }, /* 0x0D CR */ \ - {0, 0 }, /* 0x0E */ \ - {0, 0 }, /* 0x0F */ \ - {0, 0 }, /* 0x10 */ \ - {0, 0 }, /* 0x11 */ \ - {0, 0 }, /* 0x12 */ \ - {0, 0 }, /* 0x13 */ \ - {0, 0 }, /* 0x14 */ \ - {0, 0 }, /* 0x15 */ \ - {0, 0 }, /* 0x16 */ \ - {0, 0 }, /* 0x17 */ \ - {0, 0 }, /* 0x18 */ \ - {0, 0 }, /* 0x19 */ \ - {0, 0 }, /* 0x1A */ \ - {0, HID_KEY_ESCAPE }, /* 0x1B Escape */ \ - {0, 0 }, /* 0x1C */ \ - {0, 0 }, /* 0x1D */ \ - {0, 0 }, /* 0x1E */ \ - {0, 0 }, /* 0x1F */ \ - \ - {0, HID_KEY_SPACE }, /* 0x20 */ \ - {1, HID_KEY_1 }, /* 0x21 ! */ \ - {1, HID_KEY_APOSTROPHE }, /* 0x22 " */ \ - {1, HID_KEY_3 }, /* 0x23 # */ \ - {1, HID_KEY_4 }, /* 0x24 $ */ \ - {1, HID_KEY_5 }, /* 0x25 % */ \ - {1, HID_KEY_7 }, /* 0x26 & */ \ - {0, HID_KEY_APOSTROPHE }, /* 0x27 ' */ \ - {1, HID_KEY_9 }, /* 0x28 ( */ \ - {1, HID_KEY_0 }, /* 0x29 ) */ \ - {1, HID_KEY_8 }, /* 0x2A * */ \ - {1, HID_KEY_EQUAL }, /* 0x2B + */ \ - {0, HID_KEY_COMMA }, /* 0x2C , */ \ - {0, HID_KEY_MINUS }, /* 0x2D - */ \ - {0, HID_KEY_PERIOD }, /* 0x2E . */ \ - {0, HID_KEY_SLASH }, /* 0x2F / */ \ - {0, HID_KEY_0 }, /* 0x30 0 */ \ - {0, HID_KEY_1 }, /* 0x31 1 */ \ - {0, HID_KEY_2 }, /* 0x32 2 */ \ - {0, HID_KEY_3 }, /* 0x33 3 */ \ - {0, HID_KEY_4 }, /* 0x34 4 */ \ - {0, HID_KEY_5 }, /* 0x35 5 */ \ - {0, HID_KEY_6 }, /* 0x36 6 */ \ - {0, HID_KEY_7 }, /* 0x37 7 */ \ - {0, HID_KEY_8 }, /* 0x38 8 */ \ - {0, HID_KEY_9 }, /* 0x39 9 */ \ - {1, HID_KEY_SEMICOLON }, /* 0x3A : */ \ - {0, HID_KEY_SEMICOLON }, /* 0x3B ; */ \ - {1, HID_KEY_COMMA }, /* 0x3C < */ \ - {0, HID_KEY_EQUAL }, /* 0x3D = */ \ - {1, HID_KEY_PERIOD }, /* 0x3E > */ \ - {1, HID_KEY_SLASH }, /* 0x3F ? */ \ - \ - {1, HID_KEY_2 }, /* 0x40 @ */ \ - {1, HID_KEY_A }, /* 0x41 A */ \ - {1, HID_KEY_B }, /* 0x42 B */ \ - {1, HID_KEY_C }, /* 0x43 C */ \ - {1, HID_KEY_D }, /* 0x44 D */ \ - {1, HID_KEY_E }, /* 0x45 E */ \ - {1, HID_KEY_F }, /* 0x46 F */ \ - {1, HID_KEY_G }, /* 0x47 G */ \ - {1, HID_KEY_H }, /* 0x48 H */ \ - {1, HID_KEY_I }, /* 0x49 I */ \ - {1, HID_KEY_J }, /* 0x4A J */ \ - {1, HID_KEY_K }, /* 0x4B K */ \ - {1, HID_KEY_L }, /* 0x4C L */ \ - {1, HID_KEY_M }, /* 0x4D M */ \ - {1, HID_KEY_N }, /* 0x4E N */ \ - {1, HID_KEY_O }, /* 0x4F O */ \ - {1, HID_KEY_P }, /* 0x50 P */ \ - {1, HID_KEY_Q }, /* 0x51 Q */ \ - {1, HID_KEY_R }, /* 0x52 R */ \ - {1, HID_KEY_S }, /* 0x53 S */ \ - {1, HID_KEY_T }, /* 0x55 T */ \ - {1, HID_KEY_U }, /* 0x55 U */ \ - {1, HID_KEY_V }, /* 0x56 V */ \ - {1, HID_KEY_W }, /* 0x57 W */ \ - {1, HID_KEY_X }, /* 0x58 X */ \ - {1, HID_KEY_Y }, /* 0x59 Y */ \ - {1, HID_KEY_Z }, /* 0x5A Z */ \ - {0, HID_KEY_BRACKET_LEFT }, /* 0x5B [ */ \ - {0, HID_KEY_BACKSLASH }, /* 0x5C '\' */ \ - {0, HID_KEY_BRACKET_RIGHT }, /* 0x5D ] */ \ - {1, HID_KEY_6 }, /* 0x5E ^ */ \ - {1, HID_KEY_MINUS }, /* 0x5F _ */ \ - \ - {0, HID_KEY_GRAVE }, /* 0x60 ` */ \ - {0, HID_KEY_A }, /* 0x61 a */ \ - {0, HID_KEY_B }, /* 0x62 b */ \ - {0, HID_KEY_C }, /* 0x63 c */ \ - {0, HID_KEY_D }, /* 0x66 d */ \ - {0, HID_KEY_E }, /* 0x65 e */ \ - {0, HID_KEY_F }, /* 0x66 f */ \ - {0, HID_KEY_G }, /* 0x67 g */ \ - {0, HID_KEY_H }, /* 0x68 h */ \ - {0, HID_KEY_I }, /* 0x69 i */ \ - {0, HID_KEY_J }, /* 0x6A j */ \ - {0, HID_KEY_K }, /* 0x6B k */ \ - {0, HID_KEY_L }, /* 0x6C l */ \ - {0, HID_KEY_M }, /* 0x6D m */ \ - {0, HID_KEY_N }, /* 0x6E n */ \ - {0, HID_KEY_O }, /* 0x6F o */ \ - {0, HID_KEY_P }, /* 0x70 p */ \ - {0, HID_KEY_Q }, /* 0x71 q */ \ - {0, HID_KEY_R }, /* 0x72 r */ \ - {0, HID_KEY_S }, /* 0x73 s */ \ - {0, HID_KEY_T }, /* 0x75 t */ \ - {0, HID_KEY_U }, /* 0x75 u */ \ - {0, HID_KEY_V }, /* 0x76 v */ \ - {0, HID_KEY_W }, /* 0x77 w */ \ - {0, HID_KEY_X }, /* 0x78 x */ \ - {0, HID_KEY_Y }, /* 0x79 y */ \ - {0, HID_KEY_Z }, /* 0x7A z */ \ - {1, HID_KEY_BRACKET_LEFT }, /* 0x7B { */ \ - {1, HID_KEY_BACKSLASH }, /* 0x7C | */ \ - {1, HID_KEY_BRACKET_RIGHT }, /* 0x7D } */ \ - {1, HID_KEY_GRAVE }, /* 0x7E ~ */ \ - {0, HID_KEY_DELETE } /* 0x7F Delete */ \ - -/*-------------------------------------------------------------------- - * KEYCODE to Ascii Conversion - * Expand to array of [128][2] (ascii without shift, ascii with shift) - * - * Usage: example to convert ascii from keycode (key) and shift modifier (shift). - * Here we assume key < 128 ( printable ) - * - * uint8_t const conv_table[128][2] = { HID_KEYCODE_TO_ASCII }; - * char ch = shift ? conv_table[chr][1] : conv_table[chr][0]; - * - *--------------------------------------------------------------------*/ -#define HID_KEYCODE_TO_ASCII \ - {0 , 0 }, /* 0x00 */ \ - {0 , 0 }, /* 0x01 */ \ - {0 , 0 }, /* 0x02 */ \ - {0 , 0 }, /* 0x03 */ \ - {'a' , 'A' }, /* 0x04 */ \ - {'b' , 'B' }, /* 0x05 */ \ - {'c' , 'C' }, /* 0x06 */ \ - {'d' , 'D' }, /* 0x07 */ \ - {'e' , 'E' }, /* 0x08 */ \ - {'f' , 'F' }, /* 0x09 */ \ - {'g' , 'G' }, /* 0x0a */ \ - {'h' , 'H' }, /* 0x0b */ \ - {'i' , 'I' }, /* 0x0c */ \ - {'j' , 'J' }, /* 0x0d */ \ - {'k' , 'K' }, /* 0x0e */ \ - {'l' , 'L' }, /* 0x0f */ \ - {'m' , 'M' }, /* 0x10 */ \ - {'n' , 'N' }, /* 0x11 */ \ - {'o' , 'O' }, /* 0x12 */ \ - {'p' , 'P' }, /* 0x13 */ \ - {'q' , 'Q' }, /* 0x14 */ \ - {'r' , 'R' }, /* 0x15 */ \ - {'s' , 'S' }, /* 0x16 */ \ - {'t' , 'T' }, /* 0x17 */ \ - {'u' , 'U' }, /* 0x18 */ \ - {'v' , 'V' }, /* 0x19 */ \ - {'w' , 'W' }, /* 0x1a */ \ - {'x' , 'X' }, /* 0x1b */ \ - {'y' , 'Y' }, /* 0x1c */ \ - {'z' , 'Z' }, /* 0x1d */ \ - {'1' , '!' }, /* 0x1e */ \ - {'2' , '@' }, /* 0x1f */ \ - {'3' , '#' }, /* 0x20 */ \ - {'4' , '$' }, /* 0x21 */ \ - {'5' , '%' }, /* 0x22 */ \ - {'6' , '^' }, /* 0x23 */ \ - {'7' , '&' }, /* 0x24 */ \ - {'8' , '*' }, /* 0x25 */ \ - {'9' , '(' }, /* 0x26 */ \ - {'0' , ')' }, /* 0x27 */ \ - {'\r' , '\r' }, /* 0x28 */ \ - {'\x1b', '\x1b' }, /* 0x29 */ \ - {'\b' , '\b' }, /* 0x2a */ \ - {'\t' , '\t' }, /* 0x2b */ \ - {' ' , ' ' }, /* 0x2c */ \ - {'-' , '_' }, /* 0x2d */ \ - {'=' , '+' }, /* 0x2e */ \ - {'[' , '{' }, /* 0x2f */ \ - {']' , '}' }, /* 0x30 */ \ - {'\\' , '|' }, /* 0x31 */ \ - {'#' , '~' }, /* 0x32 */ \ - {';' , ':' }, /* 0x33 */ \ - {'\'' , '\"' }, /* 0x34 */ \ - {'`' , '~' }, /* 0x35 */ \ - {',' , '<' }, /* 0x36 */ \ - {'.' , '>' }, /* 0x37 */ \ - {'/' , '?' }, /* 0x38 */ \ - \ - {0 , 0 }, /* 0x39 */ \ - {0 , 0 }, /* 0x3a */ \ - {0 , 0 }, /* 0x3b */ \ - {0 , 0 }, /* 0x3c */ \ - {0 , 0 }, /* 0x3d */ \ - {0 , 0 }, /* 0x3e */ \ - {0 , 0 }, /* 0x3f */ \ - {0 , 0 }, /* 0x40 */ \ - {0 , 0 }, /* 0x41 */ \ - {0 , 0 }, /* 0x42 */ \ - {0 , 0 }, /* 0x43 */ \ - {0 , 0 }, /* 0x44 */ \ - {0 , 0 }, /* 0x45 */ \ - {0 , 0 }, /* 0x46 */ \ - {0 , 0 }, /* 0x47 */ \ - {0 , 0 }, /* 0x48 */ \ - {0 , 0 }, /* 0x49 */ \ - {0 , 0 }, /* 0x4a */ \ - {0 , 0 }, /* 0x4b */ \ - {0 , 0 }, /* 0x4c */ \ - {0 , 0 }, /* 0x4d */ \ - {0 , 0 }, /* 0x4e */ \ - {0 , 0 }, /* 0x4f */ \ - {0 , 0 }, /* 0x50 */ \ - {0 , 0 }, /* 0x51 */ \ - {0 , 0 }, /* 0x52 */ \ - {0 , 0 }, /* 0x53 */ \ - \ - {'/' , '/' }, /* 0x54 */ \ - {'*' , '*' }, /* 0x55 */ \ - {'-' , '-' }, /* 0x56 */ \ - {'+' , '+' }, /* 0x57 */ \ - {'\r' , '\r' }, /* 0x58 */ \ - {'1' , 0 }, /* 0x59 */ \ - {'2' , 0 }, /* 0x5a */ \ - {'3' , 0 }, /* 0x5b */ \ - {'4' , 0 }, /* 0x5c */ \ - {'5' , '5' }, /* 0x5d */ \ - {'6' , 0 }, /* 0x5e */ \ - {'7' , 0 }, /* 0x5f */ \ - {'8' , 0 }, /* 0x60 */ \ - {'9' , 0 }, /* 0x61 */ \ - {'0' , 0 }, /* 0x62 */ \ - {'0' , 0 }, /* 0x63 */ \ - {'=' , '=' }, /* 0x67 */ \ - - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_HID_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_class + * \defgroup ClassDriver_HID Human Interface Device (HID) + * @{ */ + +#ifndef _TUSB_HID_H_ +#define _TUSB_HID_H_ + +#include "common/tusb_common.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Common Definitions +//--------------------------------------------------------------------+ +/** \defgroup ClassDriver_HID_Common Common Definitions + * @{ */ + + /// USB HID Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength; /**< Numeric expression that is the total size of the HID descriptor */ + uint8_t bDescriptorType; /**< Constant name specifying type of HID descriptor. */ + + uint16_t bcdHID; /**< Numeric expression identifying the HID Class Specification release */ + uint8_t bCountryCode; /**< Numeric expression identifying country code of the localized hardware. */ + uint8_t bNumDescriptors; /**< Numeric expression specifying the number of class descriptors */ + + uint8_t bReportType; /**< Type of HID class report. */ + uint16_t wReportLength; /**< the total size of the Report descriptor. */ +} tusb_hid_descriptor_hid_t; + +/// HID Subclass +typedef enum +{ + HID_SUBCLASS_NONE = 0, ///< No Subclass + HID_SUBCLASS_BOOT = 1 ///< Boot Interface Subclass +}hid_subclass_type_t; + +/// HID Protocol +typedef enum +{ + HID_PROTOCOL_NONE = 0, ///< None + HID_PROTOCOL_KEYBOARD = 1, ///< Keyboard + HID_PROTOCOL_MOUSE = 2 ///< Mouse +}hid_protocol_type_t; + +/// HID Descriptor Type +typedef enum +{ + HID_DESC_TYPE_HID = 0x21, ///< HID Descriptor + HID_DESC_TYPE_REPORT = 0x22, ///< Report Descriptor + HID_DESC_TYPE_PHYSICAL = 0x23 ///< Physical Descriptor +}hid_descriptor_type_t; + +/// HID Request Report Type +typedef enum +{ + HID_REPORT_TYPE_INVALID = 0, + HID_REPORT_TYPE_INPUT, ///< Input + HID_REPORT_TYPE_OUTPUT, ///< Output + HID_REPORT_TYPE_FEATURE ///< Feature +}hid_report_type_t; + +/// HID Class Specific Control Request +typedef enum +{ + HID_REQ_CONTROL_GET_REPORT = 0x01, ///< Get Report + HID_REQ_CONTROL_GET_IDLE = 0x02, ///< Get Idle + HID_REQ_CONTROL_GET_PROTOCOL = 0x03, ///< Get Protocol + HID_REQ_CONTROL_SET_REPORT = 0x09, ///< Set Report + HID_REQ_CONTROL_SET_IDLE = 0x0a, ///< Set Idle + HID_REQ_CONTROL_SET_PROTOCOL = 0x0b ///< Set Protocol +}hid_request_type_t; + +/// HID Country Code +typedef enum +{ + HID_LOCAL_NotSupported = 0 , ///< NotSupported + HID_LOCAL_Arabic , ///< Arabic + HID_LOCAL_Belgian , ///< Belgian + HID_LOCAL_Canadian_Bilingual , ///< Canadian_Bilingual + HID_LOCAL_Canadian_French , ///< Canadian_French + HID_LOCAL_Czech_Republic , ///< Czech_Republic + HID_LOCAL_Danish , ///< Danish + HID_LOCAL_Finnish , ///< Finnish + HID_LOCAL_French , ///< French + HID_LOCAL_German , ///< German + HID_LOCAL_Greek , ///< Greek + HID_LOCAL_Hebrew , ///< Hebrew + HID_LOCAL_Hungary , ///< Hungary + HID_LOCAL_International , ///< International + HID_LOCAL_Italian , ///< Italian + HID_LOCAL_Japan_Katakana , ///< Japan_Katakana + HID_LOCAL_Korean , ///< Korean + HID_LOCAL_Latin_American , ///< Latin_American + HID_LOCAL_Netherlands_Dutch , ///< Netherlands/Dutch + HID_LOCAL_Norwegian , ///< Norwegian + HID_LOCAL_Persian_Farsi , ///< Persian (Farsi) + HID_LOCAL_Poland , ///< Poland + HID_LOCAL_Portuguese , ///< Portuguese + HID_LOCAL_Russia , ///< Russia + HID_LOCAL_Slovakia , ///< Slovakia + HID_LOCAL_Spanish , ///< Spanish + HID_LOCAL_Swedish , ///< Swedish + HID_LOCAL_Swiss_French , ///< Swiss/French + HID_LOCAL_Swiss_German , ///< Swiss/German + HID_LOCAL_Switzerland , ///< Switzerland + HID_LOCAL_Taiwan , ///< Taiwan + HID_LOCAL_Turkish_Q , ///< Turkish-Q + HID_LOCAL_UK , ///< UK + HID_LOCAL_US , ///< US + HID_LOCAL_Yugoslavia , ///< Yugoslavia + HID_LOCAL_Turkish_F ///< Turkish-F +} hid_country_code_t; + +/** @} */ + +//--------------------------------------------------------------------+ +// MOUSE +//--------------------------------------------------------------------+ +/** \addtogroup ClassDriver_HID_Mouse Mouse + * @{ */ + +/// Standard HID Boot Protocol Mouse Report. +typedef struct TU_ATTR_PACKED +{ + uint8_t buttons; /**< buttons mask for currently pressed buttons in the mouse. */ + int8_t x; /**< Current delta x movement of the mouse. */ + int8_t y; /**< Current delta y movement on the mouse. */ + int8_t wheel; /**< Current delta wheel movement on the mouse. */ + int8_t pan; // using AC Pan +} hid_mouse_report_t; + +/// Standard Mouse Buttons Bitmap +typedef enum +{ + MOUSE_BUTTON_LEFT = TU_BIT(0), ///< Left button + MOUSE_BUTTON_RIGHT = TU_BIT(1), ///< Right button + MOUSE_BUTTON_MIDDLE = TU_BIT(2), ///< Middle button + MOUSE_BUTTON_BACKWARD = TU_BIT(3), ///< Backward button, + MOUSE_BUTTON_FORWARD = TU_BIT(4), ///< Forward button, +}hid_mouse_button_bm_t; + +/// @} + +//--------------------------------------------------------------------+ +// Keyboard +//--------------------------------------------------------------------+ +/** \addtogroup ClassDriver_HID_Keyboard Keyboard + * @{ */ + +/// Standard HID Boot Protocol Keyboard Report. +typedef struct TU_ATTR_PACKED +{ + uint8_t modifier; /**< Keyboard modifier (KEYBOARD_MODIFIER_* masks). */ + uint8_t reserved; /**< Reserved for OEM use, always set to 0. */ + uint8_t keycode[6]; /**< Key codes of the currently pressed keys. */ +} hid_keyboard_report_t; + +/// Keyboard modifier codes bitmap +typedef enum +{ + KEYBOARD_MODIFIER_LEFTCTRL = TU_BIT(0), ///< Left Control + KEYBOARD_MODIFIER_LEFTSHIFT = TU_BIT(1), ///< Left Shift + KEYBOARD_MODIFIER_LEFTALT = TU_BIT(2), ///< Left Alt + KEYBOARD_MODIFIER_LEFTGUI = TU_BIT(3), ///< Left Window + KEYBOARD_MODIFIER_RIGHTCTRL = TU_BIT(4), ///< Right Control + KEYBOARD_MODIFIER_RIGHTSHIFT = TU_BIT(5), ///< Right Shift + KEYBOARD_MODIFIER_RIGHTALT = TU_BIT(6), ///< Right Alt + KEYBOARD_MODIFIER_RIGHTGUI = TU_BIT(7) ///< Right Window +}hid_keyboard_modifier_bm_t; + +typedef enum +{ + KEYBOARD_LED_NUMLOCK = TU_BIT(0), ///< Num Lock LED + KEYBOARD_LED_CAPSLOCK = TU_BIT(1), ///< Caps Lock LED + KEYBOARD_LED_SCROLLLOCK = TU_BIT(2), ///< Scroll Lock LED + KEYBOARD_LED_COMPOSE = TU_BIT(3), ///< Composition Mode + KEYBOARD_LED_KANA = TU_BIT(4) ///< Kana mode +}hid_keyboard_led_bm_t; + +/// @} + +//--------------------------------------------------------------------+ +// HID KEYCODE +//--------------------------------------------------------------------+ +#define HID_KEY_NONE 0x00 +#define HID_KEY_A 0x04 +#define HID_KEY_B 0x05 +#define HID_KEY_C 0x06 +#define HID_KEY_D 0x07 +#define HID_KEY_E 0x08 +#define HID_KEY_F 0x09 +#define HID_KEY_G 0x0A +#define HID_KEY_H 0x0B +#define HID_KEY_I 0x0C +#define HID_KEY_J 0x0D +#define HID_KEY_K 0x0E +#define HID_KEY_L 0x0F +#define HID_KEY_M 0x10 +#define HID_KEY_N 0x11 +#define HID_KEY_O 0x12 +#define HID_KEY_P 0x13 +#define HID_KEY_Q 0x14 +#define HID_KEY_R 0x15 +#define HID_KEY_S 0x16 +#define HID_KEY_T 0x17 +#define HID_KEY_U 0x18 +#define HID_KEY_V 0x19 +#define HID_KEY_W 0x1A +#define HID_KEY_X 0x1B +#define HID_KEY_Y 0x1C +#define HID_KEY_Z 0x1D +#define HID_KEY_1 0x1E +#define HID_KEY_2 0x1F +#define HID_KEY_3 0x20 +#define HID_KEY_4 0x21 +#define HID_KEY_5 0x22 +#define HID_KEY_6 0x23 +#define HID_KEY_7 0x24 +#define HID_KEY_8 0x25 +#define HID_KEY_9 0x26 +#define HID_KEY_0 0x27 +#define HID_KEY_RETURN 0x28 +#define HID_KEY_ESCAPE 0x29 +#define HID_KEY_BACKSPACE 0x2A +#define HID_KEY_TAB 0x2B +#define HID_KEY_SPACE 0x2C +#define HID_KEY_MINUS 0x2D +#define HID_KEY_EQUAL 0x2E +#define HID_KEY_BRACKET_LEFT 0x2F +#define HID_KEY_BRACKET_RIGHT 0x30 +#define HID_KEY_BACKSLASH 0x31 +#define HID_KEY_EUROPE_1 0x32 +#define HID_KEY_SEMICOLON 0x33 +#define HID_KEY_APOSTROPHE 0x34 +#define HID_KEY_GRAVE 0x35 +#define HID_KEY_COMMA 0x36 +#define HID_KEY_PERIOD 0x37 +#define HID_KEY_SLASH 0x38 +#define HID_KEY_CAPS_LOCK 0x39 +#define HID_KEY_F1 0x3A +#define HID_KEY_F2 0x3B +#define HID_KEY_F3 0x3C +#define HID_KEY_F4 0x3D +#define HID_KEY_F5 0x3E +#define HID_KEY_F6 0x3F +#define HID_KEY_F7 0x40 +#define HID_KEY_F8 0x41 +#define HID_KEY_F9 0x42 +#define HID_KEY_F10 0x43 +#define HID_KEY_F11 0x44 +#define HID_KEY_F12 0x45 +#define HID_KEY_PRINT_SCREEN 0x46 +#define HID_KEY_SCROLL_LOCK 0x47 +#define HID_KEY_PAUSE 0x48 +#define HID_KEY_INSERT 0x49 +#define HID_KEY_HOME 0x4A +#define HID_KEY_PAGE_UP 0x4B +#define HID_KEY_DELETE 0x4C +#define HID_KEY_END 0x4D +#define HID_KEY_PAGE_DOWN 0x4E +#define HID_KEY_ARROW_RIGHT 0x4F +#define HID_KEY_ARROW_LEFT 0x50 +#define HID_KEY_ARROW_DOWN 0x51 +#define HID_KEY_ARROW_UP 0x52 +#define HID_KEY_NUM_LOCK 0x53 +#define HID_KEY_KEYPAD_DIVIDE 0x54 +#define HID_KEY_KEYPAD_MULTIPLY 0x55 +#define HID_KEY_KEYPAD_SUBTRACT 0x56 +#define HID_KEY_KEYPAD_ADD 0x57 +#define HID_KEY_KEYPAD_ENTER 0x58 +#define HID_KEY_KEYPAD_1 0x59 +#define HID_KEY_KEYPAD_2 0x5A +#define HID_KEY_KEYPAD_3 0x5B +#define HID_KEY_KEYPAD_4 0x5C +#define HID_KEY_KEYPAD_5 0x5D +#define HID_KEY_KEYPAD_6 0x5E +#define HID_KEY_KEYPAD_7 0x5F +#define HID_KEY_KEYPAD_8 0x60 +#define HID_KEY_KEYPAD_9 0x61 +#define HID_KEY_KEYPAD_0 0x62 +#define HID_KEY_KEYPAD_DECIMAL 0x63 +#define HID_KEY_EUROPE_2 0x64 +#define HID_KEY_APPLICATION 0x65 +#define HID_KEY_POWER 0x66 +#define HID_KEY_KEYPAD_EQUAL 0x67 +#define HID_KEY_F13 0x68 +#define HID_KEY_F14 0x69 +#define HID_KEY_F15 0x6A +#define HID_KEY_CONTROL_LEFT 0xE0 +#define HID_KEY_SHIFT_LEFT 0xE1 +#define HID_KEY_ALT_LEFT 0xE2 +#define HID_KEY_GUI_LEFT 0xE3 +#define HID_KEY_CONTROL_RIGHT 0xE4 +#define HID_KEY_SHIFT_RIGHT 0xE5 +#define HID_KEY_ALT_RIGHT 0xE6 +#define HID_KEY_GUI_RIGHT 0xE7 + + +//--------------------------------------------------------------------+ +// REPORT DESCRIPTOR +//--------------------------------------------------------------------+ +//------------- ITEM & TAG -------------// +#define HID_REPORT_DATA_0(data) +#define HID_REPORT_DATA_1(data) , data +#define HID_REPORT_DATA_2(data) , U16_TO_U8S_LE(data) +#define HID_REPORT_DATA_3(data) , U32_TO_U8S_LE(data) + +#define HID_REPORT_ITEM(data, tag, type, size) \ + (((tag) << 4) | ((type) << 2) | (size)) HID_REPORT_DATA_##size(data) + +#define RI_TYPE_MAIN 0 +#define RI_TYPE_GLOBAL 1 +#define RI_TYPE_LOCAL 2 + +//------------- MAIN ITEMS 6.2.2.4 -------------// +#define HID_INPUT(x) HID_REPORT_ITEM(x, 8, RI_TYPE_MAIN, 1) +#define HID_OUTPUT(x) HID_REPORT_ITEM(x, 9, RI_TYPE_MAIN, 1) +#define HID_COLLECTION(x) HID_REPORT_ITEM(x, 10, RI_TYPE_MAIN, 1) +#define HID_FEATURE(x) HID_REPORT_ITEM(x, 11, RI_TYPE_MAIN, 1) +#define HID_COLLECTION_END HID_REPORT_ITEM(x, 12, RI_TYPE_MAIN, 0) + +//------------- INPUT, OUTPUT, FEATURE 6.2.2.5 -------------// +#define HID_DATA (0<<0) +#define HID_CONSTANT (1<<0) + +#define HID_ARRAY (0<<1) +#define HID_VARIABLE (1<<1) + +#define HID_ABSOLUTE (0<<2) +#define HID_RELATIVE (1<<2) + +#define HID_WRAP_NO (0<<3) +#define HID_WRAP (1<<3) + +#define HID_LINEAR (0<<4) +#define HID_NONLINEAR (1<<4) + +#define HID_PREFERRED_STATE (0<<5) +#define HID_PREFERRED_NO (1<<5) + +#define HID_NO_NULL_POSITION (0<<6) +#define HID_NULL_STATE (1<<6) + +#define HID_NON_VOLATILE (0<<7) +#define HID_VOLATILE (1<<7) + +#define HID_BITFIELD (0<<8) +#define HID_BUFFERED_BYTES (1<<8) + +//------------- COLLECTION ITEM 6.2.2.6 -------------// +enum { + HID_COLLECTION_PHYSICAL = 0, + HID_COLLECTION_APPLICATION, + HID_COLLECTION_LOGICAL, + HID_COLLECTION_REPORT, + HID_COLLECTION_NAMED_ARRAY, + HID_COLLECTION_USAGE_SWITCH, + HID_COLLECTION_USAGE_MODIFIER +}; + +//------------- GLOBAL ITEMS 6.2.2.7 -------------// +#define HID_USAGE_PAGE(x) HID_REPORT_ITEM(x, 0, RI_TYPE_GLOBAL, 1) +#define HID_USAGE_PAGE_N(x, n) HID_REPORT_ITEM(x, 0, RI_TYPE_GLOBAL, n) + +#define HID_LOGICAL_MIN(x) HID_REPORT_ITEM(x, 1, RI_TYPE_GLOBAL, 1) +#define HID_LOGICAL_MIN_N(x, n) HID_REPORT_ITEM(x, 1, RI_TYPE_GLOBAL, n) + +#define HID_LOGICAL_MAX(x) HID_REPORT_ITEM(x, 2, RI_TYPE_GLOBAL, 1) +#define HID_LOGICAL_MAX_N(x, n) HID_REPORT_ITEM(x, 2, RI_TYPE_GLOBAL, n) + +#define HID_PHYSICAL_MIN(x) HID_REPORT_ITEM(x, 3, RI_TYPE_GLOBAL, 1) +#define HID_PHYSICAL_MIN_N(x, n) HID_REPORT_ITEM(x, 3, RI_TYPE_GLOBAL, n) + +#define HID_PHYSICAL_MAX(x) HID_REPORT_ITEM(x, 4, RI_TYPE_GLOBAL, 1) +#define HID_PHYSICAL_MAX_N(x, n) HID_REPORT_ITEM(x, 4, RI_TYPE_GLOBAL, n) + +#define HID_UNIT_EXPONENT(x) HID_REPORT_ITEM(x, 5, RI_TYPE_GLOBAL, 1) +#define HID_UNIT_EXPONENT_N(x, n) HID_REPORT_ITEM(x, 5, RI_TYPE_GLOBAL, n) + +#define HID_UNIT(x) HID_REPORT_ITEM(x, 6, RI_TYPE_GLOBAL, 1) +#define HID_UNIT_N(x, n) HID_REPORT_ITEM(x, 6, RI_TYPE_GLOBAL, n) + +#define HID_REPORT_SIZE(x) HID_REPORT_ITEM(x, 7, RI_TYPE_GLOBAL, 1) +#define HID_REPORT_SIZE_N(x, n) HID_REPORT_ITEM(x, 7, RI_TYPE_GLOBAL, n) + +#define HID_REPORT_ID(x) HID_REPORT_ITEM(x, 8, RI_TYPE_GLOBAL, 1) +#define HID_REPORT_ID_N(x) HID_REPORT_ITEM(x, 8, RI_TYPE_GLOBAL, n) + +#define HID_REPORT_COUNT(x) HID_REPORT_ITEM(x, 9, RI_TYPE_GLOBAL, 1) +#define HID_REPORT_COUNT_N(x, n) HID_REPORT_ITEM(x, 9, RI_TYPE_GLOBAL, n) + +#define HID_PUSH HID_REPORT_ITEM(x, 10, RI_TYPE_GLOBAL, 0) +#define HID_POP HID_REPORT_ITEM(x, 11, RI_TYPE_GLOBAL, 0) + +//------------- LOCAL ITEMS 6.2.2.8 -------------// +#define HID_USAGE(x) HID_REPORT_ITEM(x, 0, RI_TYPE_LOCAL, 1) +#define HID_USAGE_N(x, n) HID_REPORT_ITEM(x, 0, RI_TYPE_LOCAL, n) + +#define HID_USAGE_MIN(x) HID_REPORT_ITEM(x, 1, RI_TYPE_LOCAL, 1) +#define HID_USAGE_MIN_N(x, n) HID_REPORT_ITEM(x, 1, RI_TYPE_LOCAL, n) + +#define HID_USAGE_MAX(x) HID_REPORT_ITEM(x, 2, RI_TYPE_LOCAL, 1) +#define HID_USAGE_MAX_N(x, n) HID_REPORT_ITEM(x, 2, RI_TYPE_LOCAL, n) + +//--------------------------------------------------------------------+ +// Usage Table +//--------------------------------------------------------------------+ + +/// HID Usage Table - Table 1: Usage Page Summary +enum { + HID_USAGE_PAGE_DESKTOP = 0x01, + HID_USAGE_PAGE_SIMULATE = 0x02, + HID_USAGE_PAGE_VIRTUAL_REALITY = 0x03, + HID_USAGE_PAGE_SPORT = 0x04, + HID_USAGE_PAGE_GAME = 0x05, + HID_USAGE_PAGE_GENERIC_DEVICE = 0x06, + HID_USAGE_PAGE_KEYBOARD = 0x07, + HID_USAGE_PAGE_LED = 0x08, + HID_USAGE_PAGE_BUTTON = 0x09, + HID_USAGE_PAGE_ORDINAL = 0x0a, + HID_USAGE_PAGE_TELEPHONY = 0x0b, + HID_USAGE_PAGE_CONSUMER = 0x0c, + HID_USAGE_PAGE_DIGITIZER = 0x0d, + HID_USAGE_PAGE_PID = 0x0f, + HID_USAGE_PAGE_UNICODE = 0x10, + HID_USAGE_PAGE_ALPHA_DISPLAY = 0x14, + HID_USAGE_PAGE_MEDICAL = 0x40, + HID_USAGE_PAGE_MONITOR = 0x80, //0x80 - 0x83 + HID_USAGE_PAGE_POWER = 0x84, // 0x084 - 0x87 + HID_USAGE_PAGE_BARCODE_SCANNER = 0x8c, + HID_USAGE_PAGE_SCALE = 0x8d, + HID_USAGE_PAGE_MSR = 0x8e, + HID_USAGE_PAGE_CAMERA = 0x90, + HID_USAGE_PAGE_ARCADE = 0x91, + HID_USAGE_PAGE_VENDOR = 0xFF00 // 0xFF00 - 0xFFFF +}; + +/// HID Usage Table - Table 6: Generic Desktop Page +enum { + HID_USAGE_DESKTOP_POINTER = 0x01, + HID_USAGE_DESKTOP_MOUSE = 0x02, + HID_USAGE_DESKTOP_JOYSTICK = 0x04, + HID_USAGE_DESKTOP_GAMEPAD = 0x05, + HID_USAGE_DESKTOP_KEYBOARD = 0x06, + HID_USAGE_DESKTOP_KEYPAD = 0x07, + HID_USAGE_DESKTOP_MULTI_AXIS_CONTROLLER = 0x08, + HID_USAGE_DESKTOP_TABLET_PC_SYSTEM = 0x09, + HID_USAGE_DESKTOP_X = 0x30, + HID_USAGE_DESKTOP_Y = 0x31, + HID_USAGE_DESKTOP_Z = 0x32, + HID_USAGE_DESKTOP_RX = 0x33, + HID_USAGE_DESKTOP_RY = 0x34, + HID_USAGE_DESKTOP_RZ = 0x35, + HID_USAGE_DESKTOP_SLIDER = 0x36, + HID_USAGE_DESKTOP_DIAL = 0x37, + HID_USAGE_DESKTOP_WHEEL = 0x38, + HID_USAGE_DESKTOP_HAT_SWITCH = 0x39, + HID_USAGE_DESKTOP_COUNTED_BUFFER = 0x3a, + HID_USAGE_DESKTOP_BYTE_COUNT = 0x3b, + HID_USAGE_DESKTOP_MOTION_WAKEUP = 0x3c, + HID_USAGE_DESKTOP_START = 0x3d, + HID_USAGE_DESKTOP_SELECT = 0x3e, + HID_USAGE_DESKTOP_VX = 0x40, + HID_USAGE_DESKTOP_VY = 0x41, + HID_USAGE_DESKTOP_VZ = 0x42, + HID_USAGE_DESKTOP_VBRX = 0x43, + HID_USAGE_DESKTOP_VBRY = 0x44, + HID_USAGE_DESKTOP_VBRZ = 0x45, + HID_USAGE_DESKTOP_VNO = 0x46, + HID_USAGE_DESKTOP_FEATURE_NOTIFICATION = 0x47, + HID_USAGE_DESKTOP_RESOLUTION_MULTIPLIER = 0x48, + HID_USAGE_DESKTOP_SYSTEM_CONTROL = 0x80, + HID_USAGE_DESKTOP_SYSTEM_POWER_DOWN = 0x81, + HID_USAGE_DESKTOP_SYSTEM_SLEEP = 0x82, + HID_USAGE_DESKTOP_SYSTEM_WAKE_UP = 0x83, + HID_USAGE_DESKTOP_SYSTEM_CONTEXT_MENU = 0x84, + HID_USAGE_DESKTOP_SYSTEM_MAIN_MENU = 0x85, + HID_USAGE_DESKTOP_SYSTEM_APP_MENU = 0x86, + HID_USAGE_DESKTOP_SYSTEM_MENU_HELP = 0x87, + HID_USAGE_DESKTOP_SYSTEM_MENU_EXIT = 0x88, + HID_USAGE_DESKTOP_SYSTEM_MENU_SELECT = 0x89, + HID_USAGE_DESKTOP_SYSTEM_MENU_RIGHT = 0x8A, + HID_USAGE_DESKTOP_SYSTEM_MENU_LEFT = 0x8B, + HID_USAGE_DESKTOP_SYSTEM_MENU_UP = 0x8C, + HID_USAGE_DESKTOP_SYSTEM_MENU_DOWN = 0x8D, + HID_USAGE_DESKTOP_SYSTEM_COLD_RESTART = 0x8E, + HID_USAGE_DESKTOP_SYSTEM_WARM_RESTART = 0x8F, + HID_USAGE_DESKTOP_DPAD_UP = 0x90, + HID_USAGE_DESKTOP_DPAD_DOWN = 0x91, + HID_USAGE_DESKTOP_DPAD_RIGHT = 0x92, + HID_USAGE_DESKTOP_DPAD_LEFT = 0x93, + HID_USAGE_DESKTOP_SYSTEM_DOCK = 0xA0, + HID_USAGE_DESKTOP_SYSTEM_UNDOCK = 0xA1, + HID_USAGE_DESKTOP_SYSTEM_SETUP = 0xA2, + HID_USAGE_DESKTOP_SYSTEM_BREAK = 0xA3, + HID_USAGE_DESKTOP_SYSTEM_DEBUGGER_BREAK = 0xA4, + HID_USAGE_DESKTOP_APPLICATION_BREAK = 0xA5, + HID_USAGE_DESKTOP_APPLICATION_DEBUGGER_BREAK = 0xA6, + HID_USAGE_DESKTOP_SYSTEM_SPEAKER_MUTE = 0xA7, + HID_USAGE_DESKTOP_SYSTEM_HIBERNATE = 0xA8, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_INVERT = 0xB0, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_INTERNAL = 0xB1, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_EXTERNAL = 0xB2, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_BOTH = 0xB3, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_DUAL = 0xB4, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_TOGGLE_INT_EXT = 0xB5, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_SWAP_PRIMARY_SECONDARY = 0xB6, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_LCD_AUTOSCALE = 0xB7 +}; + + +/// HID Usage Table: Consumer Page (0x0C) +/// Only contains controls that supported by Windows (whole list is too long) +enum +{ + // Generic Control + HID_USAGE_CONSUMER_CONTROL = 0x0001, + + // Power Control + HID_USAGE_CONSUMER_POWER = 0x0030, + HID_USAGE_CONSUMER_RESET = 0x0031, + HID_USAGE_CONSUMER_SLEEP = 0x0032, + + // Screen Brightness + HID_USAGE_CONSUMER_BRIGHTNESS_INCREMENT = 0x006F, + HID_USAGE_CONSUMER_BRIGHTNESS_DECREMENT = 0x0070, + + // These HID usages operate only on mobile systems (battery powered) and + // require Windows 8 (build 8302 or greater). + HID_USAGE_CONSUMER_WIRELESS_RADIO_CONTROLS = 0x000C, + HID_USAGE_CONSUMER_WIRELESS_RADIO_BUTTONS = 0x00C6, + HID_USAGE_CONSUMER_WIRELESS_RADIO_LED = 0x00C7, + HID_USAGE_CONSUMER_WIRELESS_RADIO_SLIDER_SWITCH = 0x00C8, + + // Media Control + HID_USAGE_CONSUMER_PLAY_PAUSE = 0x00CD, + HID_USAGE_CONSUMER_SCAN_NEXT = 0x00B5, + HID_USAGE_CONSUMER_SCAN_PREVIOUS = 0x00B6, + HID_USAGE_CONSUMER_STOP = 0x00B7, + HID_USAGE_CONSUMER_VOLUME = 0x00E0, + HID_USAGE_CONSUMER_MUTE = 0x00E2, + HID_USAGE_CONSUMER_BASS = 0x00E3, + HID_USAGE_CONSUMER_TREBLE = 0x00E4, + HID_USAGE_CONSUMER_BASS_BOOST = 0x00E5, + HID_USAGE_CONSUMER_VOLUME_INCREMENT = 0x00E9, + HID_USAGE_CONSUMER_VOLUME_DECREMENT = 0x00EA, + HID_USAGE_CONSUMER_BASS_INCREMENT = 0x0152, + HID_USAGE_CONSUMER_BASS_DECREMENT = 0x0153, + HID_USAGE_CONSUMER_TREBLE_INCREMENT = 0x0154, + HID_USAGE_CONSUMER_TREBLE_DECREMENT = 0x0155, + + // Application Launcher + HID_USAGE_CONSUMER_AL_CONSUMER_CONTROL_CONFIGURATION = 0x0183, + HID_USAGE_CONSUMER_AL_EMAIL_READER = 0x018A, + HID_USAGE_CONSUMER_AL_CALCULATOR = 0x0192, + HID_USAGE_CONSUMER_AL_LOCAL_BROWSER = 0x0194, + + // Browser/Explorer Specific + HID_USAGE_CONSUMER_AC_SEARCH = 0x0221, + HID_USAGE_CONSUMER_AC_HOME = 0x0223, + HID_USAGE_CONSUMER_AC_BACK = 0x0224, + HID_USAGE_CONSUMER_AC_FORWARD = 0x0225, + HID_USAGE_CONSUMER_AC_STOP = 0x0226, + HID_USAGE_CONSUMER_AC_REFRESH = 0x0227, + HID_USAGE_CONSUMER_AC_BOOKMARKS = 0x022A, + + // Mouse Horizontal scroll + HID_USAGE_CONSUMER_AC_PAN = 0x0238, +}; + +/*-------------------------------------------------------------------- + * ASCII to KEYCODE Conversion + * Expand to array of [128][2] (shift, keycode) + * + * Usage: example to convert input chr into keyboard report (modifier + keycode) + * + * uint8_t const conv_table[128][2] = { HID_ASCII_TO_KEYCODE }; + * + * uint8_t keycode[6] = { 0 }; + * uint8_t modifier = 0; + * + * if ( conv_table[chr][0] ) modifier = KEYBOARD_MODIFIER_LEFTSHIFT; + * keycode[0] = conv_table[chr][1]; + * tud_hid_keyboard_report(report_id, modifier, keycode); + * + *--------------------------------------------------------------------*/ +#define HID_ASCII_TO_KEYCODE \ + {0, 0 }, /* 0x00 Null */ \ + {0, 0 }, /* 0x01 */ \ + {0, 0 }, /* 0x02 */ \ + {0, 0 }, /* 0x03 */ \ + {0, 0 }, /* 0x04 */ \ + {0, 0 }, /* 0x05 */ \ + {0, 0 }, /* 0x06 */ \ + {0, 0 }, /* 0x07 */ \ + {0, HID_KEY_BACKSPACE }, /* 0x08 Backspace */ \ + {0, HID_KEY_TAB }, /* 0x09 Tab */ \ + {0, HID_KEY_RETURN }, /* 0x0A Line Feed */ \ + {0, 0 }, /* 0x0B */ \ + {0, 0 }, /* 0x0C */ \ + {0, HID_KEY_RETURN }, /* 0x0D CR */ \ + {0, 0 }, /* 0x0E */ \ + {0, 0 }, /* 0x0F */ \ + {0, 0 }, /* 0x10 */ \ + {0, 0 }, /* 0x11 */ \ + {0, 0 }, /* 0x12 */ \ + {0, 0 }, /* 0x13 */ \ + {0, 0 }, /* 0x14 */ \ + {0, 0 }, /* 0x15 */ \ + {0, 0 }, /* 0x16 */ \ + {0, 0 }, /* 0x17 */ \ + {0, 0 }, /* 0x18 */ \ + {0, 0 }, /* 0x19 */ \ + {0, 0 }, /* 0x1A */ \ + {0, HID_KEY_ESCAPE }, /* 0x1B Escape */ \ + {0, 0 }, /* 0x1C */ \ + {0, 0 }, /* 0x1D */ \ + {0, 0 }, /* 0x1E */ \ + {0, 0 }, /* 0x1F */ \ + \ + {0, HID_KEY_SPACE }, /* 0x20 */ \ + {1, HID_KEY_1 }, /* 0x21 ! */ \ + {1, HID_KEY_APOSTROPHE }, /* 0x22 " */ \ + {1, HID_KEY_3 }, /* 0x23 # */ \ + {1, HID_KEY_4 }, /* 0x24 $ */ \ + {1, HID_KEY_5 }, /* 0x25 % */ \ + {1, HID_KEY_7 }, /* 0x26 & */ \ + {0, HID_KEY_APOSTROPHE }, /* 0x27 ' */ \ + {1, HID_KEY_9 }, /* 0x28 ( */ \ + {1, HID_KEY_0 }, /* 0x29 ) */ \ + {1, HID_KEY_8 }, /* 0x2A * */ \ + {1, HID_KEY_EQUAL }, /* 0x2B + */ \ + {0, HID_KEY_COMMA }, /* 0x2C , */ \ + {0, HID_KEY_MINUS }, /* 0x2D - */ \ + {0, HID_KEY_PERIOD }, /* 0x2E . */ \ + {0, HID_KEY_SLASH }, /* 0x2F / */ \ + {0, HID_KEY_0 }, /* 0x30 0 */ \ + {0, HID_KEY_1 }, /* 0x31 1 */ \ + {0, HID_KEY_2 }, /* 0x32 2 */ \ + {0, HID_KEY_3 }, /* 0x33 3 */ \ + {0, HID_KEY_4 }, /* 0x34 4 */ \ + {0, HID_KEY_5 }, /* 0x35 5 */ \ + {0, HID_KEY_6 }, /* 0x36 6 */ \ + {0, HID_KEY_7 }, /* 0x37 7 */ \ + {0, HID_KEY_8 }, /* 0x38 8 */ \ + {0, HID_KEY_9 }, /* 0x39 9 */ \ + {1, HID_KEY_SEMICOLON }, /* 0x3A : */ \ + {0, HID_KEY_SEMICOLON }, /* 0x3B ; */ \ + {1, HID_KEY_COMMA }, /* 0x3C < */ \ + {0, HID_KEY_EQUAL }, /* 0x3D = */ \ + {1, HID_KEY_PERIOD }, /* 0x3E > */ \ + {1, HID_KEY_SLASH }, /* 0x3F ? */ \ + \ + {1, HID_KEY_2 }, /* 0x40 @ */ \ + {1, HID_KEY_A }, /* 0x41 A */ \ + {1, HID_KEY_B }, /* 0x42 B */ \ + {1, HID_KEY_C }, /* 0x43 C */ \ + {1, HID_KEY_D }, /* 0x44 D */ \ + {1, HID_KEY_E }, /* 0x45 E */ \ + {1, HID_KEY_F }, /* 0x46 F */ \ + {1, HID_KEY_G }, /* 0x47 G */ \ + {1, HID_KEY_H }, /* 0x48 H */ \ + {1, HID_KEY_I }, /* 0x49 I */ \ + {1, HID_KEY_J }, /* 0x4A J */ \ + {1, HID_KEY_K }, /* 0x4B K */ \ + {1, HID_KEY_L }, /* 0x4C L */ \ + {1, HID_KEY_M }, /* 0x4D M */ \ + {1, HID_KEY_N }, /* 0x4E N */ \ + {1, HID_KEY_O }, /* 0x4F O */ \ + {1, HID_KEY_P }, /* 0x50 P */ \ + {1, HID_KEY_Q }, /* 0x51 Q */ \ + {1, HID_KEY_R }, /* 0x52 R */ \ + {1, HID_KEY_S }, /* 0x53 S */ \ + {1, HID_KEY_T }, /* 0x55 T */ \ + {1, HID_KEY_U }, /* 0x55 U */ \ + {1, HID_KEY_V }, /* 0x56 V */ \ + {1, HID_KEY_W }, /* 0x57 W */ \ + {1, HID_KEY_X }, /* 0x58 X */ \ + {1, HID_KEY_Y }, /* 0x59 Y */ \ + {1, HID_KEY_Z }, /* 0x5A Z */ \ + {0, HID_KEY_BRACKET_LEFT }, /* 0x5B [ */ \ + {0, HID_KEY_BACKSLASH }, /* 0x5C '\' */ \ + {0, HID_KEY_BRACKET_RIGHT }, /* 0x5D ] */ \ + {1, HID_KEY_6 }, /* 0x5E ^ */ \ + {1, HID_KEY_MINUS }, /* 0x5F _ */ \ + \ + {0, HID_KEY_GRAVE }, /* 0x60 ` */ \ + {0, HID_KEY_A }, /* 0x61 a */ \ + {0, HID_KEY_B }, /* 0x62 b */ \ + {0, HID_KEY_C }, /* 0x63 c */ \ + {0, HID_KEY_D }, /* 0x66 d */ \ + {0, HID_KEY_E }, /* 0x65 e */ \ + {0, HID_KEY_F }, /* 0x66 f */ \ + {0, HID_KEY_G }, /* 0x67 g */ \ + {0, HID_KEY_H }, /* 0x68 h */ \ + {0, HID_KEY_I }, /* 0x69 i */ \ + {0, HID_KEY_J }, /* 0x6A j */ \ + {0, HID_KEY_K }, /* 0x6B k */ \ + {0, HID_KEY_L }, /* 0x6C l */ \ + {0, HID_KEY_M }, /* 0x6D m */ \ + {0, HID_KEY_N }, /* 0x6E n */ \ + {0, HID_KEY_O }, /* 0x6F o */ \ + {0, HID_KEY_P }, /* 0x70 p */ \ + {0, HID_KEY_Q }, /* 0x71 q */ \ + {0, HID_KEY_R }, /* 0x72 r */ \ + {0, HID_KEY_S }, /* 0x73 s */ \ + {0, HID_KEY_T }, /* 0x75 t */ \ + {0, HID_KEY_U }, /* 0x75 u */ \ + {0, HID_KEY_V }, /* 0x76 v */ \ + {0, HID_KEY_W }, /* 0x77 w */ \ + {0, HID_KEY_X }, /* 0x78 x */ \ + {0, HID_KEY_Y }, /* 0x79 y */ \ + {0, HID_KEY_Z }, /* 0x7A z */ \ + {1, HID_KEY_BRACKET_LEFT }, /* 0x7B { */ \ + {1, HID_KEY_BACKSLASH }, /* 0x7C | */ \ + {1, HID_KEY_BRACKET_RIGHT }, /* 0x7D } */ \ + {1, HID_KEY_GRAVE }, /* 0x7E ~ */ \ + {0, HID_KEY_DELETE } /* 0x7F Delete */ \ + +/*-------------------------------------------------------------------- + * KEYCODE to Ascii Conversion + * Expand to array of [128][2] (ascii without shift, ascii with shift) + * + * Usage: example to convert ascii from keycode (key) and shift modifier (shift). + * Here we assume key < 128 ( printable ) + * + * uint8_t const conv_table[128][2] = { HID_KEYCODE_TO_ASCII }; + * char ch = shift ? conv_table[chr][1] : conv_table[chr][0]; + * + *--------------------------------------------------------------------*/ +#define HID_KEYCODE_TO_ASCII \ + {0 , 0 }, /* 0x00 */ \ + {0 , 0 }, /* 0x01 */ \ + {0 , 0 }, /* 0x02 */ \ + {0 , 0 }, /* 0x03 */ \ + {'a' , 'A' }, /* 0x04 */ \ + {'b' , 'B' }, /* 0x05 */ \ + {'c' , 'C' }, /* 0x06 */ \ + {'d' , 'D' }, /* 0x07 */ \ + {'e' , 'E' }, /* 0x08 */ \ + {'f' , 'F' }, /* 0x09 */ \ + {'g' , 'G' }, /* 0x0a */ \ + {'h' , 'H' }, /* 0x0b */ \ + {'i' , 'I' }, /* 0x0c */ \ + {'j' , 'J' }, /* 0x0d */ \ + {'k' , 'K' }, /* 0x0e */ \ + {'l' , 'L' }, /* 0x0f */ \ + {'m' , 'M' }, /* 0x10 */ \ + {'n' , 'N' }, /* 0x11 */ \ + {'o' , 'O' }, /* 0x12 */ \ + {'p' , 'P' }, /* 0x13 */ \ + {'q' , 'Q' }, /* 0x14 */ \ + {'r' , 'R' }, /* 0x15 */ \ + {'s' , 'S' }, /* 0x16 */ \ + {'t' , 'T' }, /* 0x17 */ \ + {'u' , 'U' }, /* 0x18 */ \ + {'v' , 'V' }, /* 0x19 */ \ + {'w' , 'W' }, /* 0x1a */ \ + {'x' , 'X' }, /* 0x1b */ \ + {'y' , 'Y' }, /* 0x1c */ \ + {'z' , 'Z' }, /* 0x1d */ \ + {'1' , '!' }, /* 0x1e */ \ + {'2' , '@' }, /* 0x1f */ \ + {'3' , '#' }, /* 0x20 */ \ + {'4' , '$' }, /* 0x21 */ \ + {'5' , '%' }, /* 0x22 */ \ + {'6' , '^' }, /* 0x23 */ \ + {'7' , '&' }, /* 0x24 */ \ + {'8' , '*' }, /* 0x25 */ \ + {'9' , '(' }, /* 0x26 */ \ + {'0' , ')' }, /* 0x27 */ \ + {'\r' , '\r' }, /* 0x28 */ \ + {'\x1b', '\x1b' }, /* 0x29 */ \ + {'\b' , '\b' }, /* 0x2a */ \ + {'\t' , '\t' }, /* 0x2b */ \ + {' ' , ' ' }, /* 0x2c */ \ + {'-' , '_' }, /* 0x2d */ \ + {'=' , '+' }, /* 0x2e */ \ + {'[' , '{' }, /* 0x2f */ \ + {']' , '}' }, /* 0x30 */ \ + {'\\' , '|' }, /* 0x31 */ \ + {'#' , '~' }, /* 0x32 */ \ + {';' , ':' }, /* 0x33 */ \ + {'\'' , '\"' }, /* 0x34 */ \ + {'`' , '~' }, /* 0x35 */ \ + {',' , '<' }, /* 0x36 */ \ + {'.' , '>' }, /* 0x37 */ \ + {'/' , '?' }, /* 0x38 */ \ + \ + {0 , 0 }, /* 0x39 */ \ + {0 , 0 }, /* 0x3a */ \ + {0 , 0 }, /* 0x3b */ \ + {0 , 0 }, /* 0x3c */ \ + {0 , 0 }, /* 0x3d */ \ + {0 , 0 }, /* 0x3e */ \ + {0 , 0 }, /* 0x3f */ \ + {0 , 0 }, /* 0x40 */ \ + {0 , 0 }, /* 0x41 */ \ + {0 , 0 }, /* 0x42 */ \ + {0 , 0 }, /* 0x43 */ \ + {0 , 0 }, /* 0x44 */ \ + {0 , 0 }, /* 0x45 */ \ + {0 , 0 }, /* 0x46 */ \ + {0 , 0 }, /* 0x47 */ \ + {0 , 0 }, /* 0x48 */ \ + {0 , 0 }, /* 0x49 */ \ + {0 , 0 }, /* 0x4a */ \ + {0 , 0 }, /* 0x4b */ \ + {0 , 0 }, /* 0x4c */ \ + {0 , 0 }, /* 0x4d */ \ + {0 , 0 }, /* 0x4e */ \ + {0 , 0 }, /* 0x4f */ \ + {0 , 0 }, /* 0x50 */ \ + {0 , 0 }, /* 0x51 */ \ + {0 , 0 }, /* 0x52 */ \ + {0 , 0 }, /* 0x53 */ \ + \ + {'/' , '/' }, /* 0x54 */ \ + {'*' , '*' }, /* 0x55 */ \ + {'-' , '-' }, /* 0x56 */ \ + {'+' , '+' }, /* 0x57 */ \ + {'\r' , '\r' }, /* 0x58 */ \ + {'1' , 0 }, /* 0x59 */ \ + {'2' , 0 }, /* 0x5a */ \ + {'3' , 0 }, /* 0x5b */ \ + {'4' , 0 }, /* 0x5c */ \ + {'5' , '5' }, /* 0x5d */ \ + {'6' , 0 }, /* 0x5e */ \ + {'7' , 0 }, /* 0x5f */ \ + {'8' , 0 }, /* 0x60 */ \ + {'9' , 0 }, /* 0x61 */ \ + {'0' , 0 }, /* 0x62 */ \ + {'0' , 0 }, /* 0x63 */ \ + {'=' , '=' }, /* 0x67 */ \ + + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_HID_H__ */ + +/// @} diff --git a/src/class/hid/hid_device.h b/src/class/hid/hid_device.h index 577b9ca8e..f5e29d8a2 100644 --- a/src/class/hid/hid_device.h +++ b/src/class/hid/hid_device.h @@ -1,315 +1,315 @@ -/* - * 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. - */ - -#ifndef _TUSB_HID_DEVICE_H_ -#define _TUSB_HID_DEVICE_H_ - -#include "common/tusb_common.h" -#include "device/usbd.h" -#include "hid.h" - -#ifdef __cplusplus - extern "C" { -#endif - -//--------------------------------------------------------------------+ -// Class Driver Default Configure & Validation -//--------------------------------------------------------------------+ - -#ifndef CFG_TUD_HID_BUFSIZE -#define CFG_TUD_HID_BUFSIZE 16 -#endif - -//--------------------------------------------------------------------+ -// Application API -//--------------------------------------------------------------------+ - -// Check if the interface is ready to use -bool tud_hid_ready(void); - -// Check if current mode is Boot (true) or Report (false) -bool tud_hid_boot_mode(void); - -// Send report to host -bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len); - -// KEYBOARD: convenient helper to send keyboard report if application -// use template layout report as defined by hid_keyboard_report_t -bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6]); - -// MOUSE: convenient helper to send mouse report if application -// use template layout report as defined by hid_mouse_report_t -bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal); - -//--------------------------------------------------------------------+ -// Callbacks (Weak is optional) -//--------------------------------------------------------------------+ - -// Invoked when received GET HID REPORT DESCRIPTOR request -// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete -uint8_t const * tud_hid_descriptor_report_cb(void); - -// Invoked when received GET_REPORT control request -// Application must fill buffer report's content and return its length. -// Return zero will cause the stack to STALL request -uint16_t tud_hid_get_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen); - -// Invoked when received SET_REPORT control request or -// received data on OUT endpoint ( Report ID = 0, Type = 0 ) -void tud_hid_set_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize); - -// Invoked when received SET_PROTOCOL request ( mode switch Boot <-> Report ) -TU_ATTR_WEAK void tud_hid_boot_mode_cb(uint8_t boot_mode); - -// Invoked when received SET_IDLE request. return false will stall the request -// - Idle Rate = 0 : only send report if there is changes, i.e skip duplication -// - Idle Rate > 0 : skip duplication, but send at least 1 report every idle rate (in unit of 4 ms). -TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t idle_rate); - -/* --------------------------------------------------------------------+ - * HID Report Descriptor Template - * - * Convenient for declaring popular HID device (keyboard, mouse, consumer, - * gamepad etc...). Templates take "HID_REPORT_ID(n)," as input, leave - * empty if multiple reports is not used - * - * - Only 1 report: no parameter - * uint8_t const report_desc[] = { TUD_HID_REPORT_DESC_KEYBOARD() }; - * - * - Multiple Reports: "HID_REPORT_ID(ID)," must be passed to template - * uint8_t const report_desc[] = - * { - * TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(1), ) , - * TUD_HID_REPORT_DESC_MOUSE ( HID_REPORT_ID(2), ) - * }; - *--------------------------------------------------------------------*/ - -// Keyboard Report Descriptor Template -#define TUD_HID_REPORT_DESC_KEYBOARD(...) \ - HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ - HID_USAGE ( HID_USAGE_DESKTOP_KEYBOARD ) ,\ - HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\ - /* Report ID if any */\ - __VA_ARGS__ \ - /* 8 bits Modifier Keys (Shfit, Control, Alt) */ \ - HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD ) ,\ - HID_USAGE_MIN ( 224 ) ,\ - HID_USAGE_MAX ( 231 ) ,\ - HID_LOGICAL_MIN ( 0 ) ,\ - HID_LOGICAL_MAX ( 1 ) ,\ - HID_REPORT_COUNT ( 8 ) ,\ - HID_REPORT_SIZE ( 1 ) ,\ - HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\ - /* 8 bit reserved */ \ - HID_REPORT_COUNT ( 1 ) ,\ - HID_REPORT_SIZE ( 8 ) ,\ - HID_INPUT ( HID_CONSTANT ) ,\ - /* 6-byte Keycodes */ \ - HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD ) ,\ - HID_USAGE_MIN ( 0 ) ,\ - HID_USAGE_MAX ( 255 ) ,\ - HID_LOGICAL_MIN ( 0 ) ,\ - HID_LOGICAL_MAX ( 255 ) ,\ - HID_REPORT_COUNT ( 6 ) ,\ - HID_REPORT_SIZE ( 8 ) ,\ - HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\ - /* 5-bit LED Indicator Kana | Compose | ScrollLock | CapsLock | NumLock */ \ - HID_USAGE_PAGE ( HID_USAGE_PAGE_LED ) ,\ - HID_USAGE_MIN ( 1 ) ,\ - HID_USAGE_MAX ( 5 ) ,\ - HID_REPORT_COUNT ( 5 ) ,\ - HID_REPORT_SIZE ( 1 ) ,\ - HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\ - /* led padding */ \ - HID_REPORT_COUNT ( 1 ) ,\ - HID_REPORT_SIZE ( 3 ) ,\ - HID_OUTPUT ( HID_CONSTANT ) ,\ - HID_COLLECTION_END \ - -// Mouse Report Descriptor Template -#define TUD_HID_REPORT_DESC_MOUSE(...) \ - HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ - HID_USAGE ( HID_USAGE_DESKTOP_MOUSE ) ,\ - HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\ - /* Report ID if any */\ - __VA_ARGS__ \ - HID_USAGE ( HID_USAGE_DESKTOP_POINTER ) ,\ - HID_COLLECTION ( HID_COLLECTION_PHYSICAL ) ,\ - HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\ - HID_USAGE_MIN ( 1 ) ,\ - HID_USAGE_MAX ( 5 ) ,\ - HID_LOGICAL_MIN ( 0 ) ,\ - HID_LOGICAL_MAX ( 1 ) ,\ - /* Left, Right, Middle, Backward, Forward buttons */ \ - HID_REPORT_COUNT( 5 ) ,\ - HID_REPORT_SIZE ( 1 ) ,\ - HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\ - /* 3 bit padding */ \ - HID_REPORT_COUNT( 1 ) ,\ - HID_REPORT_SIZE ( 3 ) ,\ - HID_INPUT ( HID_CONSTANT ) ,\ - HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ - /* X, Y position [-127, 127] */ \ - HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\ - HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\ - HID_LOGICAL_MIN ( 0x81 ) ,\ - HID_LOGICAL_MAX ( 0x7f ) ,\ - HID_REPORT_COUNT( 2 ) ,\ - HID_REPORT_SIZE ( 8 ) ,\ - HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\ - /* Verital wheel scroll [-127, 127] */ \ - HID_USAGE ( HID_USAGE_DESKTOP_WHEEL ) ,\ - HID_LOGICAL_MIN ( 0x81 ) ,\ - HID_LOGICAL_MAX ( 0x7f ) ,\ - HID_REPORT_COUNT( 1 ) ,\ - HID_REPORT_SIZE ( 8 ) ,\ - HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\ - HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ), \ - /* Horizontal wheel scroll [-127, 127] */ \ - HID_USAGE_N ( HID_USAGE_CONSUMER_AC_PAN, 2 ), \ - HID_LOGICAL_MIN ( 0x81 ), \ - HID_LOGICAL_MAX ( 0x7f ), \ - HID_REPORT_COUNT( 1 ), \ - HID_REPORT_SIZE ( 8 ), \ - HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ), \ - HID_COLLECTION_END , \ - HID_COLLECTION_END \ - -// Consumer Control Report Descriptor Template -#define TUD_HID_REPORT_DESC_CONSUMER(...) \ - HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ) ,\ - HID_USAGE ( HID_USAGE_CONSUMER_CONTROL ) ,\ - HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\ - /* Report ID if any */\ - __VA_ARGS__ \ - HID_LOGICAL_MIN ( 0x00 ) ,\ - HID_LOGICAL_MAX_N( 0x03FF, 2 ) ,\ - HID_USAGE_MIN ( 0x00 ) ,\ - HID_USAGE_MAX_N ( 0x03FF, 2 ) ,\ - HID_REPORT_COUNT ( 1 ) ,\ - HID_REPORT_SIZE ( 16 ) ,\ - HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\ - HID_COLLECTION_END \ - -/* System Control Report Descriptor Template - * 0x00 - do nothing - * 0x01 - Power Off - * 0x02 - Standby - * 0x04 - Wake Host - */ -#define TUD_HID_REPORT_DESC_SYSTEM_CONTROL(...) \ - HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ - HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_CONTROL ) ,\ - HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\ - /* Report ID if any */\ - __VA_ARGS__ \ - /* 2 bit system power control */ \ - HID_LOGICAL_MIN ( 1 ) ,\ - HID_LOGICAL_MAX ( 3 ) ,\ - HID_REPORT_COUNT ( 1 ) ,\ - HID_REPORT_SIZE ( 2 ) ,\ - HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_SLEEP ) ,\ - HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_POWER_DOWN ) ,\ - HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_WAKE_UP ) ,\ - HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\ - /* 6 bit padding */ \ - HID_REPORT_COUNT ( 1 ) ,\ - HID_REPORT_SIZE ( 6 ) ,\ - HID_INPUT ( HID_CONSTANT ) ,\ - HID_COLLECTION_END \ - -// Gamepad Report Descriptor Template -// with 16 buttons and 2 joysticks with following layout -// | Button Map (2 bytes) | X | Y | Z | Rz -#define TUD_HID_REPORT_DESC_GAMEPAD(...) \ - HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ - HID_USAGE ( HID_USAGE_DESKTOP_GAMEPAD ) ,\ - HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\ - /* Report ID if any */\ - __VA_ARGS__ \ - /* 16 bit Button Map */ \ - HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\ - HID_USAGE_MIN ( 1 ) ,\ - HID_USAGE_MAX ( 16 ) ,\ - HID_LOGICAL_MIN ( 0 ) ,\ - HID_LOGICAL_MAX ( 1 ) ,\ - HID_REPORT_COUNT ( 16 ) ,\ - HID_REPORT_SIZE ( 1 ) ,\ - HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\ - /* X, Y, Z, Rz (min -127, max 127 ) */ \ - HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ - HID_LOGICAL_MIN ( 0x81 ) ,\ - HID_LOGICAL_MAX ( 0x7f ) ,\ - HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\ - HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\ - HID_USAGE ( HID_USAGE_DESKTOP_Z ) ,\ - HID_USAGE ( HID_USAGE_DESKTOP_RZ ) ,\ - HID_REPORT_COUNT ( 4 ) ,\ - HID_REPORT_SIZE ( 8 ) ,\ - HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\ - HID_COLLECTION_END \ - -// HID Generic Input & Output -// - 1st parameter is report size (mandatory) -// - 2nd parameter is report id HID_REPORT_ID(n) (optional) -#define TUD_HID_REPORT_DESC_GENERIC_INOUT(report_size, ...) \ - HID_USAGE_PAGE_N ( HID_USAGE_PAGE_VENDOR, 2 ),\ - HID_USAGE ( 0x01 ),\ - HID_COLLECTION ( HID_COLLECTION_APPLICATION ),\ - /* Report ID if any */\ - __VA_ARGS__ \ - /* Input */ \ - HID_USAGE ( 0x02 ),\ - HID_LOGICAL_MIN ( 0x00 ),\ - HID_LOGICAL_MAX ( 0xff ),\ - HID_REPORT_SIZE ( 8 ),\ - HID_REPORT_COUNT( report_size ),\ - HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\ - /* Output */ \ - HID_USAGE ( 0x03 ),\ - HID_LOGICAL_MIN ( 0x00 ),\ - HID_LOGICAL_MAX ( 0xff ),\ - HID_REPORT_SIZE ( 8 ),\ - HID_REPORT_COUNT( report_size ),\ - HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\ - HID_COLLECTION_END \ - -//--------------------------------------------------------------------+ -// Internal Class Driver API -//--------------------------------------------------------------------+ -void hidd_init (void); -void hidd_reset (uint8_t rhport); -bool hidd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length); -bool hidd_control_request (uint8_t rhport, tusb_control_request_t const * request); -bool hidd_control_complete (uint8_t rhport, tusb_control_request_t const * request); -bool hidd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_HID_DEVICE_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. + */ + +#ifndef _TUSB_HID_DEVICE_H_ +#define _TUSB_HID_DEVICE_H_ + +#include "common/tusb_common.h" +#include "device/usbd.h" +#include "hid.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Class Driver Default Configure & Validation +//--------------------------------------------------------------------+ + +#ifndef CFG_TUD_HID_BUFSIZE +#define CFG_TUD_HID_BUFSIZE 16 +#endif + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ + +// Check if the interface is ready to use +bool tud_hid_ready(void); + +// Check if current mode is Boot (true) or Report (false) +bool tud_hid_boot_mode(void); + +// Send report to host +bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len); + +// KEYBOARD: convenient helper to send keyboard report if application +// use template layout report as defined by hid_keyboard_report_t +bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6]); + +// MOUSE: convenient helper to send mouse report if application +// use template layout report as defined by hid_mouse_report_t +bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal); + +//--------------------------------------------------------------------+ +// Callbacks (Weak is optional) +//--------------------------------------------------------------------+ + +// Invoked when received GET HID REPORT DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint8_t const * tud_hid_descriptor_report_cb(void); + +// Invoked when received GET_REPORT control request +// Application must fill buffer report's content and return its length. +// Return zero will cause the stack to STALL request +uint16_t tud_hid_get_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen); + +// Invoked when received SET_REPORT control request or +// received data on OUT endpoint ( Report ID = 0, Type = 0 ) +void tud_hid_set_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize); + +// Invoked when received SET_PROTOCOL request ( mode switch Boot <-> Report ) +TU_ATTR_WEAK void tud_hid_boot_mode_cb(uint8_t boot_mode); + +// Invoked when received SET_IDLE request. return false will stall the request +// - Idle Rate = 0 : only send report if there is changes, i.e skip duplication +// - Idle Rate > 0 : skip duplication, but send at least 1 report every idle rate (in unit of 4 ms). +TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t idle_rate); + +/* --------------------------------------------------------------------+ + * HID Report Descriptor Template + * + * Convenient for declaring popular HID device (keyboard, mouse, consumer, + * gamepad etc...). Templates take "HID_REPORT_ID(n)," as input, leave + * empty if multiple reports is not used + * + * - Only 1 report: no parameter + * uint8_t const report_desc[] = { TUD_HID_REPORT_DESC_KEYBOARD() }; + * + * - Multiple Reports: "HID_REPORT_ID(ID)," must be passed to template + * uint8_t const report_desc[] = + * { + * TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(1), ) , + * TUD_HID_REPORT_DESC_MOUSE ( HID_REPORT_ID(2), ) + * }; + *--------------------------------------------------------------------*/ + +// Keyboard Report Descriptor Template +#define TUD_HID_REPORT_DESC_KEYBOARD(...) \ + HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_KEYBOARD ) ,\ + HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\ + /* Report ID if any */\ + __VA_ARGS__ \ + /* 8 bits Modifier Keys (Shfit, Control, Alt) */ \ + HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD ) ,\ + HID_USAGE_MIN ( 224 ) ,\ + HID_USAGE_MAX ( 231 ) ,\ + HID_LOGICAL_MIN ( 0 ) ,\ + HID_LOGICAL_MAX ( 1 ) ,\ + HID_REPORT_COUNT ( 8 ) ,\ + HID_REPORT_SIZE ( 1 ) ,\ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\ + /* 8 bit reserved */ \ + HID_REPORT_COUNT ( 1 ) ,\ + HID_REPORT_SIZE ( 8 ) ,\ + HID_INPUT ( HID_CONSTANT ) ,\ + /* 6-byte Keycodes */ \ + HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD ) ,\ + HID_USAGE_MIN ( 0 ) ,\ + HID_USAGE_MAX ( 255 ) ,\ + HID_LOGICAL_MIN ( 0 ) ,\ + HID_LOGICAL_MAX ( 255 ) ,\ + HID_REPORT_COUNT ( 6 ) ,\ + HID_REPORT_SIZE ( 8 ) ,\ + HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\ + /* 5-bit LED Indicator Kana | Compose | ScrollLock | CapsLock | NumLock */ \ + HID_USAGE_PAGE ( HID_USAGE_PAGE_LED ) ,\ + HID_USAGE_MIN ( 1 ) ,\ + HID_USAGE_MAX ( 5 ) ,\ + HID_REPORT_COUNT ( 5 ) ,\ + HID_REPORT_SIZE ( 1 ) ,\ + HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\ + /* led padding */ \ + HID_REPORT_COUNT ( 1 ) ,\ + HID_REPORT_SIZE ( 3 ) ,\ + HID_OUTPUT ( HID_CONSTANT ) ,\ + HID_COLLECTION_END \ + +// Mouse Report Descriptor Template +#define TUD_HID_REPORT_DESC_MOUSE(...) \ + HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_MOUSE ) ,\ + HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\ + /* Report ID if any */\ + __VA_ARGS__ \ + HID_USAGE ( HID_USAGE_DESKTOP_POINTER ) ,\ + HID_COLLECTION ( HID_COLLECTION_PHYSICAL ) ,\ + HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\ + HID_USAGE_MIN ( 1 ) ,\ + HID_USAGE_MAX ( 5 ) ,\ + HID_LOGICAL_MIN ( 0 ) ,\ + HID_LOGICAL_MAX ( 1 ) ,\ + /* Left, Right, Middle, Backward, Forward buttons */ \ + HID_REPORT_COUNT( 5 ) ,\ + HID_REPORT_SIZE ( 1 ) ,\ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\ + /* 3 bit padding */ \ + HID_REPORT_COUNT( 1 ) ,\ + HID_REPORT_SIZE ( 3 ) ,\ + HID_INPUT ( HID_CONSTANT ) ,\ + HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ + /* X, Y position [-127, 127] */ \ + HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\ + HID_LOGICAL_MIN ( 0x81 ) ,\ + HID_LOGICAL_MAX ( 0x7f ) ,\ + HID_REPORT_COUNT( 2 ) ,\ + HID_REPORT_SIZE ( 8 ) ,\ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\ + /* Verital wheel scroll [-127, 127] */ \ + HID_USAGE ( HID_USAGE_DESKTOP_WHEEL ) ,\ + HID_LOGICAL_MIN ( 0x81 ) ,\ + HID_LOGICAL_MAX ( 0x7f ) ,\ + HID_REPORT_COUNT( 1 ) ,\ + HID_REPORT_SIZE ( 8 ) ,\ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\ + HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ), \ + /* Horizontal wheel scroll [-127, 127] */ \ + HID_USAGE_N ( HID_USAGE_CONSUMER_AC_PAN, 2 ), \ + HID_LOGICAL_MIN ( 0x81 ), \ + HID_LOGICAL_MAX ( 0x7f ), \ + HID_REPORT_COUNT( 1 ), \ + HID_REPORT_SIZE ( 8 ), \ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ), \ + HID_COLLECTION_END , \ + HID_COLLECTION_END \ + +// Consumer Control Report Descriptor Template +#define TUD_HID_REPORT_DESC_CONSUMER(...) \ + HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ) ,\ + HID_USAGE ( HID_USAGE_CONSUMER_CONTROL ) ,\ + HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\ + /* Report ID if any */\ + __VA_ARGS__ \ + HID_LOGICAL_MIN ( 0x00 ) ,\ + HID_LOGICAL_MAX_N( 0x03FF, 2 ) ,\ + HID_USAGE_MIN ( 0x00 ) ,\ + HID_USAGE_MAX_N ( 0x03FF, 2 ) ,\ + HID_REPORT_COUNT ( 1 ) ,\ + HID_REPORT_SIZE ( 16 ) ,\ + HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\ + HID_COLLECTION_END \ + +/* System Control Report Descriptor Template + * 0x00 - do nothing + * 0x01 - Power Off + * 0x02 - Standby + * 0x04 - Wake Host + */ +#define TUD_HID_REPORT_DESC_SYSTEM_CONTROL(...) \ + HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_CONTROL ) ,\ + HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\ + /* Report ID if any */\ + __VA_ARGS__ \ + /* 2 bit system power control */ \ + HID_LOGICAL_MIN ( 1 ) ,\ + HID_LOGICAL_MAX ( 3 ) ,\ + HID_REPORT_COUNT ( 1 ) ,\ + HID_REPORT_SIZE ( 2 ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_SLEEP ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_POWER_DOWN ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_WAKE_UP ) ,\ + HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\ + /* 6 bit padding */ \ + HID_REPORT_COUNT ( 1 ) ,\ + HID_REPORT_SIZE ( 6 ) ,\ + HID_INPUT ( HID_CONSTANT ) ,\ + HID_COLLECTION_END \ + +// Gamepad Report Descriptor Template +// with 16 buttons and 2 joysticks with following layout +// | Button Map (2 bytes) | X | Y | Z | Rz +#define TUD_HID_REPORT_DESC_GAMEPAD(...) \ + HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_GAMEPAD ) ,\ + HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\ + /* Report ID if any */\ + __VA_ARGS__ \ + /* 16 bit Button Map */ \ + HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\ + HID_USAGE_MIN ( 1 ) ,\ + HID_USAGE_MAX ( 16 ) ,\ + HID_LOGICAL_MIN ( 0 ) ,\ + HID_LOGICAL_MAX ( 1 ) ,\ + HID_REPORT_COUNT ( 16 ) ,\ + HID_REPORT_SIZE ( 1 ) ,\ + HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\ + /* X, Y, Z, Rz (min -127, max 127 ) */ \ + HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ + HID_LOGICAL_MIN ( 0x81 ) ,\ + HID_LOGICAL_MAX ( 0x7f ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_Z ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_RZ ) ,\ + HID_REPORT_COUNT ( 4 ) ,\ + HID_REPORT_SIZE ( 8 ) ,\ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\ + HID_COLLECTION_END \ + +// HID Generic Input & Output +// - 1st parameter is report size (mandatory) +// - 2nd parameter is report id HID_REPORT_ID(n) (optional) +#define TUD_HID_REPORT_DESC_GENERIC_INOUT(report_size, ...) \ + HID_USAGE_PAGE_N ( HID_USAGE_PAGE_VENDOR, 2 ),\ + HID_USAGE ( 0x01 ),\ + HID_COLLECTION ( HID_COLLECTION_APPLICATION ),\ + /* Report ID if any */\ + __VA_ARGS__ \ + /* Input */ \ + HID_USAGE ( 0x02 ),\ + HID_LOGICAL_MIN ( 0x00 ),\ + HID_LOGICAL_MAX ( 0xff ),\ + HID_REPORT_SIZE ( 8 ),\ + HID_REPORT_COUNT( report_size ),\ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\ + /* Output */ \ + HID_USAGE ( 0x03 ),\ + HID_LOGICAL_MIN ( 0x00 ),\ + HID_LOGICAL_MAX ( 0xff ),\ + HID_REPORT_SIZE ( 8 ),\ + HID_REPORT_COUNT( report_size ),\ + HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\ + HID_COLLECTION_END \ + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +void hidd_init (void); +void hidd_reset (uint8_t rhport); +bool hidd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length); +bool hidd_control_request (uint8_t rhport, tusb_control_request_t const * request); +bool hidd_control_complete (uint8_t rhport, tusb_control_request_t const * request); +bool hidd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_HID_DEVICE_H_ */ + diff --git a/src/class/hid/hid_host.c b/src/class/hid/hid_host.c index 91a441af2..69c49b012 100644 --- a/src/class/hid/hid_host.c +++ b/src/class/hid/hid_host.c @@ -1,289 +1,289 @@ -/* - * 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_HOST_ENABLED && HOST_CLASS_HID) - -#include "common/tusb_common.h" -#include "hid_host.h" - -//--------------------------------------------------------------------+ -// MACRO CONSTANT TYPEDEF -//--------------------------------------------------------------------+ - -//--------------------------------------------------------------------+ -// HID Interface common functions -//--------------------------------------------------------------------+ -static inline bool hidh_interface_open(uint8_t dev_addr, uint8_t interface_number, tusb_desc_endpoint_t const *p_endpoint_desc, hidh_interface_info_t *p_hid) -{ - p_hid->pipe_hdl = hcd_edpt_open(dev_addr, p_endpoint_desc, TUSB_CLASS_HID); - p_hid->report_size = p_endpoint_desc->wMaxPacketSize.size; // TODO get size from report descriptor - p_hid->interface_number = interface_number; - - TU_ASSERT (pipehandle_is_valid(p_hid->pipe_hdl)); - - return true; -} - -static inline void hidh_interface_close(hidh_interface_info_t *p_hid) -{ - tu_memclr(p_hid, sizeof(hidh_interface_info_t)); -} - -// called from public API need to validate parameters -tusb_error_t hidh_interface_get_report(uint8_t dev_addr, void * report, hidh_interface_info_t *p_hid) -{ - //------------- parameters validation -------------// - // TODO change to use is configured function - TU_ASSERT (TUSB_DEVICE_STATE_CONFIGURED == tuh_device_get_state(dev_addr), TUSB_ERROR_DEVICE_NOT_READY); - TU_VERIFY (report, TUSB_ERROR_INVALID_PARA); - TU_ASSERT (!hcd_edpt_busy(p_hid->pipe_hdl), TUSB_ERROR_INTERFACE_IS_BUSY); - - TU_ASSERT_ERR( hcd_pipe_xfer(p_hid->pipe_hdl, report, p_hid->report_size, true) ) ; - - return TUSB_ERROR_NONE; -} - -//--------------------------------------------------------------------+ -// KEYBOARD -//--------------------------------------------------------------------+ -#if CFG_TUH_HID_KEYBOARD - -#if 0 -#define EXPAND_KEYCODE_TO_ASCII(keycode, ascii, shift_modified) \ - [0][keycode] = ascii,\ - [1][keycode] = shift_modified,\ - -// TODO size of table should be a macro for application to check boundary -uint8_t const hid_keycode_to_ascii_tbl[2][128] = -{ - HID_KEYCODE_TABLE(EXPAND_KEYCODE_TO_ASCII) -}; -#endif - -static hidh_interface_info_t keyboardh_data[CFG_TUSB_HOST_DEVICE_MAX]; // does not have addr0, index = dev_address-1 - -//------------- KEYBOARD PUBLIC API (parameter validation required) -------------// -bool tuh_hid_keyboard_is_mounted(uint8_t dev_addr) -{ - return tuh_device_is_configured(dev_addr) && pipehandle_is_valid(keyboardh_data[dev_addr-1].pipe_hdl); -} - -tusb_error_t tuh_hid_keyboard_get_report(uint8_t dev_addr, void* p_report) -{ - return hidh_interface_get_report(dev_addr, p_report, &keyboardh_data[dev_addr-1]); -} - -bool tuh_hid_keyboard_is_busy(uint8_t dev_addr) -{ - return tuh_hid_keyboard_is_mounted(dev_addr) && - hcd_edpt_busy( keyboardh_data[dev_addr-1].pipe_hdl ); -} - -#endif - -//--------------------------------------------------------------------+ -// MOUSE -//--------------------------------------------------------------------+ -#if CFG_TUH_HID_MOUSE - -static hidh_interface_info_t mouseh_data[CFG_TUSB_HOST_DEVICE_MAX]; // does not have addr0, index = dev_address-1 - -//------------- Public API -------------// -bool tuh_hid_mouse_is_mounted(uint8_t dev_addr) -{ - return tuh_device_is_configured(dev_addr) && pipehandle_is_valid(mouseh_data[dev_addr-1].pipe_hdl); -} - -bool tuh_hid_mouse_is_busy(uint8_t dev_addr) -{ - return tuh_hid_mouse_is_mounted(dev_addr) && - hcd_edpt_busy( mouseh_data[dev_addr-1].pipe_hdl ); -} - -tusb_error_t tuh_hid_mouse_get_report(uint8_t dev_addr, void * report) -{ - return hidh_interface_get_report(dev_addr, report, &mouseh_data[dev_addr-1]); -} - -#endif - -//--------------------------------------------------------------------+ -// GENERIC -//--------------------------------------------------------------------+ -#if CFG_TUSB_HOST_HID_GENERIC - -//STATIC_ struct { -// hidh_interface_info_t -//} generic_data[CFG_TUSB_HOST_DEVICE_MAX]; - -#endif - -//--------------------------------------------------------------------+ -// CLASS-USBH API (don't require to verify parameters) -//--------------------------------------------------------------------+ -void hidh_init(void) -{ -#if CFG_TUH_HID_KEYBOARD - tu_memclr(&keyboardh_data, sizeof(hidh_interface_info_t)*CFG_TUSB_HOST_DEVICE_MAX); -#endif - -#if CFG_TUH_HID_MOUSE - tu_memclr(&mouseh_data, sizeof(hidh_interface_info_t)*CFG_TUSB_HOST_DEVICE_MAX); -#endif - -#if CFG_TUSB_HOST_HID_GENERIC - hidh_generic_init(); -#endif -} - -#if 0 -CFG_TUSB_MEM_SECTION uint8_t report_descriptor[256]; -#endif - -bool hidh_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length) -{ - uint8_t const *p_desc = (uint8_t const *) p_interface_desc; - - //------------- HID descriptor -------------// - p_desc += p_desc[DESC_OFFSET_LEN]; - tusb_hid_descriptor_hid_t const *p_desc_hid = (tusb_hid_descriptor_hid_t const *) p_desc; - TU_ASSERT(HID_DESC_TYPE_HID == p_desc_hid->bDescriptorType, TUSB_ERROR_INVALID_PARA); - - //------------- Endpoint Descriptor -------------// - p_desc += p_desc[DESC_OFFSET_LEN]; - tusb_desc_endpoint_t const * p_endpoint_desc = (tusb_desc_endpoint_t const *) p_desc; - TU_ASSERT(TUSB_DESC_ENDPOINT == p_endpoint_desc->bDescriptorType, TUSB_ERROR_INVALID_PARA); - - //------------- SET IDLE (0) request -------------// - tusb_control_request_t request = { - .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT }, - .bRequest = HID_REQ_CONTROL_SET_IDLE, - .wValue = 0, // idle_rate = 0 - .wIndex = p_interface_desc->bInterfaceNumber, - .wLength = 0 - }; - TU_ASSERT( usbh_control_xfer( dev_addr, &request, NULL ) ); - -#if 0 - //------------- Get Report Descriptor TODO HID parser -------------// - if ( p_desc_hid->bNumDescriptors ) - { - STASK_INVOKE( - usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_IN, TUSB_REQ_TYPE_STANDARD, TUSB_REQ_RCPT_INTERFACE), - TUSB_REQ_GET_DESCRIPTOR, (p_desc_hid->bReportType << 8), 0, - p_desc_hid->wReportLength, report_descriptor ), - error - ); - (void) error; // if error in getting report descriptor --> treating like there is none - } -#endif - - if ( HID_SUBCLASS_BOOT == p_interface_desc->bInterfaceSubClass ) - { - #if CFG_TUH_HID_KEYBOARD - if ( HID_PROTOCOL_KEYBOARD == p_interface_desc->bInterfaceProtocol) - { - TU_ASSERT( hidh_interface_open(dev_addr, p_interface_desc->bInterfaceNumber, p_endpoint_desc, &keyboardh_data[dev_addr-1]) ); - tuh_hid_keyboard_mounted_cb(dev_addr); - } else - #endif - - #if CFG_TUH_HID_MOUSE - if ( HID_PROTOCOL_MOUSE == p_interface_desc->bInterfaceProtocol) - { - TU_ASSERT ( hidh_interface_open(dev_addr, p_interface_desc->bInterfaceNumber, p_endpoint_desc, &mouseh_data[dev_addr-1]) ); - tuh_hid_mouse_mounted_cb(dev_addr); - } else - #endif - - { - // Not supported protocol - return false; - } - }else - { - // Not supported subclass - return false; - } - - *p_length = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + sizeof(tusb_desc_endpoint_t); - - return true; -} - -void hidh_isr(pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes) -{ - (void) xferred_bytes; // TODO may need to use this para later - -#if CFG_TUH_HID_KEYBOARD - if ( pipehandle_is_equal(pipe_hdl, keyboardh_data[pipe_hdl.dev_addr-1].pipe_hdl) ) - { - tuh_hid_keyboard_isr(pipe_hdl.dev_addr, event); - return; - } -#endif - -#if CFG_TUH_HID_MOUSE - if ( pipehandle_is_equal(pipe_hdl, mouseh_data[pipe_hdl.dev_addr-1].pipe_hdl) ) - { - tuh_hid_mouse_isr(pipe_hdl.dev_addr, event); - return; - } -#endif - -#if CFG_TUSB_HOST_HID_GENERIC - -#endif -} - -void hidh_close(uint8_t dev_addr) -{ -#if CFG_TUH_HID_KEYBOARD - if ( pipehandle_is_valid( keyboardh_data[dev_addr-1].pipe_hdl ) ) - { - hidh_interface_close(&keyboardh_data[dev_addr-1]); - tuh_hid_keyboard_unmounted_cb(dev_addr); - } -#endif - -#if CFG_TUH_HID_MOUSE - if( pipehandle_is_valid( mouseh_data[dev_addr-1].pipe_hdl ) ) - { - hidh_interface_close(&mouseh_data[dev_addr-1]); - tuh_hid_mouse_unmounted_cb( dev_addr ); - } -#endif - -#if CFG_TUSB_HOST_HID_GENERIC - hidh_generic_close(dev_addr); -#endif -} - - - -#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_HOST_ENABLED && HOST_CLASS_HID) + +#include "common/tusb_common.h" +#include "hid_host.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +//--------------------------------------------------------------------+ +// HID Interface common functions +//--------------------------------------------------------------------+ +static inline bool hidh_interface_open(uint8_t dev_addr, uint8_t interface_number, tusb_desc_endpoint_t const *p_endpoint_desc, hidh_interface_info_t *p_hid) +{ + p_hid->pipe_hdl = hcd_edpt_open(dev_addr, p_endpoint_desc, TUSB_CLASS_HID); + p_hid->report_size = p_endpoint_desc->wMaxPacketSize.size; // TODO get size from report descriptor + p_hid->interface_number = interface_number; + + TU_ASSERT (pipehandle_is_valid(p_hid->pipe_hdl)); + + return true; +} + +static inline void hidh_interface_close(hidh_interface_info_t *p_hid) +{ + tu_memclr(p_hid, sizeof(hidh_interface_info_t)); +} + +// called from public API need to validate parameters +tusb_error_t hidh_interface_get_report(uint8_t dev_addr, void * report, hidh_interface_info_t *p_hid) +{ + //------------- parameters validation -------------// + // TODO change to use is configured function + TU_ASSERT (TUSB_DEVICE_STATE_CONFIGURED == tuh_device_get_state(dev_addr), TUSB_ERROR_DEVICE_NOT_READY); + TU_VERIFY (report, TUSB_ERROR_INVALID_PARA); + TU_ASSERT (!hcd_edpt_busy(p_hid->pipe_hdl), TUSB_ERROR_INTERFACE_IS_BUSY); + + TU_ASSERT_ERR( hcd_pipe_xfer(p_hid->pipe_hdl, report, p_hid->report_size, true) ) ; + + return TUSB_ERROR_NONE; +} + +//--------------------------------------------------------------------+ +// KEYBOARD +//--------------------------------------------------------------------+ +#if CFG_TUH_HID_KEYBOARD + +#if 0 +#define EXPAND_KEYCODE_TO_ASCII(keycode, ascii, shift_modified) \ + [0][keycode] = ascii,\ + [1][keycode] = shift_modified,\ + +// TODO size of table should be a macro for application to check boundary +uint8_t const hid_keycode_to_ascii_tbl[2][128] = +{ + HID_KEYCODE_TABLE(EXPAND_KEYCODE_TO_ASCII) +}; +#endif + +static hidh_interface_info_t keyboardh_data[CFG_TUSB_HOST_DEVICE_MAX]; // does not have addr0, index = dev_address-1 + +//------------- KEYBOARD PUBLIC API (parameter validation required) -------------// +bool tuh_hid_keyboard_is_mounted(uint8_t dev_addr) +{ + return tuh_device_is_configured(dev_addr) && pipehandle_is_valid(keyboardh_data[dev_addr-1].pipe_hdl); +} + +tusb_error_t tuh_hid_keyboard_get_report(uint8_t dev_addr, void* p_report) +{ + return hidh_interface_get_report(dev_addr, p_report, &keyboardh_data[dev_addr-1]); +} + +bool tuh_hid_keyboard_is_busy(uint8_t dev_addr) +{ + return tuh_hid_keyboard_is_mounted(dev_addr) && + hcd_edpt_busy( keyboardh_data[dev_addr-1].pipe_hdl ); +} + +#endif + +//--------------------------------------------------------------------+ +// MOUSE +//--------------------------------------------------------------------+ +#if CFG_TUH_HID_MOUSE + +static hidh_interface_info_t mouseh_data[CFG_TUSB_HOST_DEVICE_MAX]; // does not have addr0, index = dev_address-1 + +//------------- Public API -------------// +bool tuh_hid_mouse_is_mounted(uint8_t dev_addr) +{ + return tuh_device_is_configured(dev_addr) && pipehandle_is_valid(mouseh_data[dev_addr-1].pipe_hdl); +} + +bool tuh_hid_mouse_is_busy(uint8_t dev_addr) +{ + return tuh_hid_mouse_is_mounted(dev_addr) && + hcd_edpt_busy( mouseh_data[dev_addr-1].pipe_hdl ); +} + +tusb_error_t tuh_hid_mouse_get_report(uint8_t dev_addr, void * report) +{ + return hidh_interface_get_report(dev_addr, report, &mouseh_data[dev_addr-1]); +} + +#endif + +//--------------------------------------------------------------------+ +// GENERIC +//--------------------------------------------------------------------+ +#if CFG_TUSB_HOST_HID_GENERIC + +//STATIC_ struct { +// hidh_interface_info_t +//} generic_data[CFG_TUSB_HOST_DEVICE_MAX]; + +#endif + +//--------------------------------------------------------------------+ +// CLASS-USBH API (don't require to verify parameters) +//--------------------------------------------------------------------+ +void hidh_init(void) +{ +#if CFG_TUH_HID_KEYBOARD + tu_memclr(&keyboardh_data, sizeof(hidh_interface_info_t)*CFG_TUSB_HOST_DEVICE_MAX); +#endif + +#if CFG_TUH_HID_MOUSE + tu_memclr(&mouseh_data, sizeof(hidh_interface_info_t)*CFG_TUSB_HOST_DEVICE_MAX); +#endif + +#if CFG_TUSB_HOST_HID_GENERIC + hidh_generic_init(); +#endif +} + +#if 0 +CFG_TUSB_MEM_SECTION uint8_t report_descriptor[256]; +#endif + +bool hidh_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length) +{ + uint8_t const *p_desc = (uint8_t const *) p_interface_desc; + + //------------- HID descriptor -------------// + p_desc += p_desc[DESC_OFFSET_LEN]; + tusb_hid_descriptor_hid_t const *p_desc_hid = (tusb_hid_descriptor_hid_t const *) p_desc; + TU_ASSERT(HID_DESC_TYPE_HID == p_desc_hid->bDescriptorType, TUSB_ERROR_INVALID_PARA); + + //------------- Endpoint Descriptor -------------// + p_desc += p_desc[DESC_OFFSET_LEN]; + tusb_desc_endpoint_t const * p_endpoint_desc = (tusb_desc_endpoint_t const *) p_desc; + TU_ASSERT(TUSB_DESC_ENDPOINT == p_endpoint_desc->bDescriptorType, TUSB_ERROR_INVALID_PARA); + + //------------- SET IDLE (0) request -------------// + tusb_control_request_t request = { + .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT }, + .bRequest = HID_REQ_CONTROL_SET_IDLE, + .wValue = 0, // idle_rate = 0 + .wIndex = p_interface_desc->bInterfaceNumber, + .wLength = 0 + }; + TU_ASSERT( usbh_control_xfer( dev_addr, &request, NULL ) ); + +#if 0 + //------------- Get Report Descriptor TODO HID parser -------------// + if ( p_desc_hid->bNumDescriptors ) + { + STASK_INVOKE( + usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_IN, TUSB_REQ_TYPE_STANDARD, TUSB_REQ_RCPT_INTERFACE), + TUSB_REQ_GET_DESCRIPTOR, (p_desc_hid->bReportType << 8), 0, + p_desc_hid->wReportLength, report_descriptor ), + error + ); + (void) error; // if error in getting report descriptor --> treating like there is none + } +#endif + + if ( HID_SUBCLASS_BOOT == p_interface_desc->bInterfaceSubClass ) + { + #if CFG_TUH_HID_KEYBOARD + if ( HID_PROTOCOL_KEYBOARD == p_interface_desc->bInterfaceProtocol) + { + TU_ASSERT( hidh_interface_open(dev_addr, p_interface_desc->bInterfaceNumber, p_endpoint_desc, &keyboardh_data[dev_addr-1]) ); + tuh_hid_keyboard_mounted_cb(dev_addr); + } else + #endif + + #if CFG_TUH_HID_MOUSE + if ( HID_PROTOCOL_MOUSE == p_interface_desc->bInterfaceProtocol) + { + TU_ASSERT ( hidh_interface_open(dev_addr, p_interface_desc->bInterfaceNumber, p_endpoint_desc, &mouseh_data[dev_addr-1]) ); + tuh_hid_mouse_mounted_cb(dev_addr); + } else + #endif + + { + // Not supported protocol + return false; + } + }else + { + // Not supported subclass + return false; + } + + *p_length = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + sizeof(tusb_desc_endpoint_t); + + return true; +} + +void hidh_isr(pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes) +{ + (void) xferred_bytes; // TODO may need to use this para later + +#if CFG_TUH_HID_KEYBOARD + if ( pipehandle_is_equal(pipe_hdl, keyboardh_data[pipe_hdl.dev_addr-1].pipe_hdl) ) + { + tuh_hid_keyboard_isr(pipe_hdl.dev_addr, event); + return; + } +#endif + +#if CFG_TUH_HID_MOUSE + if ( pipehandle_is_equal(pipe_hdl, mouseh_data[pipe_hdl.dev_addr-1].pipe_hdl) ) + { + tuh_hid_mouse_isr(pipe_hdl.dev_addr, event); + return; + } +#endif + +#if CFG_TUSB_HOST_HID_GENERIC + +#endif +} + +void hidh_close(uint8_t dev_addr) +{ +#if CFG_TUH_HID_KEYBOARD + if ( pipehandle_is_valid( keyboardh_data[dev_addr-1].pipe_hdl ) ) + { + hidh_interface_close(&keyboardh_data[dev_addr-1]); + tuh_hid_keyboard_unmounted_cb(dev_addr); + } +#endif + +#if CFG_TUH_HID_MOUSE + if( pipehandle_is_valid( mouseh_data[dev_addr-1].pipe_hdl ) ) + { + hidh_interface_close(&mouseh_data[dev_addr-1]); + tuh_hid_mouse_unmounted_cb( dev_addr ); + } +#endif + +#if CFG_TUSB_HOST_HID_GENERIC + hidh_generic_close(dev_addr); +#endif +} + + + +#endif diff --git a/src/class/hid/hid_host.h b/src/class/hid/hid_host.h index 1c619c570..a3b614cd7 100644 --- a/src/class/hid/hid_host.h +++ b/src/class/hid/hid_host.h @@ -1,215 +1,215 @@ -/* - * 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. - */ - -/** \addtogroup ClassDriver_HID - * @{ */ - -#ifndef _TUSB_HID_HOST_H_ -#define _TUSB_HID_HOST_H_ - -#include "common/tusb_common.h" -#include "host/usbh.h" -#include "hid.h" - -#ifdef __cplusplus - extern "C" { -#endif - -//--------------------------------------------------------------------+ -// KEYBOARD Application API -//--------------------------------------------------------------------+ -/** \addtogroup ClassDriver_HID_Keyboard Keyboard - * @{ */ - -/** \defgroup Keyboard_Host Host - * The interface API includes status checking function, data transferring function and callback functions - * @{ */ - -extern uint8_t const hid_keycode_to_ascii_tbl[2][128]; // TODO used weak attr if build failed without KEYBOARD enabled - -/** \brief Check if device supports Keyboard interface or not - * \param[in] dev_addr device address - * \retval true if device supports Keyboard interface - * \retval false if device does not support Keyboard interface or is not mounted - */ -bool tuh_hid_keyboard_is_mounted(uint8_t dev_addr); - -/** \brief Check if the interface is currently busy or not - * \param[in] dev_addr device address - * \retval true if the interface is busy meaning the stack is still transferring/waiting data from/to device - * \retval false if the interface is not busy meaning the stack successfully transferred data from/to device - * \note This function is primarily used for polling/waiting result after \ref tuh_hid_keyboard_get_report. - * Alternatively, asynchronous event API can be used - */ -bool tuh_hid_keyboard_is_busy(uint8_t dev_addr); - -/** \brief Perform a get report from Keyboard interface - * \param[in] dev_addr device address - * \param[in,out] p_report address that is used to store data from device. Must be accessible by usb controller (see \ref CFG_TUSB_MEM_SECTION) - * \returns \ref tusb_error_t type to indicate success or error condition. - * \retval TUSB_ERROR_NONE on success - * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device - * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) - * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct - * \note This function is non-blocking and returns immediately. The result of usb transfer will be reported by the interface's callback function - */ -tusb_error_t tuh_hid_keyboard_get_report(uint8_t dev_addr, void * p_report); - -//------------- Application Callback -------------// -/** \brief Callback function that is invoked when an transferring event occurred - * \param[in] dev_addr Address of device - * \param[in] event an value from \ref xfer_result_t - * \note event can be one of following - * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully. - * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error. - * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device. - * \note Application should schedule the next report by calling \ref tuh_hid_keyboard_get_report within this callback - */ -void tuh_hid_keyboard_isr(uint8_t dev_addr, xfer_result_t event); - -/** \brief Callback function that will be invoked when a device with Keyboard interface is mounted - * \param[in] dev_addr Address of newly mounted device - * \note This callback should be used by Application to set-up interface-related data - */ -void tuh_hid_keyboard_mounted_cb(uint8_t dev_addr); - -/** \brief Callback function that will be invoked when a device with Keyboard interface is unmounted - * \param[in] dev_addr Address of newly unmounted device - * \note This callback should be used by Application to tear-down interface-related data - */ -void tuh_hid_keyboard_unmounted_cb(uint8_t dev_addr); - -/** @} */ // Keyboard_Host -/** @} */ // ClassDriver_HID_Keyboard - -//--------------------------------------------------------------------+ -// MOUSE Application API -//--------------------------------------------------------------------+ -/** \addtogroup ClassDriver_HID_Mouse Mouse - * @{ */ - -/** \defgroup Mouse_Host Host - * The interface API includes status checking function, data transferring function and callback functions - * @{ */ - -/** \brief Check if device supports Mouse interface or not - * \param[in] dev_addr device address - * \retval true if device supports Mouse interface - * \retval false if device does not support Mouse interface or is not mounted - */ -bool tuh_hid_mouse_is_mounted(uint8_t dev_addr); - -/** \brief Check if the interface is currently busy or not - * \param[in] dev_addr device address - * \retval true if the interface is busy meaning the stack is still transferring/waiting data from/to device - * \retval false if the interface is not busy meaning the stack successfully transferred data from/to device - * \note This function is primarily used for polling/waiting result after \ref tuh_hid_mouse_get_report. - * Alternatively, asynchronous event API can be used - */ -bool tuh_hid_mouse_is_busy(uint8_t dev_addr); - -/** \brief Perform a get report from Mouse interface - * \param[in] dev_addr device address - * \param[in,out] p_report address that is used to store data from device. Must be accessible by usb controller (see \ref CFG_TUSB_MEM_SECTION) - * \returns \ref tusb_error_t type to indicate success or error condition. - * \retval TUSB_ERROR_NONE on success - * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device - * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) - * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct - * \note This function is non-blocking and returns immediately. The result of usb transfer will be reported by the interface's callback function - */ -tusb_error_t tuh_hid_mouse_get_report(uint8_t dev_addr, void* p_report); - -//------------- Application Callback -------------// -/** \brief Callback function that is invoked when an transferring event occurred - * \param[in] dev_addr Address of device - * \param[in] event an value from \ref xfer_result_t - * \note event can be one of following - * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully. - * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error. - * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device. - * \note Application should schedule the next report by calling \ref tuh_hid_mouse_get_report within this callback - */ -void tuh_hid_mouse_isr(uint8_t dev_addr, xfer_result_t event); - -/** \brief Callback function that will be invoked when a device with Mouse interface is mounted - * \param[in] dev_addr Address of newly mounted device - * \note This callback should be used by Application to set-up interface-related data - */ -void tuh_hid_mouse_mounted_cb(uint8_t dev_addr); - -/** \brief Callback function that will be invoked when a device with Mouse interface is unmounted - * \param[in] dev_addr Address of newly unmounted device - * \note This callback should be used by Application to tear-down interface-related data - */ -void tuh_hid_mouse_unmounted_cb(uint8_t dev_addr); - -/** @} */ // Mouse_Host -/** @} */ // ClassDriver_HID_Mouse - -//--------------------------------------------------------------------+ -// GENERIC Application API -//--------------------------------------------------------------------+ -/** \addtogroup ClassDriver_HID_Generic Generic (not supported yet) - * @{ */ - -/** \defgroup Generic_Host Host - * The interface API includes status checking function, data transferring function and callback functions - * @{ */ - -bool tuh_hid_generic_is_mounted(uint8_t dev_addr); -tusb_error_t tuh_hid_generic_get_report(uint8_t dev_addr, void* p_report, bool int_on_complete); -tusb_error_t tuh_hid_generic_set_report(uint8_t dev_addr, void* p_report, bool int_on_complete); -tusb_interface_status_t tuh_hid_generic_get_status(uint8_t dev_addr); -tusb_interface_status_t tuh_hid_generic_set_status(uint8_t dev_addr); - -//------------- Application Callback -------------// -void tuh_hid_generic_isr(uint8_t dev_addr, xfer_result_t event); - -/** @} */ // Generic_Host -/** @} */ // ClassDriver_HID_Generic - -//--------------------------------------------------------------------+ -// Internal Class Driver API -//--------------------------------------------------------------------+ -typedef struct { - pipe_handle_t pipe_hdl; - uint16_t report_size; - uint8_t interface_number; -}hidh_interface_info_t; - -void hidh_init(void); -bool hidh_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length); -void hidh_isr(pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes); -void hidh_close(uint8_t dev_addr); - -#ifdef __cplusplus -} -#endif - -#endif /* _TUSB_HID_HOST_H_ */ - -/** @} */ // ClassDriver_HID +/* + * 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. + */ + +/** \addtogroup ClassDriver_HID + * @{ */ + +#ifndef _TUSB_HID_HOST_H_ +#define _TUSB_HID_HOST_H_ + +#include "common/tusb_common.h" +#include "host/usbh.h" +#include "hid.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// KEYBOARD Application API +//--------------------------------------------------------------------+ +/** \addtogroup ClassDriver_HID_Keyboard Keyboard + * @{ */ + +/** \defgroup Keyboard_Host Host + * The interface API includes status checking function, data transferring function and callback functions + * @{ */ + +extern uint8_t const hid_keycode_to_ascii_tbl[2][128]; // TODO used weak attr if build failed without KEYBOARD enabled + +/** \brief Check if device supports Keyboard interface or not + * \param[in] dev_addr device address + * \retval true if device supports Keyboard interface + * \retval false if device does not support Keyboard interface or is not mounted + */ +bool tuh_hid_keyboard_is_mounted(uint8_t dev_addr); + +/** \brief Check if the interface is currently busy or not + * \param[in] dev_addr device address + * \retval true if the interface is busy meaning the stack is still transferring/waiting data from/to device + * \retval false if the interface is not busy meaning the stack successfully transferred data from/to device + * \note This function is primarily used for polling/waiting result after \ref tuh_hid_keyboard_get_report. + * Alternatively, asynchronous event API can be used + */ +bool tuh_hid_keyboard_is_busy(uint8_t dev_addr); + +/** \brief Perform a get report from Keyboard interface + * \param[in] dev_addr device address + * \param[in,out] p_report address that is used to store data from device. Must be accessible by usb controller (see \ref CFG_TUSB_MEM_SECTION) + * \returns \ref tusb_error_t type to indicate success or error condition. + * \retval TUSB_ERROR_NONE on success + * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device + * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) + * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct + * \note This function is non-blocking and returns immediately. The result of usb transfer will be reported by the interface's callback function + */ +tusb_error_t tuh_hid_keyboard_get_report(uint8_t dev_addr, void * p_report); + +//------------- Application Callback -------------// +/** \brief Callback function that is invoked when an transferring event occurred + * \param[in] dev_addr Address of device + * \param[in] event an value from \ref xfer_result_t + * \note event can be one of following + * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully. + * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error. + * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device. + * \note Application should schedule the next report by calling \ref tuh_hid_keyboard_get_report within this callback + */ +void tuh_hid_keyboard_isr(uint8_t dev_addr, xfer_result_t event); + +/** \brief Callback function that will be invoked when a device with Keyboard interface is mounted + * \param[in] dev_addr Address of newly mounted device + * \note This callback should be used by Application to set-up interface-related data + */ +void tuh_hid_keyboard_mounted_cb(uint8_t dev_addr); + +/** \brief Callback function that will be invoked when a device with Keyboard interface is unmounted + * \param[in] dev_addr Address of newly unmounted device + * \note This callback should be used by Application to tear-down interface-related data + */ +void tuh_hid_keyboard_unmounted_cb(uint8_t dev_addr); + +/** @} */ // Keyboard_Host +/** @} */ // ClassDriver_HID_Keyboard + +//--------------------------------------------------------------------+ +// MOUSE Application API +//--------------------------------------------------------------------+ +/** \addtogroup ClassDriver_HID_Mouse Mouse + * @{ */ + +/** \defgroup Mouse_Host Host + * The interface API includes status checking function, data transferring function and callback functions + * @{ */ + +/** \brief Check if device supports Mouse interface or not + * \param[in] dev_addr device address + * \retval true if device supports Mouse interface + * \retval false if device does not support Mouse interface or is not mounted + */ +bool tuh_hid_mouse_is_mounted(uint8_t dev_addr); + +/** \brief Check if the interface is currently busy or not + * \param[in] dev_addr device address + * \retval true if the interface is busy meaning the stack is still transferring/waiting data from/to device + * \retval false if the interface is not busy meaning the stack successfully transferred data from/to device + * \note This function is primarily used for polling/waiting result after \ref tuh_hid_mouse_get_report. + * Alternatively, asynchronous event API can be used + */ +bool tuh_hid_mouse_is_busy(uint8_t dev_addr); + +/** \brief Perform a get report from Mouse interface + * \param[in] dev_addr device address + * \param[in,out] p_report address that is used to store data from device. Must be accessible by usb controller (see \ref CFG_TUSB_MEM_SECTION) + * \returns \ref tusb_error_t type to indicate success or error condition. + * \retval TUSB_ERROR_NONE on success + * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device + * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) + * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct + * \note This function is non-blocking and returns immediately. The result of usb transfer will be reported by the interface's callback function + */ +tusb_error_t tuh_hid_mouse_get_report(uint8_t dev_addr, void* p_report); + +//------------- Application Callback -------------// +/** \brief Callback function that is invoked when an transferring event occurred + * \param[in] dev_addr Address of device + * \param[in] event an value from \ref xfer_result_t + * \note event can be one of following + * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully. + * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error. + * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device. + * \note Application should schedule the next report by calling \ref tuh_hid_mouse_get_report within this callback + */ +void tuh_hid_mouse_isr(uint8_t dev_addr, xfer_result_t event); + +/** \brief Callback function that will be invoked when a device with Mouse interface is mounted + * \param[in] dev_addr Address of newly mounted device + * \note This callback should be used by Application to set-up interface-related data + */ +void tuh_hid_mouse_mounted_cb(uint8_t dev_addr); + +/** \brief Callback function that will be invoked when a device with Mouse interface is unmounted + * \param[in] dev_addr Address of newly unmounted device + * \note This callback should be used by Application to tear-down interface-related data + */ +void tuh_hid_mouse_unmounted_cb(uint8_t dev_addr); + +/** @} */ // Mouse_Host +/** @} */ // ClassDriver_HID_Mouse + +//--------------------------------------------------------------------+ +// GENERIC Application API +//--------------------------------------------------------------------+ +/** \addtogroup ClassDriver_HID_Generic Generic (not supported yet) + * @{ */ + +/** \defgroup Generic_Host Host + * The interface API includes status checking function, data transferring function and callback functions + * @{ */ + +bool tuh_hid_generic_is_mounted(uint8_t dev_addr); +tusb_error_t tuh_hid_generic_get_report(uint8_t dev_addr, void* p_report, bool int_on_complete); +tusb_error_t tuh_hid_generic_set_report(uint8_t dev_addr, void* p_report, bool int_on_complete); +tusb_interface_status_t tuh_hid_generic_get_status(uint8_t dev_addr); +tusb_interface_status_t tuh_hid_generic_set_status(uint8_t dev_addr); + +//------------- Application Callback -------------// +void tuh_hid_generic_isr(uint8_t dev_addr, xfer_result_t event); + +/** @} */ // Generic_Host +/** @} */ // ClassDriver_HID_Generic + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +typedef struct { + pipe_handle_t pipe_hdl; + uint16_t report_size; + uint8_t interface_number; +}hidh_interface_info_t; + +void hidh_init(void); +bool hidh_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length); +void hidh_isr(pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes); +void hidh_close(uint8_t dev_addr); + +#ifdef __cplusplus +} +#endif + +#endif /* _TUSB_HID_HOST_H_ */ + +/** @} */ // ClassDriver_HID diff --git a/src/class/midi/midi.h b/src/class/midi/midi.h index b3cc17af9..f758b6e1c 100644 --- a/src/class/midi/midi.h +++ b/src/class/midi/midi.h @@ -1,167 +1,167 @@ -/* - * 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_class - * \defgroup ClassDriver_CDC Communication Device Class (CDC) - * Currently only Abstract Control Model subclass is supported - * @{ */ - -#ifndef _TUSB_MIDI_H__ -#define _TUSB_MIDI_H__ - -#include "common/tusb_common.h" - -#ifdef __cplusplus - extern "C" { -#endif - -//--------------------------------------------------------------------+ -// Class Specific Descriptor -//--------------------------------------------------------------------+ - -typedef enum -{ - MIDI_CS_INTERFACE_HEADER = 0x01, - MIDI_CS_INTERFACE_IN_JACK = 0x02, - MIDI_CS_INTERFACE_OUT_JACK = 0x03, - MIDI_CS_INTERFACE_ELEMENT = 0x04, -} midi_cs_interface_subtype_t; - -typedef enum -{ - MIDI_CS_ENDPOINT_GENERAL = 0x01 -} midi_cs_endpoint_subtype_t; - -typedef enum -{ - MIDI_JACK_EMBEDDED = 0x01, - MIDI_JACK_EXTERNAL = 0x02 -} midi_jack_type_t; - -/// MIDI Interface Header Descriptor -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength ; ///< Size of this descriptor in bytes. - uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific - uint8_t bDescriptorSubType ; ///< Descriptor SubType - uint16_t bcdMSC ; ///< MidiStreaming SubClass release number in Binary-Coded Decimal - uint16_t wTotalLength ; -} midi_desc_header_t; - -/// MIDI In Jack Descriptor -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength ; ///< Size of this descriptor in bytes. - uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific - uint8_t bDescriptorSubType ; ///< Descriptor SubType - uint8_t bJackType ; ///< Embedded or External - uint8_t bJackID ; ///< Unique ID for MIDI IN Jack - uint8_t iJack ; ///< string descriptor -} midi_desc_in_jack_t; - - -/// MIDI Out Jack Descriptor with single pin -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength ; ///< Size of this descriptor in bytes. - uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific - uint8_t bDescriptorSubType ; ///< Descriptor SubType - uint8_t bJackType ; ///< Embedded or External - uint8_t bJackID ; ///< Unique ID for MIDI IN Jack - uint8_t bNrInputPins; - - uint8_t baSourceID; - uint8_t baSourcePin; - - uint8_t iJack ; ///< string descriptor -} midi_desc_out_jack_t ; - -/// MIDI Out Jack Descriptor with multiple pins -#define midi_desc_out_jack_n_t(input_num) \ - struct TU_ATTR_PACKED { \ - uint8_t bLength ; \ - uint8_t bDescriptorType ; \ - uint8_t bDescriptorSubType ; \ - uint8_t bJackType ; \ - uint8_t bJackID ; \ - uint8_t bNrInputPins ; \ - struct TU_ATTR_PACKED { \ - uint8_t baSourceID; \ - uint8_t baSourcePin; \ - } pins[input_num]; \ - uint8_t iJack ; \ - } - -/// MIDI Element Descriptor -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength ; ///< Size of this descriptor in bytes. - uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific - uint8_t bDescriptorSubType ; ///< Descriptor SubType - uint8_t bElementID; - - uint8_t bNrInputPins; - uint8_t baSourceID; - uint8_t baSourcePin; - - uint8_t bNrOutputPins; - uint8_t bInTerminalLink; - uint8_t bOutTerminalLink; - uint8_t bElCapsSize; - - uint16_t bmElementCaps; - uint8_t iElement; -} midi_desc_element_t; - -/// MIDI Element Descriptor with multiple pins -#define midi_desc_element_n_t(input_num) \ - struct TU_ATTR_PACKED { \ - uint8_t bLength; \ - uint8_t bDescriptorType; \ - uint8_t bDescriptorSubType; \ - uint8_t bElementID; \ - uint8_t bNrInputPins; \ - struct TU_ATTR_PACKED { \ - uint8_t baSourceID; \ - uint8_t baSourcePin; \ - } pins[input_num]; \ - uint8_t bNrOutputPins; \ - uint8_t bInTerminalLink; \ - uint8_t bOutTerminalLink; \ - uint8_t bElCapsSize; \ - uint16_t bmElementCaps; \ - uint8_t iElement; \ - } - -/** @} */ - -#ifdef __cplusplus - } -#endif - -#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. + */ + +/** \ingroup group_class + * \defgroup ClassDriver_CDC Communication Device Class (CDC) + * Currently only Abstract Control Model subclass is supported + * @{ */ + +#ifndef _TUSB_MIDI_H__ +#define _TUSB_MIDI_H__ + +#include "common/tusb_common.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Class Specific Descriptor +//--------------------------------------------------------------------+ + +typedef enum +{ + MIDI_CS_INTERFACE_HEADER = 0x01, + MIDI_CS_INTERFACE_IN_JACK = 0x02, + MIDI_CS_INTERFACE_OUT_JACK = 0x03, + MIDI_CS_INTERFACE_ELEMENT = 0x04, +} midi_cs_interface_subtype_t; + +typedef enum +{ + MIDI_CS_ENDPOINT_GENERAL = 0x01 +} midi_cs_endpoint_subtype_t; + +typedef enum +{ + MIDI_JACK_EMBEDDED = 0x01, + MIDI_JACK_EXTERNAL = 0x02 +} midi_jack_type_t; + +/// MIDI Interface Header Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType + uint16_t bcdMSC ; ///< MidiStreaming SubClass release number in Binary-Coded Decimal + uint16_t wTotalLength ; +} midi_desc_header_t; + +/// MIDI In Jack Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType + uint8_t bJackType ; ///< Embedded or External + uint8_t bJackID ; ///< Unique ID for MIDI IN Jack + uint8_t iJack ; ///< string descriptor +} midi_desc_in_jack_t; + + +/// MIDI Out Jack Descriptor with single pin +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType + uint8_t bJackType ; ///< Embedded or External + uint8_t bJackID ; ///< Unique ID for MIDI IN Jack + uint8_t bNrInputPins; + + uint8_t baSourceID; + uint8_t baSourcePin; + + uint8_t iJack ; ///< string descriptor +} midi_desc_out_jack_t ; + +/// MIDI Out Jack Descriptor with multiple pins +#define midi_desc_out_jack_n_t(input_num) \ + struct TU_ATTR_PACKED { \ + uint8_t bLength ; \ + uint8_t bDescriptorType ; \ + uint8_t bDescriptorSubType ; \ + uint8_t bJackType ; \ + uint8_t bJackID ; \ + uint8_t bNrInputPins ; \ + struct TU_ATTR_PACKED { \ + uint8_t baSourceID; \ + uint8_t baSourcePin; \ + } pins[input_num]; \ + uint8_t iJack ; \ + } + +/// MIDI Element Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType + uint8_t bElementID; + + uint8_t bNrInputPins; + uint8_t baSourceID; + uint8_t baSourcePin; + + uint8_t bNrOutputPins; + uint8_t bInTerminalLink; + uint8_t bOutTerminalLink; + uint8_t bElCapsSize; + + uint16_t bmElementCaps; + uint8_t iElement; +} midi_desc_element_t; + +/// MIDI Element Descriptor with multiple pins +#define midi_desc_element_n_t(input_num) \ + struct TU_ATTR_PACKED { \ + uint8_t bLength; \ + uint8_t bDescriptorType; \ + uint8_t bDescriptorSubType; \ + uint8_t bElementID; \ + uint8_t bNrInputPins; \ + struct TU_ATTR_PACKED { \ + uint8_t baSourceID; \ + uint8_t baSourcePin; \ + } pins[input_num]; \ + uint8_t bNrOutputPins; \ + uint8_t bInTerminalLink; \ + uint8_t bOutTerminalLink; \ + uint8_t bElCapsSize; \ + uint16_t bmElementCaps; \ + uint8_t iElement; \ + } + +/** @} */ + +#ifdef __cplusplus + } +#endif + +#endif + +/** @} */ diff --git a/src/class/midi/midi_device.c b/src/class/midi/midi_device.c index 0c68f8e71..312405621 100644 --- a/src/class/midi/midi_device.c +++ b/src/class/midi/midi_device.c @@ -1,361 +1,361 @@ -/* - * 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 && CFG_TUD_MIDI) - -//--------------------------------------------------------------------+ -// INCLUDE -//--------------------------------------------------------------------+ -#include "midi_device.h" -#include "class/audio/audio.h" -#include "device/usbd_pvt.h" - -//--------------------------------------------------------------------+ -// MACRO CONSTANT TYPEDEF -//--------------------------------------------------------------------+ -typedef struct -{ - uint8_t itf_num; - uint8_t ep_in; - uint8_t ep_out; - - /*------------- From this point, data is not cleared by bus reset -------------*/ - // FIFO - tu_fifo_t rx_ff; - tu_fifo_t tx_ff; - uint8_t rx_ff_buf[CFG_TUD_MIDI_RX_BUFSIZE]; - uint8_t tx_ff_buf[CFG_TUD_MIDI_TX_BUFSIZE]; - - #if CFG_FIFO_MUTEX - osal_mutex_def_t rx_ff_mutex; - osal_mutex_def_t tx_ff_mutex; - #endif - - // We need to pack messages into words before queueing their transmission so buffer across write - // calls. - uint8_t message_buffer[4]; - uint8_t message_buffer_length; - uint8_t message_target_length; - - // Endpoint Transfer buffer - CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_MIDI_EPSIZE]; - CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_MIDI_EPSIZE]; - -} midid_interface_t; - -#define ITF_MEM_RESET_SIZE offsetof(midid_interface_t, rx_ff) - -//--------------------------------------------------------------------+ -// INTERNAL OBJECT & FUNCTION DECLARATION -//--------------------------------------------------------------------+ -CFG_TUSB_MEM_SECTION midid_interface_t _midid_itf[CFG_TUD_MIDI]; - -bool tud_midi_n_mounted (uint8_t itf) -{ - midid_interface_t* midi = &_midid_itf[itf]; - return midi->ep_in && midi->ep_out; -} - -//--------------------------------------------------------------------+ -// READ API -//--------------------------------------------------------------------+ -uint32_t tud_midi_n_available(uint8_t itf, uint8_t jack_id) -{ - (void) jack_id; - return tu_fifo_count(&_midid_itf[itf].rx_ff); -} - -uint32_t tud_midi_n_read(uint8_t itf, uint8_t jack_id, void* buffer, uint32_t bufsize) -{ - (void) jack_id; - return tu_fifo_read_n(&_midid_itf[itf].rx_ff, buffer, bufsize); -} - -void tud_midi_n_read_flush (uint8_t itf, uint8_t jack_id) -{ - (void) jack_id; - tu_fifo_clear(&_midid_itf[itf].rx_ff); -} - -void midi_rx_done_cb(midid_interface_t* midi, uint8_t const* buffer, uint32_t bufsize) { - if (bufsize % 4 != 0) { - return; - } - - for(uint32_t i=0; i> 4; - uint8_t code_index = header & 0x0f; - // We always copy over the first byte. - uint8_t count = 1; - // Ignore subsequent bytes based on the code. - if (code_index != 0x5 && code_index != 0xf) { - count = 2; - if (code_index != 0x2 && code_index != 0x6 && code_index != 0xc && code_index != 0xd) { - count = 3; - } - } - tu_fifo_write_n(&midi->rx_ff, &buffer[i + 1], count); - } -} - - -//--------------------------------------------------------------------+ -// WRITE API -//--------------------------------------------------------------------+ - -static bool maybe_transmit(midid_interface_t* midi, uint8_t itf_index) -{ - (void) itf_index; - - // skip if previous transfer not complete - TU_VERIFY( !usbd_edpt_busy(TUD_OPT_RHPORT, midi->ep_in) ); - - uint16_t count = tu_fifo_read_n(&midi->tx_ff, midi->epin_buf, CFG_TUD_MIDI_EPSIZE); - if (count > 0) - { - TU_ASSERT( usbd_edpt_xfer(TUD_OPT_RHPORT, midi->ep_in, midi->epin_buf, count) ); - } - return true; -} - -uint32_t tud_midi_n_write(uint8_t itf, uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize) -{ - midid_interface_t* midi = &_midid_itf[itf]; - if (midi->itf_num == 0) { - return 0; - } - - uint32_t i = 0; - while (i < bufsize) { - uint8_t data = buffer[i]; - if (midi->message_buffer_length == 0) { - uint8_t msg = data >> 4; - midi->message_buffer[1] = data; - midi->message_buffer_length = 2; - // Check to see if we're still in a SysEx transmit. - if (midi->message_buffer[0] == 0x4) { - if (data == 0xf7) { - midi->message_buffer[0] = 0x5; - } else { - midi->message_buffer_length = 4; - } - } else if ((msg >= 0x8 && msg <= 0xB) || msg == 0xE) { - midi->message_buffer[0] = jack_id << 4 | msg; - midi->message_target_length = 4; - } else if (msg == 0xC || msg == 0xD) { - midi->message_buffer[0] = jack_id << 4 | msg; - midi->message_target_length = 3; - } else if (msg == 0xf) { - if (data == 0xf0) { - midi->message_buffer[0] = 0x4; - midi->message_target_length = 4; - } else if (data == 0xf1 || data == 0xf3) { - midi->message_buffer[0] = 0x2; - midi->message_target_length = 3; - } else if (data == 0xf2) { - midi->message_buffer[0] = 0x3; - midi->message_target_length = 4; - } else { - midi->message_buffer[0] = 0x5; - midi->message_target_length = 2; - } - } else { - // Pack individual bytes if we don't support packing them into words. - midi->message_buffer[0] = jack_id << 4 | 0xf; - midi->message_buffer[2] = 0; - midi->message_buffer[3] = 0; - midi->message_buffer_length = 2; - midi->message_target_length = 2; - } - } else { - midi->message_buffer[midi->message_buffer_length] = data; - midi->message_buffer_length += 1; - // See if this byte ends a SysEx. - if (midi->message_buffer[0] == 0x4 && data == 0xf7) { - midi->message_buffer[0] = 0x4 + (midi->message_buffer_length - 1); - midi->message_target_length = midi->message_buffer_length; - } - } - - if (midi->message_buffer_length == midi->message_target_length) { - uint16_t written = tu_fifo_write_n(&midi->tx_ff, midi->message_buffer, 4); - if (written < 4) { - TU_ASSERT( written == 0 ); - break; - } - midi->message_buffer_length = 0; - } - i++; - } - maybe_transmit(midi, itf); - - return i; -} - -//--------------------------------------------------------------------+ -// USBD Driver API -//--------------------------------------------------------------------+ -void midid_init(void) -{ - tu_memclr(_midid_itf, sizeof(_midid_itf)); - - for(uint8_t i=0; irx_ff, midi->rx_ff_buf, CFG_TUD_MIDI_RX_BUFSIZE, 1, true); - tu_fifo_config(&midi->tx_ff, midi->tx_ff_buf, CFG_TUD_MIDI_TX_BUFSIZE, 1, true); - - #if CFG_FIFO_MUTEX - tu_fifo_config_mutex(&midi->rx_ff, osal_mutex_create(&midi->rx_ff_mutex)); - tu_fifo_config_mutex(&midi->tx_ff, osal_mutex_create(&midi->tx_ff_mutex)); - #endif - } -} - -void midid_reset(uint8_t rhport) -{ - (void) rhport; - - for(uint8_t i=0; irx_ff); - tu_fifo_clear(&midi->tx_ff); - } -} - -bool midid_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length) -{ - // For now handle the audio control interface as well. - if ( AUDIO_SUBCLASS_CONTROL == p_interface_desc->bInterfaceSubClass) { - uint8_t const * p_desc = tu_desc_next ( (uint8_t const *) p_interface_desc ); - (*p_length) = sizeof(tusb_desc_interface_t); - - // Skip over the class specific descriptor. - (*p_length) += tu_desc_len(p_desc); - p_desc = tu_desc_next(p_desc); - return true; - } - - TU_VERIFY(AUDIO_SUBCLASS_MIDI_STREAMING == p_interface_desc->bInterfaceSubClass && - AUDIO_PROTOCOL_V1 == p_interface_desc->bInterfaceProtocol ); - - // Find available interface - midid_interface_t * p_midi = NULL; - for(uint8_t i=0; iitf_num = p_interface_desc->bInterfaceNumber; - - uint8_t const * p_desc = tu_desc_next( (uint8_t const *) p_interface_desc ); - (*p_length) = sizeof(tusb_desc_interface_t); - - uint8_t found_endpoints = 0; - while (found_endpoints < p_interface_desc->bNumEndpoints) - { - if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE]) - { - TU_ASSERT( dcd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), false); - uint8_t ep_addr = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress; - if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { - p_midi->ep_in = ep_addr; - } else { - p_midi->ep_out = ep_addr; - } - - (*p_length) += p_desc[DESC_OFFSET_LEN]; - p_desc = tu_desc_next(p_desc); - found_endpoints += 1; - } - (*p_length) += p_desc[DESC_OFFSET_LEN]; - p_desc = tu_desc_next(p_desc); - } - - // Prepare for incoming data - TU_ASSERT( usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, CFG_TUD_MIDI_EPSIZE), false); - - return true; -} - -bool midid_control_complete(uint8_t rhport, tusb_control_request_t const * p_request) -{ - (void) rhport; - (void) p_request; - return true; -} - -bool midid_control_request(uint8_t rhport, tusb_control_request_t const * p_request) -{ - (void) rhport; - (void) p_request; - - // driver doesn't support any request yet - return false; -} - -bool midid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) -{ - (void) result; - - uint8_t itf = 0; - midid_interface_t* p_midi = _midid_itf; - - for ( ; ; itf++, p_midi++) - { - if (itf >= TU_ARRAY_SIZE(_midid_itf)) return false; - - if ( ep_addr == p_midi->ep_out ) break; - } - - // receive new data - if ( ep_addr == p_midi->ep_out ) - { - midi_rx_done_cb(p_midi, p_midi->epout_buf, xferred_bytes); - - // prepare for next - TU_ASSERT( usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, CFG_TUD_MIDI_EPSIZE), false ); - } else if ( ep_addr == p_midi->ep_in ) { - maybe_transmit(p_midi, itf); - } - - // nothing to do with in and notif endpoint - - return TUSB_ERROR_NONE; -} - -#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 && CFG_TUD_MIDI) + +//--------------------------------------------------------------------+ +// INCLUDE +//--------------------------------------------------------------------+ +#include "midi_device.h" +#include "class/audio/audio.h" +#include "device/usbd_pvt.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +typedef struct +{ + uint8_t itf_num; + uint8_t ep_in; + uint8_t ep_out; + + /*------------- From this point, data is not cleared by bus reset -------------*/ + // FIFO + tu_fifo_t rx_ff; + tu_fifo_t tx_ff; + uint8_t rx_ff_buf[CFG_TUD_MIDI_RX_BUFSIZE]; + uint8_t tx_ff_buf[CFG_TUD_MIDI_TX_BUFSIZE]; + + #if CFG_FIFO_MUTEX + osal_mutex_def_t rx_ff_mutex; + osal_mutex_def_t tx_ff_mutex; + #endif + + // We need to pack messages into words before queueing their transmission so buffer across write + // calls. + uint8_t message_buffer[4]; + uint8_t message_buffer_length; + uint8_t message_target_length; + + // Endpoint Transfer buffer + CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_MIDI_EPSIZE]; + CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_MIDI_EPSIZE]; + +} midid_interface_t; + +#define ITF_MEM_RESET_SIZE offsetof(midid_interface_t, rx_ff) + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ +CFG_TUSB_MEM_SECTION midid_interface_t _midid_itf[CFG_TUD_MIDI]; + +bool tud_midi_n_mounted (uint8_t itf) +{ + midid_interface_t* midi = &_midid_itf[itf]; + return midi->ep_in && midi->ep_out; +} + +//--------------------------------------------------------------------+ +// READ API +//--------------------------------------------------------------------+ +uint32_t tud_midi_n_available(uint8_t itf, uint8_t jack_id) +{ + (void) jack_id; + return tu_fifo_count(&_midid_itf[itf].rx_ff); +} + +uint32_t tud_midi_n_read(uint8_t itf, uint8_t jack_id, void* buffer, uint32_t bufsize) +{ + (void) jack_id; + return tu_fifo_read_n(&_midid_itf[itf].rx_ff, buffer, bufsize); +} + +void tud_midi_n_read_flush (uint8_t itf, uint8_t jack_id) +{ + (void) jack_id; + tu_fifo_clear(&_midid_itf[itf].rx_ff); +} + +void midi_rx_done_cb(midid_interface_t* midi, uint8_t const* buffer, uint32_t bufsize) { + if (bufsize % 4 != 0) { + return; + } + + for(uint32_t i=0; i> 4; + uint8_t code_index = header & 0x0f; + // We always copy over the first byte. + uint8_t count = 1; + // Ignore subsequent bytes based on the code. + if (code_index != 0x5 && code_index != 0xf) { + count = 2; + if (code_index != 0x2 && code_index != 0x6 && code_index != 0xc && code_index != 0xd) { + count = 3; + } + } + tu_fifo_write_n(&midi->rx_ff, &buffer[i + 1], count); + } +} + + +//--------------------------------------------------------------------+ +// WRITE API +//--------------------------------------------------------------------+ + +static bool maybe_transmit(midid_interface_t* midi, uint8_t itf_index) +{ + (void) itf_index; + + // skip if previous transfer not complete + TU_VERIFY( !usbd_edpt_busy(TUD_OPT_RHPORT, midi->ep_in) ); + + uint16_t count = tu_fifo_read_n(&midi->tx_ff, midi->epin_buf, CFG_TUD_MIDI_EPSIZE); + if (count > 0) + { + TU_ASSERT( usbd_edpt_xfer(TUD_OPT_RHPORT, midi->ep_in, midi->epin_buf, count) ); + } + return true; +} + +uint32_t tud_midi_n_write(uint8_t itf, uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize) +{ + midid_interface_t* midi = &_midid_itf[itf]; + if (midi->itf_num == 0) { + return 0; + } + + uint32_t i = 0; + while (i < bufsize) { + uint8_t data = buffer[i]; + if (midi->message_buffer_length == 0) { + uint8_t msg = data >> 4; + midi->message_buffer[1] = data; + midi->message_buffer_length = 2; + // Check to see if we're still in a SysEx transmit. + if (midi->message_buffer[0] == 0x4) { + if (data == 0xf7) { + midi->message_buffer[0] = 0x5; + } else { + midi->message_buffer_length = 4; + } + } else if ((msg >= 0x8 && msg <= 0xB) || msg == 0xE) { + midi->message_buffer[0] = jack_id << 4 | msg; + midi->message_target_length = 4; + } else if (msg == 0xC || msg == 0xD) { + midi->message_buffer[0] = jack_id << 4 | msg; + midi->message_target_length = 3; + } else if (msg == 0xf) { + if (data == 0xf0) { + midi->message_buffer[0] = 0x4; + midi->message_target_length = 4; + } else if (data == 0xf1 || data == 0xf3) { + midi->message_buffer[0] = 0x2; + midi->message_target_length = 3; + } else if (data == 0xf2) { + midi->message_buffer[0] = 0x3; + midi->message_target_length = 4; + } else { + midi->message_buffer[0] = 0x5; + midi->message_target_length = 2; + } + } else { + // Pack individual bytes if we don't support packing them into words. + midi->message_buffer[0] = jack_id << 4 | 0xf; + midi->message_buffer[2] = 0; + midi->message_buffer[3] = 0; + midi->message_buffer_length = 2; + midi->message_target_length = 2; + } + } else { + midi->message_buffer[midi->message_buffer_length] = data; + midi->message_buffer_length += 1; + // See if this byte ends a SysEx. + if (midi->message_buffer[0] == 0x4 && data == 0xf7) { + midi->message_buffer[0] = 0x4 + (midi->message_buffer_length - 1); + midi->message_target_length = midi->message_buffer_length; + } + } + + if (midi->message_buffer_length == midi->message_target_length) { + uint16_t written = tu_fifo_write_n(&midi->tx_ff, midi->message_buffer, 4); + if (written < 4) { + TU_ASSERT( written == 0 ); + break; + } + midi->message_buffer_length = 0; + } + i++; + } + maybe_transmit(midi, itf); + + return i; +} + +//--------------------------------------------------------------------+ +// USBD Driver API +//--------------------------------------------------------------------+ +void midid_init(void) +{ + tu_memclr(_midid_itf, sizeof(_midid_itf)); + + for(uint8_t i=0; irx_ff, midi->rx_ff_buf, CFG_TUD_MIDI_RX_BUFSIZE, 1, true); + tu_fifo_config(&midi->tx_ff, midi->tx_ff_buf, CFG_TUD_MIDI_TX_BUFSIZE, 1, true); + + #if CFG_FIFO_MUTEX + tu_fifo_config_mutex(&midi->rx_ff, osal_mutex_create(&midi->rx_ff_mutex)); + tu_fifo_config_mutex(&midi->tx_ff, osal_mutex_create(&midi->tx_ff_mutex)); + #endif + } +} + +void midid_reset(uint8_t rhport) +{ + (void) rhport; + + for(uint8_t i=0; irx_ff); + tu_fifo_clear(&midi->tx_ff); + } +} + +bool midid_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length) +{ + // For now handle the audio control interface as well. + if ( AUDIO_SUBCLASS_CONTROL == p_interface_desc->bInterfaceSubClass) { + uint8_t const * p_desc = tu_desc_next ( (uint8_t const *) p_interface_desc ); + (*p_length) = sizeof(tusb_desc_interface_t); + + // Skip over the class specific descriptor. + (*p_length) += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + return true; + } + + TU_VERIFY(AUDIO_SUBCLASS_MIDI_STREAMING == p_interface_desc->bInterfaceSubClass && + AUDIO_PROTOCOL_V1 == p_interface_desc->bInterfaceProtocol ); + + // Find available interface + midid_interface_t * p_midi = NULL; + for(uint8_t i=0; iitf_num = p_interface_desc->bInterfaceNumber; + + uint8_t const * p_desc = tu_desc_next( (uint8_t const *) p_interface_desc ); + (*p_length) = sizeof(tusb_desc_interface_t); + + uint8_t found_endpoints = 0; + while (found_endpoints < p_interface_desc->bNumEndpoints) + { + if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE]) + { + TU_ASSERT( dcd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), false); + uint8_t ep_addr = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress; + if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { + p_midi->ep_in = ep_addr; + } else { + p_midi->ep_out = ep_addr; + } + + (*p_length) += p_desc[DESC_OFFSET_LEN]; + p_desc = tu_desc_next(p_desc); + found_endpoints += 1; + } + (*p_length) += p_desc[DESC_OFFSET_LEN]; + p_desc = tu_desc_next(p_desc); + } + + // Prepare for incoming data + TU_ASSERT( usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, CFG_TUD_MIDI_EPSIZE), false); + + return true; +} + +bool midid_control_complete(uint8_t rhport, tusb_control_request_t const * p_request) +{ + (void) rhport; + (void) p_request; + return true; +} + +bool midid_control_request(uint8_t rhport, tusb_control_request_t const * p_request) +{ + (void) rhport; + (void) p_request; + + // driver doesn't support any request yet + return false; +} + +bool midid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) +{ + (void) result; + + uint8_t itf = 0; + midid_interface_t* p_midi = _midid_itf; + + for ( ; ; itf++, p_midi++) + { + if (itf >= TU_ARRAY_SIZE(_midid_itf)) return false; + + if ( ep_addr == p_midi->ep_out ) break; + } + + // receive new data + if ( ep_addr == p_midi->ep_out ) + { + midi_rx_done_cb(p_midi, p_midi->epout_buf, xferred_bytes); + + // prepare for next + TU_ASSERT( usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, CFG_TUD_MIDI_EPSIZE), false ); + } else if ( ep_addr == p_midi->ep_in ) { + maybe_transmit(p_midi, itf); + } + + // nothing to do with in and notif endpoint + + return TUSB_ERROR_NONE; +} + +#endif diff --git a/src/class/midi/midi_device.h b/src/class/midi/midi_device.h index 6d9b292a3..fbf4bce0b 100644 --- a/src/class/midi/midi_device.h +++ b/src/class/midi/midi_device.h @@ -1,138 +1,138 @@ -/* - * 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. - */ - -#ifndef _TUSB_MIDI_DEVICE_H_ -#define _TUSB_MIDI_DEVICE_H_ - -#include "common/tusb_common.h" -#include "device/usbd.h" - -#include "class/audio/audio.h" -#include "midi.h" - -//--------------------------------------------------------------------+ -// Class Driver Configuration -//--------------------------------------------------------------------+ -#ifndef CFG_TUD_MIDI_EPSIZE -#define CFG_TUD_MIDI_EPSIZE 64 -#endif - -#ifdef __cplusplus - extern "C" { -#endif - -/** \addtogroup MIDI_Serial Serial - * @{ - * \defgroup MIDI_Serial_Device Device - * @{ */ - -//--------------------------------------------------------------------+ -// Application API (Multiple Interfaces) -// CFG_TUD_MIDI > 1 -//--------------------------------------------------------------------+ -bool tud_midi_n_mounted (uint8_t itf); -uint32_t tud_midi_n_available (uint8_t itf, uint8_t jack_id); -uint32_t tud_midi_n_read (uint8_t itf, uint8_t jack_id, void* buffer, uint32_t bufsize); -void tud_midi_n_read_flush (uint8_t itf, uint8_t jack_id); -uint32_t tud_midi_n_write (uint8_t itf, uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize); - -static inline -uint32_t tud_midi_n_write24 (uint8_t itf, uint8_t jack_id, uint8_t b1, uint8_t b2, uint8_t b3); - -//--------------------------------------------------------------------+ -// Application API (Interface0) -//--------------------------------------------------------------------+ -static inline bool tud_midi_mounted (void); -static inline uint32_t tud_midi_available (void); -static inline uint32_t tud_midi_read (void* buffer, uint32_t bufsize); -static inline void tud_midi_read_flush (void); -static inline uint32_t tud_midi_write (uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize); -static inline uint32_t tudi_midi_write24 (uint8_t jack_id, uint8_t b1, uint8_t b2, uint8_t b3); - -//--------------------------------------------------------------------+ -// Application Callback API (weak is optional) -//--------------------------------------------------------------------+ -TU_ATTR_WEAK void tud_midi_rx_cb(uint8_t itf); - -//--------------------------------------------------------------------+ -// Inline Functions -//--------------------------------------------------------------------+ - -static inline uint32_t tud_midi_n_write24 (uint8_t itf, uint8_t jack_id, uint8_t b1, uint8_t b2, uint8_t b3) -{ - uint8_t msg[3] = { b1, b2, b3 }; - return tud_midi_n_write(itf, jack_id, msg, 3); -} - -static inline bool tud_midi_mounted (void) -{ - return tud_midi_n_mounted(0); -} - -static inline uint32_t tud_midi_available (void) -{ - return tud_midi_n_available(0, 0); -} - -static inline uint32_t tud_midi_read (void* buffer, uint32_t bufsize) -{ - return tud_midi_n_read(0, 0, buffer, bufsize); -} - -static inline void tud_midi_read_flush (void) -{ - tud_midi_n_read_flush(0, 0); -} - -static inline uint32_t tud_midi_write (uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize) -{ - return tud_midi_n_write(0, jack_id, buffer, bufsize); -} - -static inline uint32_t tudi_midi_write24 (uint8_t jack_id, uint8_t b1, uint8_t b2, uint8_t b3) -{ - uint8_t msg[3] = { b1, b2, b3 }; - return tud_midi_write(jack_id, msg, 3); -} - -//--------------------------------------------------------------------+ -// Internal Class Driver API -//--------------------------------------------------------------------+ -void midid_init (void); -void midid_reset (uint8_t rhport); -bool midid_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length); -bool midid_control_request (uint8_t rhport, tusb_control_request_t const * request); -bool midid_control_complete (uint8_t rhport, tusb_control_request_t const * request); -bool midid_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes); - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_MIDI_DEVICE_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. + */ + +#ifndef _TUSB_MIDI_DEVICE_H_ +#define _TUSB_MIDI_DEVICE_H_ + +#include "common/tusb_common.h" +#include "device/usbd.h" + +#include "class/audio/audio.h" +#include "midi.h" + +//--------------------------------------------------------------------+ +// Class Driver Configuration +//--------------------------------------------------------------------+ +#ifndef CFG_TUD_MIDI_EPSIZE +#define CFG_TUD_MIDI_EPSIZE 64 +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/** \addtogroup MIDI_Serial Serial + * @{ + * \defgroup MIDI_Serial_Device Device + * @{ */ + +//--------------------------------------------------------------------+ +// Application API (Multiple Interfaces) +// CFG_TUD_MIDI > 1 +//--------------------------------------------------------------------+ +bool tud_midi_n_mounted (uint8_t itf); +uint32_t tud_midi_n_available (uint8_t itf, uint8_t jack_id); +uint32_t tud_midi_n_read (uint8_t itf, uint8_t jack_id, void* buffer, uint32_t bufsize); +void tud_midi_n_read_flush (uint8_t itf, uint8_t jack_id); +uint32_t tud_midi_n_write (uint8_t itf, uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize); + +static inline +uint32_t tud_midi_n_write24 (uint8_t itf, uint8_t jack_id, uint8_t b1, uint8_t b2, uint8_t b3); + +//--------------------------------------------------------------------+ +// Application API (Interface0) +//--------------------------------------------------------------------+ +static inline bool tud_midi_mounted (void); +static inline uint32_t tud_midi_available (void); +static inline uint32_t tud_midi_read (void* buffer, uint32_t bufsize); +static inline void tud_midi_read_flush (void); +static inline uint32_t tud_midi_write (uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize); +static inline uint32_t tudi_midi_write24 (uint8_t jack_id, uint8_t b1, uint8_t b2, uint8_t b3); + +//--------------------------------------------------------------------+ +// Application Callback API (weak is optional) +//--------------------------------------------------------------------+ +TU_ATTR_WEAK void tud_midi_rx_cb(uint8_t itf); + +//--------------------------------------------------------------------+ +// Inline Functions +//--------------------------------------------------------------------+ + +static inline uint32_t tud_midi_n_write24 (uint8_t itf, uint8_t jack_id, uint8_t b1, uint8_t b2, uint8_t b3) +{ + uint8_t msg[3] = { b1, b2, b3 }; + return tud_midi_n_write(itf, jack_id, msg, 3); +} + +static inline bool tud_midi_mounted (void) +{ + return tud_midi_n_mounted(0); +} + +static inline uint32_t tud_midi_available (void) +{ + return tud_midi_n_available(0, 0); +} + +static inline uint32_t tud_midi_read (void* buffer, uint32_t bufsize) +{ + return tud_midi_n_read(0, 0, buffer, bufsize); +} + +static inline void tud_midi_read_flush (void) +{ + tud_midi_n_read_flush(0, 0); +} + +static inline uint32_t tud_midi_write (uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize) +{ + return tud_midi_n_write(0, jack_id, buffer, bufsize); +} + +static inline uint32_t tudi_midi_write24 (uint8_t jack_id, uint8_t b1, uint8_t b2, uint8_t b3) +{ + uint8_t msg[3] = { b1, b2, b3 }; + return tud_midi_write(jack_id, msg, 3); +} + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +void midid_init (void); +void midid_reset (uint8_t rhport); +bool midid_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length); +bool midid_control_request (uint8_t rhport, tusb_control_request_t const * request); +bool midid_control_complete (uint8_t rhport, tusb_control_request_t const * request); +bool midid_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_MIDI_DEVICE_H_ */ + +/** @} */ +/** @} */ diff --git a/src/class/msc/msc.h b/src/class/msc/msc.h index d461513fc..cba8066b7 100644 --- a/src/class/msc/msc.h +++ b/src/class/msc/msc.h @@ -1,392 +1,392 @@ -/* - * 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_class - * \defgroup ClassDriver_MSC MassStorage (MSC) - * @{ */ - -/** \defgroup ClassDriver_MSC_Common Common Definitions - * @{ */ - -#ifndef _TUSB_MSC_H_ -#define _TUSB_MSC_H_ - -#include "common/tusb_common.h" - -#ifdef __cplusplus - extern "C" { -#endif - -//--------------------------------------------------------------------+ -// Mass Storage Class Constant -//--------------------------------------------------------------------+ -/// MassStorage Subclass -typedef enum -{ - MSC_SUBCLASS_RBC = 1 , ///< Reduced Block Commands (RBC) T10 Project 1240-D - MSC_SUBCLASS_SFF_MMC , ///< SFF-8020i, MMC-2 (ATAPI). Typically used by a CD/DVD device - MSC_SUBCLASS_QIC , ///< QIC-157. Typically used by a tape device - MSC_SUBCLASS_UFI , ///< UFI. Typically used by Floppy Disk Drive (FDD) device - MSC_SUBCLASS_SFF , ///< SFF-8070i. Can be used by Floppy Disk Drive (FDD) device - MSC_SUBCLASS_SCSI ///< SCSI transparent command set -}msc_subclass_type_t; - -enum { - MSC_CBW_SIGNATURE = 0x43425355, ///< Constant value of 43425355h (little endian) - MSC_CSW_SIGNATURE = 0x53425355 ///< Constant value of 53425355h (little endian) -}; - -/// \brief MassStorage Protocol. -/// \details CBI only approved to use with full-speed floopy disk & should not used with highspeed or device other than floopy -typedef enum -{ - MSC_PROTOCOL_CBI = 0 , ///< Control/Bulk/Interrupt protocol (with command completion interrupt) - MSC_PROTOCOL_CBI_NO_INTERRUPT = 1 , ///< Control/Bulk/Interrupt protocol (without command completion interrupt) - MSC_PROTOCOL_BOT = 0x50 ///< Bulk-Only Transport -}msc_protocol_type_t; - -/// MassStorage Class-Specific Control Request -typedef enum -{ - MSC_REQ_GET_MAX_LUN = 254, ///< The Get Max LUN device request is used to determine the number of logical units supported by the device. Logical Unit Numbers on the device shall be numbered contiguously starting from LUN 0 to a maximum LUN of 15 - MSC_REQ_RESET = 255 ///< This request is used to reset the mass storage device and its associated interface. This class-specific request shall ready the device for the next CBW from the host. -}msc_request_type_t; - -/// \brief Command Block Status Values -/// \details Indicates the success or failure of the command. The device shall set this byte to zero if the command completed -/// successfully. A non-zero value shall indicate a failure during command execution according to the following -typedef enum -{ - MSC_CSW_STATUS_PASSED = 0 , ///< MSC_CSW_STATUS_PASSED - MSC_CSW_STATUS_FAILED , ///< MSC_CSW_STATUS_FAILED - MSC_CSW_STATUS_PHASE_ERROR ///< MSC_CSW_STATUS_PHASE_ERROR -}msc_csw_status_t; - -/// Command Block Wrapper -typedef struct TU_ATTR_PACKED -{ - uint32_t signature; ///< Signature that helps identify this data packet as a CBW. The signature field shall contain the value 43425355h (little endian), indicating a CBW. - uint32_t tag; ///< Tag sent by the host. The device shall echo the contents of this field back to the host in the dCSWTagfield of the associated CSW. The dCSWTagpositively associates a CSW with the corresponding CBW. - uint32_t total_bytes; ///< The number of bytes of data that the host expects to transfer on the Bulk-In or Bulk-Out endpoint (as indicated by the Direction bit) during the execution of this command. If this field is zero, the device and the host shall transfer no data between the CBW and the associated CSW, and the device shall ignore the value of the Direction bit in bmCBWFlags. - uint8_t dir; ///< Bit 7 of this field define transfer direction \n - 0 : Data-Out from host to the device. \n - 1 : Data-In from the device to the host. - uint8_t lun; ///< The device Logical Unit Number (LUN) to which the command block is being sent. For devices that support multiple LUNs, the host shall place into this field the LUN to which this command block is addressed. Otherwise, the host shall set this field to zero. - uint8_t cmd_len; ///< The valid length of the CBWCBin bytes. This defines the valid length of the command block. The only legal values are 1 through 16 - uint8_t command[16]; ///< The command block to be executed by the device. The device shall interpret the first cmd_len bytes in this field as a command block -}msc_cbw_t; - -TU_VERIFY_STATIC(sizeof(msc_cbw_t) == 31, "size is not correct"); - -/// Command Status Wrapper -typedef struct TU_ATTR_PACKED -{ - uint32_t signature ; ///< Signature that helps identify this data packet as a CSW. The signature field shall contain the value 53425355h (little endian), indicating CSW. - uint32_t tag ; ///< The device shall set this field to the value received in the dCBWTag of the associated CBW. - uint32_t data_residue ; ///< For Data-Out the device shall report in the dCSWDataResiduethe difference between the amount of data expected as stated in the dCBWDataTransferLength, and the actual amount of data processed by the device. For Data-In the device shall report in the dCSWDataResiduethe difference between the amount of data expected as stated in the dCBWDataTransferLengthand the actual amount of relevant data sent by the device - uint8_t status ; ///< indicates the success or failure of the command. Values from \ref msc_csw_status_t -}msc_csw_t; - -TU_VERIFY_STATIC(sizeof(msc_csw_t) == 13, "size is not correct"); - -//--------------------------------------------------------------------+ -// SCSI Constant -//--------------------------------------------------------------------+ - -/// SCSI Command Operation Code -typedef enum -{ - SCSI_CMD_TEST_UNIT_READY = 0x00, ///< The SCSI Test Unit Ready command is used to determine if a device is ready to transfer data (read/write), i.e. if a disk has spun up, if a tape is loaded and ready etc. The device does not perform a self-test operation. - SCSI_CMD_INQUIRY = 0x12, ///< The SCSI Inquiry command is used to obtain basic information from a target device. - SCSI_CMD_MODE_SELECT_6 = 0x15, ///< provides a means for the application client to specify medium, logical unit, or peripheral device parameters to the device server. Device servers that implement the MODE SELECT(6) command shall also implement the MODE SENSE(6) command. Application clients should issue MODE SENSE(6) prior to each MODE SELECT(6) to determine supported mode pages, page lengths, and other parameters. - SCSI_CMD_MODE_SENSE_6 = 0x1A, ///< provides a means for a device server to report parameters to an application client. It is a complementary command to the MODE SELECT(6) command. Device servers that implement the MODE SENSE(6) command shall also implement the MODE SELECT(6) command. - SCSI_CMD_START_STOP_UNIT = 0x1B, - SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL = 0x1E, - SCSI_CMD_READ_CAPACITY_10 = 0x25, ///< The SCSI Read Capacity command is used to obtain data capacity information from a target device. - SCSI_CMD_REQUEST_SENSE = 0x03, ///< The SCSI Request Sense command is part of the SCSI computer protocol standard. This command is used to obtain sense data -- status/error information -- from a target device. - SCSI_CMD_READ_FORMAT_CAPACITY = 0x23, ///< The command allows the Host to request a list of the possible format capacities for an installed writable media. This command also has the capability to report the writable capacity for a media when it is installed - SCSI_CMD_READ_10 = 0x28, ///< The READ (10) command requests that the device server read the specified logical block(s) and transfer them to the data-in buffer. - SCSI_CMD_WRITE_10 = 0x2A, ///< The WRITE (10) command requests thatthe device server transfer the specified logical block(s) from the data-out buffer and write them. -}scsi_cmd_type_t; - -/// SCSI Sense Key -typedef enum -{ - SCSI_SENSE_NONE = 0x00, ///< no specific Sense Key. This would be the case for a successful command - SCSI_SENSE_RECOVERED_ERROR = 0x01, ///< ndicates the last command completed successfully with some recovery action performed by the disc drive. - SCSI_SENSE_NOT_READY = 0x02, ///< Indicates the logical unit addressed cannot be accessed. - SCSI_SENSE_MEDIUM_ERROR = 0x03, ///< Indicates the command terminated with a non-recovered error condition. - SCSI_SENSE_HARDWARE_ERROR = 0x04, ///< Indicates the disc drive detected a nonrecoverable hardware failure while performing the command or during a self test. - SCSI_SENSE_ILLEGAL_REQUEST = 0x05, ///< Indicates an illegal parameter in the command descriptor block or in the additional parameters - SCSI_SENSE_UNIT_ATTENTION = 0x06, ///< Indicates the disc drive may have been reset. - SCSI_SENSE_DATA_PROTECT = 0x07, ///< Indicates that a command that reads or writes the medium was attempted on a block that is protected from this operation. The read or write operation is not performed. - SCSI_SENSE_FIRMWARE_ERROR = 0x08, ///< Vendor specific sense key. - SCSI_SENSE_ABORTED_COMMAND = 0x0b, ///< Indicates the disc drive aborted the command. - SCSI_SENSE_EQUAL = 0x0c, ///< Indicates a SEARCH DATA command has satisfied an equal comparison. - SCSI_SENSE_VOLUME_OVERFLOW = 0x0d, ///< Indicates a buffered peripheral device has reached the end of medium partition and data remains in the buffer that has not been written to the medium. - SCSI_SENSE_MISCOMPARE = 0x0e ///< ndicates that the source data did not match the data read from the medium. -}scsi_sense_key_type_t; - -//--------------------------------------------------------------------+ -// SCSI Primary Command (SPC-4) -//--------------------------------------------------------------------+ - -/// SCSI Test Unit Ready Command -typedef struct TU_ATTR_PACKED -{ - uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_TEST_UNIT_READY - uint8_t lun ; ///< Logical Unit - uint8_t reserved[3] ; - uint8_t control ; -} scsi_test_unit_ready_t; - -TU_VERIFY_STATIC(sizeof(scsi_test_unit_ready_t) == 6, "size is not correct"); - -/// SCSI Inquiry Command -typedef struct TU_ATTR_PACKED -{ - uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_INQUIRY - uint8_t reserved1 ; - uint8_t page_code ; - uint8_t reserved2 ; - uint8_t alloc_length ; ///< specifies the maximum number of bytes that USB host has allocated in the Data-In Buffer. An allocation length of zero specifies that no data shall be transferred. - uint8_t control ; -} scsi_inquiry_t, scsi_request_sense_t; - -TU_VERIFY_STATIC(sizeof(scsi_inquiry_t) == 6, "size is not correct"); - -/// SCSI Inquiry Response Data -typedef struct TU_ATTR_PACKED -{ - uint8_t peripheral_device_type : 5; - uint8_t peripheral_qualifier : 3; - - uint8_t : 7; - uint8_t is_removable : 1; - - uint8_t version; - - uint8_t response_data_format : 4; - uint8_t hierarchical_support : 1; - uint8_t normal_aca : 1; - uint8_t : 2; - - uint8_t additional_length; - - uint8_t protect : 1; - uint8_t : 2; - uint8_t third_party_copy : 1; - uint8_t target_port_group_support : 2; - uint8_t access_control_coordinator : 1; - uint8_t scc_support : 1; - - uint8_t addr16 : 1; - uint8_t : 3; - uint8_t multi_port : 1; - uint8_t : 1; // vendor specific - uint8_t enclosure_service : 1; - uint8_t : 1; - - uint8_t : 1; // vendor specific - uint8_t cmd_que : 1; - uint8_t : 2; - uint8_t sync : 1; - uint8_t wbus16 : 1; - uint8_t : 2; - - uint8_t vendor_id[8] ; ///< 8 bytes of ASCII data identifying the vendor of the product. - uint8_t product_id[16]; ///< 16 bytes of ASCII data defined by the vendor. - uint8_t product_rev[4]; ///< 4 bytes of ASCII data defined by the vendor. -} scsi_inquiry_resp_t; - -TU_VERIFY_STATIC(sizeof(scsi_inquiry_resp_t) == 36, "size is not correct"); - - -typedef struct TU_ATTR_PACKED -{ - uint8_t response_code : 7; ///< 70h - current errors, Fixed Format 71h - deferred errors, Fixed Format - uint8_t valid : 1; - - uint8_t reserved; - - uint8_t sense_key : 4; - uint8_t : 1; - uint8_t ili : 1; ///< Incorrect length indicator - uint8_t end_of_medium : 1; - uint8_t filemark : 1; - - uint32_t information; - uint8_t add_sense_len; - uint32_t command_specific_info; - uint8_t add_sense_code; - uint8_t add_sense_qualifier; - uint8_t field_replaceable_unit_code; - - uint8_t sense_key_specific[3]; ///< sense key specific valid bit is bit 7 of key[0], aka MSB in Big Endian layout - -} scsi_sense_fixed_resp_t; - -TU_VERIFY_STATIC(sizeof(scsi_sense_fixed_resp_t) == 18, "size is not correct"); - -typedef struct TU_ATTR_PACKED -{ - uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_MODE_SENSE_6 - - uint8_t : 3; - uint8_t disable_block_descriptor : 1; - uint8_t : 0; - - uint8_t page_code : 6; - uint8_t page_control : 2; - - uint8_t subpage_code; - uint8_t alloc_length; - uint8_t control; -} scsi_mode_sense6_t; - -TU_VERIFY_STATIC( sizeof(scsi_mode_sense6_t) == 6, "size is not correct"); - -// This is only a Mode parameter header(6). -typedef struct TU_ATTR_PACKED -{ - uint8_t data_len; - uint8_t medium_type; - - uint8_t reserved : 7; - bool write_protected : 1; - - uint8_t block_descriptor_len; -} scsi_mode_sense6_resp_t; - -TU_VERIFY_STATIC( sizeof(scsi_mode_sense6_resp_t) == 4, "size is not correct"); - -typedef struct TU_ATTR_PACKED -{ - uint8_t cmd_code; ///< SCSI OpCode for \ref SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL - uint8_t reserved[3]; - uint8_t prohibit_removal; - uint8_t control; -} scsi_prevent_allow_medium_removal_t; - -TU_VERIFY_STATIC( sizeof(scsi_prevent_allow_medium_removal_t) == 6, "size is not correct"); - -typedef struct TU_ATTR_PACKED -{ - uint8_t cmd_code; - - uint8_t immded : 1; - uint8_t : 7; - - uint8_t TU_RESERVED; - - uint8_t power_condition_mod : 4; - uint8_t : 4; - - uint8_t start : 1; - uint8_t load_eject : 1; - uint8_t no_flush : 1; - uint8_t : 1; - uint8_t power_condition : 4; - - uint8_t control; -} scsi_start_stop_unit_t; - -TU_VERIFY_STATIC( sizeof(scsi_start_stop_unit_t) == 6, "size is not correct"); - -//--------------------------------------------------------------------+ -// SCSI MMC -//--------------------------------------------------------------------+ -/// SCSI Read Format Capacity: Write Capacity -typedef struct TU_ATTR_PACKED -{ - uint8_t cmd_code; - uint8_t reserved[6]; - uint16_t alloc_length; - uint8_t control; -} scsi_read_format_capacity_t; - -TU_VERIFY_STATIC( sizeof(scsi_read_format_capacity_t) == 10, "size is not correct"); - -typedef struct TU_ATTR_PACKED{ - uint8_t reserved[3]; - uint8_t list_length; /// must be 8*n, length in bytes of formattable capacity descriptor followed it. - - uint32_t block_num; /// Number of Logical Blocks - uint8_t descriptor_type; // 00: reserved, 01 unformatted media , 10 Formatted media, 11 No media present - - uint8_t reserved2; - uint16_t block_size_u16; - -} scsi_read_format_capacity_data_t; - -TU_VERIFY_STATIC( sizeof(scsi_read_format_capacity_data_t) == 12, "size is not correct"); - -//--------------------------------------------------------------------+ -// SCSI Block Command (SBC-3) -// NOTE: All data in SCSI command are in Big Endian -//--------------------------------------------------------------------+ - -/// SCSI Read Capacity 10 Command: Read Capacity -typedef struct TU_ATTR_PACKED -{ - uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_READ_CAPACITY_10 - uint8_t reserved1 ; - uint32_t lba ; ///< The first Logical Block Address (LBA) accessed by this command - uint16_t reserved2 ; - uint8_t partial_medium_indicator ; - uint8_t control ; -} scsi_read_capacity10_t; - -TU_VERIFY_STATIC(sizeof(scsi_read_capacity10_t) == 10, "size is not correct"); - -/// SCSI Read Capacity 10 Response Data -typedef struct { - uint32_t last_lba ; ///< The last Logical Block Address of the device - uint32_t block_size ; ///< Block size in bytes -} scsi_read_capacity10_resp_t; - -TU_VERIFY_STATIC(sizeof(scsi_read_capacity10_resp_t) == 8, "size is not correct"); - -/// SCSI Read 10 Command -typedef struct TU_ATTR_PACKED -{ - uint8_t cmd_code ; ///< SCSI OpCode - uint8_t reserved ; // has LUN according to wiki - uint32_t lba ; ///< The first Logical Block Address (LBA) accessed by this command - uint8_t reserved2 ; - uint16_t block_count ; ///< Number of Blocks used by this command - uint8_t control ; -} scsi_read10_t, scsi_write10_t; - -TU_VERIFY_STATIC(sizeof(scsi_read10_t) == 10, "size is not correct"); -TU_VERIFY_STATIC(sizeof(scsi_write10_t) == 10, "size is not correct"); - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_MSC_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_class + * \defgroup ClassDriver_MSC MassStorage (MSC) + * @{ */ + +/** \defgroup ClassDriver_MSC_Common Common Definitions + * @{ */ + +#ifndef _TUSB_MSC_H_ +#define _TUSB_MSC_H_ + +#include "common/tusb_common.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Mass Storage Class Constant +//--------------------------------------------------------------------+ +/// MassStorage Subclass +typedef enum +{ + MSC_SUBCLASS_RBC = 1 , ///< Reduced Block Commands (RBC) T10 Project 1240-D + MSC_SUBCLASS_SFF_MMC , ///< SFF-8020i, MMC-2 (ATAPI). Typically used by a CD/DVD device + MSC_SUBCLASS_QIC , ///< QIC-157. Typically used by a tape device + MSC_SUBCLASS_UFI , ///< UFI. Typically used by Floppy Disk Drive (FDD) device + MSC_SUBCLASS_SFF , ///< SFF-8070i. Can be used by Floppy Disk Drive (FDD) device + MSC_SUBCLASS_SCSI ///< SCSI transparent command set +}msc_subclass_type_t; + +enum { + MSC_CBW_SIGNATURE = 0x43425355, ///< Constant value of 43425355h (little endian) + MSC_CSW_SIGNATURE = 0x53425355 ///< Constant value of 53425355h (little endian) +}; + +/// \brief MassStorage Protocol. +/// \details CBI only approved to use with full-speed floopy disk & should not used with highspeed or device other than floopy +typedef enum +{ + MSC_PROTOCOL_CBI = 0 , ///< Control/Bulk/Interrupt protocol (with command completion interrupt) + MSC_PROTOCOL_CBI_NO_INTERRUPT = 1 , ///< Control/Bulk/Interrupt protocol (without command completion interrupt) + MSC_PROTOCOL_BOT = 0x50 ///< Bulk-Only Transport +}msc_protocol_type_t; + +/// MassStorage Class-Specific Control Request +typedef enum +{ + MSC_REQ_GET_MAX_LUN = 254, ///< The Get Max LUN device request is used to determine the number of logical units supported by the device. Logical Unit Numbers on the device shall be numbered contiguously starting from LUN 0 to a maximum LUN of 15 + MSC_REQ_RESET = 255 ///< This request is used to reset the mass storage device and its associated interface. This class-specific request shall ready the device for the next CBW from the host. +}msc_request_type_t; + +/// \brief Command Block Status Values +/// \details Indicates the success or failure of the command. The device shall set this byte to zero if the command completed +/// successfully. A non-zero value shall indicate a failure during command execution according to the following +typedef enum +{ + MSC_CSW_STATUS_PASSED = 0 , ///< MSC_CSW_STATUS_PASSED + MSC_CSW_STATUS_FAILED , ///< MSC_CSW_STATUS_FAILED + MSC_CSW_STATUS_PHASE_ERROR ///< MSC_CSW_STATUS_PHASE_ERROR +}msc_csw_status_t; + +/// Command Block Wrapper +typedef struct TU_ATTR_PACKED +{ + uint32_t signature; ///< Signature that helps identify this data packet as a CBW. The signature field shall contain the value 43425355h (little endian), indicating a CBW. + uint32_t tag; ///< Tag sent by the host. The device shall echo the contents of this field back to the host in the dCSWTagfield of the associated CSW. The dCSWTagpositively associates a CSW with the corresponding CBW. + uint32_t total_bytes; ///< The number of bytes of data that the host expects to transfer on the Bulk-In or Bulk-Out endpoint (as indicated by the Direction bit) during the execution of this command. If this field is zero, the device and the host shall transfer no data between the CBW and the associated CSW, and the device shall ignore the value of the Direction bit in bmCBWFlags. + uint8_t dir; ///< Bit 7 of this field define transfer direction \n - 0 : Data-Out from host to the device. \n - 1 : Data-In from the device to the host. + uint8_t lun; ///< The device Logical Unit Number (LUN) to which the command block is being sent. For devices that support multiple LUNs, the host shall place into this field the LUN to which this command block is addressed. Otherwise, the host shall set this field to zero. + uint8_t cmd_len; ///< The valid length of the CBWCBin bytes. This defines the valid length of the command block. The only legal values are 1 through 16 + uint8_t command[16]; ///< The command block to be executed by the device. The device shall interpret the first cmd_len bytes in this field as a command block +}msc_cbw_t; + +TU_VERIFY_STATIC(sizeof(msc_cbw_t) == 31, "size is not correct"); + +/// Command Status Wrapper +typedef struct TU_ATTR_PACKED +{ + uint32_t signature ; ///< Signature that helps identify this data packet as a CSW. The signature field shall contain the value 53425355h (little endian), indicating CSW. + uint32_t tag ; ///< The device shall set this field to the value received in the dCBWTag of the associated CBW. + uint32_t data_residue ; ///< For Data-Out the device shall report in the dCSWDataResiduethe difference between the amount of data expected as stated in the dCBWDataTransferLength, and the actual amount of data processed by the device. For Data-In the device shall report in the dCSWDataResiduethe difference between the amount of data expected as stated in the dCBWDataTransferLengthand the actual amount of relevant data sent by the device + uint8_t status ; ///< indicates the success or failure of the command. Values from \ref msc_csw_status_t +}msc_csw_t; + +TU_VERIFY_STATIC(sizeof(msc_csw_t) == 13, "size is not correct"); + +//--------------------------------------------------------------------+ +// SCSI Constant +//--------------------------------------------------------------------+ + +/// SCSI Command Operation Code +typedef enum +{ + SCSI_CMD_TEST_UNIT_READY = 0x00, ///< The SCSI Test Unit Ready command is used to determine if a device is ready to transfer data (read/write), i.e. if a disk has spun up, if a tape is loaded and ready etc. The device does not perform a self-test operation. + SCSI_CMD_INQUIRY = 0x12, ///< The SCSI Inquiry command is used to obtain basic information from a target device. + SCSI_CMD_MODE_SELECT_6 = 0x15, ///< provides a means for the application client to specify medium, logical unit, or peripheral device parameters to the device server. Device servers that implement the MODE SELECT(6) command shall also implement the MODE SENSE(6) command. Application clients should issue MODE SENSE(6) prior to each MODE SELECT(6) to determine supported mode pages, page lengths, and other parameters. + SCSI_CMD_MODE_SENSE_6 = 0x1A, ///< provides a means for a device server to report parameters to an application client. It is a complementary command to the MODE SELECT(6) command. Device servers that implement the MODE SENSE(6) command shall also implement the MODE SELECT(6) command. + SCSI_CMD_START_STOP_UNIT = 0x1B, + SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL = 0x1E, + SCSI_CMD_READ_CAPACITY_10 = 0x25, ///< The SCSI Read Capacity command is used to obtain data capacity information from a target device. + SCSI_CMD_REQUEST_SENSE = 0x03, ///< The SCSI Request Sense command is part of the SCSI computer protocol standard. This command is used to obtain sense data -- status/error information -- from a target device. + SCSI_CMD_READ_FORMAT_CAPACITY = 0x23, ///< The command allows the Host to request a list of the possible format capacities for an installed writable media. This command also has the capability to report the writable capacity for a media when it is installed + SCSI_CMD_READ_10 = 0x28, ///< The READ (10) command requests that the device server read the specified logical block(s) and transfer them to the data-in buffer. + SCSI_CMD_WRITE_10 = 0x2A, ///< The WRITE (10) command requests thatthe device server transfer the specified logical block(s) from the data-out buffer and write them. +}scsi_cmd_type_t; + +/// SCSI Sense Key +typedef enum +{ + SCSI_SENSE_NONE = 0x00, ///< no specific Sense Key. This would be the case for a successful command + SCSI_SENSE_RECOVERED_ERROR = 0x01, ///< ndicates the last command completed successfully with some recovery action performed by the disc drive. + SCSI_SENSE_NOT_READY = 0x02, ///< Indicates the logical unit addressed cannot be accessed. + SCSI_SENSE_MEDIUM_ERROR = 0x03, ///< Indicates the command terminated with a non-recovered error condition. + SCSI_SENSE_HARDWARE_ERROR = 0x04, ///< Indicates the disc drive detected a nonrecoverable hardware failure while performing the command or during a self test. + SCSI_SENSE_ILLEGAL_REQUEST = 0x05, ///< Indicates an illegal parameter in the command descriptor block or in the additional parameters + SCSI_SENSE_UNIT_ATTENTION = 0x06, ///< Indicates the disc drive may have been reset. + SCSI_SENSE_DATA_PROTECT = 0x07, ///< Indicates that a command that reads or writes the medium was attempted on a block that is protected from this operation. The read or write operation is not performed. + SCSI_SENSE_FIRMWARE_ERROR = 0x08, ///< Vendor specific sense key. + SCSI_SENSE_ABORTED_COMMAND = 0x0b, ///< Indicates the disc drive aborted the command. + SCSI_SENSE_EQUAL = 0x0c, ///< Indicates a SEARCH DATA command has satisfied an equal comparison. + SCSI_SENSE_VOLUME_OVERFLOW = 0x0d, ///< Indicates a buffered peripheral device has reached the end of medium partition and data remains in the buffer that has not been written to the medium. + SCSI_SENSE_MISCOMPARE = 0x0e ///< ndicates that the source data did not match the data read from the medium. +}scsi_sense_key_type_t; + +//--------------------------------------------------------------------+ +// SCSI Primary Command (SPC-4) +//--------------------------------------------------------------------+ + +/// SCSI Test Unit Ready Command +typedef struct TU_ATTR_PACKED +{ + uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_TEST_UNIT_READY + uint8_t lun ; ///< Logical Unit + uint8_t reserved[3] ; + uint8_t control ; +} scsi_test_unit_ready_t; + +TU_VERIFY_STATIC(sizeof(scsi_test_unit_ready_t) == 6, "size is not correct"); + +/// SCSI Inquiry Command +typedef struct TU_ATTR_PACKED +{ + uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_INQUIRY + uint8_t reserved1 ; + uint8_t page_code ; + uint8_t reserved2 ; + uint8_t alloc_length ; ///< specifies the maximum number of bytes that USB host has allocated in the Data-In Buffer. An allocation length of zero specifies that no data shall be transferred. + uint8_t control ; +} scsi_inquiry_t, scsi_request_sense_t; + +TU_VERIFY_STATIC(sizeof(scsi_inquiry_t) == 6, "size is not correct"); + +/// SCSI Inquiry Response Data +typedef struct TU_ATTR_PACKED +{ + uint8_t peripheral_device_type : 5; + uint8_t peripheral_qualifier : 3; + + uint8_t : 7; + uint8_t is_removable : 1; + + uint8_t version; + + uint8_t response_data_format : 4; + uint8_t hierarchical_support : 1; + uint8_t normal_aca : 1; + uint8_t : 2; + + uint8_t additional_length; + + uint8_t protect : 1; + uint8_t : 2; + uint8_t third_party_copy : 1; + uint8_t target_port_group_support : 2; + uint8_t access_control_coordinator : 1; + uint8_t scc_support : 1; + + uint8_t addr16 : 1; + uint8_t : 3; + uint8_t multi_port : 1; + uint8_t : 1; // vendor specific + uint8_t enclosure_service : 1; + uint8_t : 1; + + uint8_t : 1; // vendor specific + uint8_t cmd_que : 1; + uint8_t : 2; + uint8_t sync : 1; + uint8_t wbus16 : 1; + uint8_t : 2; + + uint8_t vendor_id[8] ; ///< 8 bytes of ASCII data identifying the vendor of the product. + uint8_t product_id[16]; ///< 16 bytes of ASCII data defined by the vendor. + uint8_t product_rev[4]; ///< 4 bytes of ASCII data defined by the vendor. +} scsi_inquiry_resp_t; + +TU_VERIFY_STATIC(sizeof(scsi_inquiry_resp_t) == 36, "size is not correct"); + + +typedef struct TU_ATTR_PACKED +{ + uint8_t response_code : 7; ///< 70h - current errors, Fixed Format 71h - deferred errors, Fixed Format + uint8_t valid : 1; + + uint8_t reserved; + + uint8_t sense_key : 4; + uint8_t : 1; + uint8_t ili : 1; ///< Incorrect length indicator + uint8_t end_of_medium : 1; + uint8_t filemark : 1; + + uint32_t information; + uint8_t add_sense_len; + uint32_t command_specific_info; + uint8_t add_sense_code; + uint8_t add_sense_qualifier; + uint8_t field_replaceable_unit_code; + + uint8_t sense_key_specific[3]; ///< sense key specific valid bit is bit 7 of key[0], aka MSB in Big Endian layout + +} scsi_sense_fixed_resp_t; + +TU_VERIFY_STATIC(sizeof(scsi_sense_fixed_resp_t) == 18, "size is not correct"); + +typedef struct TU_ATTR_PACKED +{ + uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_MODE_SENSE_6 + + uint8_t : 3; + uint8_t disable_block_descriptor : 1; + uint8_t : 0; + + uint8_t page_code : 6; + uint8_t page_control : 2; + + uint8_t subpage_code; + uint8_t alloc_length; + uint8_t control; +} scsi_mode_sense6_t; + +TU_VERIFY_STATIC( sizeof(scsi_mode_sense6_t) == 6, "size is not correct"); + +// This is only a Mode parameter header(6). +typedef struct TU_ATTR_PACKED +{ + uint8_t data_len; + uint8_t medium_type; + + uint8_t reserved : 7; + bool write_protected : 1; + + uint8_t block_descriptor_len; +} scsi_mode_sense6_resp_t; + +TU_VERIFY_STATIC( sizeof(scsi_mode_sense6_resp_t) == 4, "size is not correct"); + +typedef struct TU_ATTR_PACKED +{ + uint8_t cmd_code; ///< SCSI OpCode for \ref SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL + uint8_t reserved[3]; + uint8_t prohibit_removal; + uint8_t control; +} scsi_prevent_allow_medium_removal_t; + +TU_VERIFY_STATIC( sizeof(scsi_prevent_allow_medium_removal_t) == 6, "size is not correct"); + +typedef struct TU_ATTR_PACKED +{ + uint8_t cmd_code; + + uint8_t immded : 1; + uint8_t : 7; + + uint8_t TU_RESERVED; + + uint8_t power_condition_mod : 4; + uint8_t : 4; + + uint8_t start : 1; + uint8_t load_eject : 1; + uint8_t no_flush : 1; + uint8_t : 1; + uint8_t power_condition : 4; + + uint8_t control; +} scsi_start_stop_unit_t; + +TU_VERIFY_STATIC( sizeof(scsi_start_stop_unit_t) == 6, "size is not correct"); + +//--------------------------------------------------------------------+ +// SCSI MMC +//--------------------------------------------------------------------+ +/// SCSI Read Format Capacity: Write Capacity +typedef struct TU_ATTR_PACKED +{ + uint8_t cmd_code; + uint8_t reserved[6]; + uint16_t alloc_length; + uint8_t control; +} scsi_read_format_capacity_t; + +TU_VERIFY_STATIC( sizeof(scsi_read_format_capacity_t) == 10, "size is not correct"); + +typedef struct TU_ATTR_PACKED{ + uint8_t reserved[3]; + uint8_t list_length; /// must be 8*n, length in bytes of formattable capacity descriptor followed it. + + uint32_t block_num; /// Number of Logical Blocks + uint8_t descriptor_type; // 00: reserved, 01 unformatted media , 10 Formatted media, 11 No media present + + uint8_t reserved2; + uint16_t block_size_u16; + +} scsi_read_format_capacity_data_t; + +TU_VERIFY_STATIC( sizeof(scsi_read_format_capacity_data_t) == 12, "size is not correct"); + +//--------------------------------------------------------------------+ +// SCSI Block Command (SBC-3) +// NOTE: All data in SCSI command are in Big Endian +//--------------------------------------------------------------------+ + +/// SCSI Read Capacity 10 Command: Read Capacity +typedef struct TU_ATTR_PACKED +{ + uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_READ_CAPACITY_10 + uint8_t reserved1 ; + uint32_t lba ; ///< The first Logical Block Address (LBA) accessed by this command + uint16_t reserved2 ; + uint8_t partial_medium_indicator ; + uint8_t control ; +} scsi_read_capacity10_t; + +TU_VERIFY_STATIC(sizeof(scsi_read_capacity10_t) == 10, "size is not correct"); + +/// SCSI Read Capacity 10 Response Data +typedef struct { + uint32_t last_lba ; ///< The last Logical Block Address of the device + uint32_t block_size ; ///< Block size in bytes +} scsi_read_capacity10_resp_t; + +TU_VERIFY_STATIC(sizeof(scsi_read_capacity10_resp_t) == 8, "size is not correct"); + +/// SCSI Read 10 Command +typedef struct TU_ATTR_PACKED +{ + uint8_t cmd_code ; ///< SCSI OpCode + uint8_t reserved ; // has LUN according to wiki + uint32_t lba ; ///< The first Logical Block Address (LBA) accessed by this command + uint8_t reserved2 ; + uint16_t block_count ; ///< Number of Blocks used by this command + uint8_t control ; +} scsi_read10_t, scsi_write10_t; + +TU_VERIFY_STATIC(sizeof(scsi_read10_t) == 10, "size is not correct"); +TU_VERIFY_STATIC(sizeof(scsi_write10_t) == 10, "size is not correct"); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_MSC_H_ */ + +/// @} +/// @} diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c index bef5d599b..3ff0eba92 100644 --- a/src/class/msc/msc_device.c +++ b/src/class/msc/msc_device.c @@ -1,700 +1,700 @@ -/* - * 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 && CFG_TUD_MSC) - -#include "common/tusb_common.h" -#include "msc_device.h" -#include "device/usbd_pvt.h" - -//--------------------------------------------------------------------+ -// MACRO CONSTANT TYPEDEF -//--------------------------------------------------------------------+ -enum -{ - MSC_STAGE_CMD = 0, - MSC_STAGE_DATA, - MSC_STAGE_STATUS, - MSC_STAGE_STATUS_SENT -}; - -typedef struct -{ - // TODO optimize alignment - CFG_TUSB_MEM_ALIGN msc_cbw_t cbw; - CFG_TUSB_MEM_ALIGN msc_csw_t csw; - - uint8_t itf_num; - uint8_t ep_in; - uint8_t ep_out; - - // Bulk Only Transfer (BOT) Protocol - uint8_t stage; - uint32_t total_len; - uint32_t xferred_len; // numbered of bytes transferred so far in the Data Stage - - // Sense Response Data - uint8_t sense_key; - uint8_t add_sense_code; - uint8_t add_sense_qualifier; -}mscd_interface_t; - -CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static mscd_interface_t _mscd_itf; -CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _mscd_buf[CFG_TUD_MSC_BUFSIZE]; - -//--------------------------------------------------------------------+ -// INTERNAL OBJECT & FUNCTION DECLARATION -//--------------------------------------------------------------------+ -static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc); -static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc); - -static inline uint32_t rdwr10_get_lba(uint8_t const command[]) -{ - // read10 & write10 has the same format - scsi_write10_t* p_rdwr10 = (scsi_write10_t*) command; - - // copy first to prevent mis-aligned access - uint32_t lba; - memcpy(&lba, &p_rdwr10->lba, 4); - - // lba is in Big Endian format - return tu_ntohl(lba); -} - -static inline uint16_t rdwr10_get_blockcount(uint8_t const command[]) -{ - // read10 & write10 has the same format - scsi_write10_t* p_rdwr10 = (scsi_write10_t*) command; - - // copy first to prevent mis-aligned access - uint16_t block_count; - memcpy(&block_count, &p_rdwr10->block_count, 2); - - return tu_ntohs(block_count); -} - -//--------------------------------------------------------------------+ -// Debug -//--------------------------------------------------------------------+ -#if CFG_TUSB_DEBUG >= 2 - -static lookup_entry_t const _msc_scsi_cmd_lookup[] = -{ - { .key = SCSI_CMD_TEST_UNIT_READY , .data = "Test Unit Ready" }, - { .key = SCSI_CMD_INQUIRY , .data = "Inquiry" }, - { .key = SCSI_CMD_MODE_SELECT_6 , .data = "Mode_Select 6" }, - { .key = SCSI_CMD_MODE_SENSE_6 , .data = "Mode_Sense 6" }, - { .key = SCSI_CMD_START_STOP_UNIT , .data = "Start Stop Unit" }, - { .key = SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL , .data = "Prevent Allow Medium Removal" }, - { .key = SCSI_CMD_READ_CAPACITY_10 , .data = "Read Capacity10" }, - { .key = SCSI_CMD_REQUEST_SENSE , .data = "Request Sense" }, - { .key = SCSI_CMD_READ_FORMAT_CAPACITY , .data = "Read Format Capacity" }, - { .key = SCSI_CMD_READ_10 , .data = "Read10" }, - { .key = SCSI_CMD_WRITE_10 , .data = "Write10" } -}; - -static lookup_table_t const _msc_scsi_cmd_table = -{ - .count = TU_ARRAY_SIZE(_msc_scsi_cmd_lookup), - .items = _msc_scsi_cmd_lookup -}; - -#endif - -//--------------------------------------------------------------------+ -// APPLICATION API -//--------------------------------------------------------------------+ -bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier) -{ - (void) lun; - - _mscd_itf.sense_key = sense_key; - _mscd_itf.add_sense_code = add_sense_code; - _mscd_itf.add_sense_qualifier = add_sense_qualifier; - - return true; -} - -//--------------------------------------------------------------------+ -// USBD Driver API -//--------------------------------------------------------------------+ -void mscd_init(void) -{ - tu_memclr(&_mscd_itf, sizeof(mscd_interface_t)); -} - -void mscd_reset(uint8_t rhport) -{ - (void) rhport; - tu_memclr(&_mscd_itf, sizeof(mscd_interface_t)); -} - -bool mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_len) -{ - // only support SCSI's BOT protocol - TU_ASSERT(MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass && - MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol); - - mscd_interface_t * p_msc = &_mscd_itf; - - // Open endpoint pair - TU_ASSERT( usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in) ); - - p_msc->itf_num = itf_desc->bInterfaceNumber; - (*p_len) = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t); - - // Prepare for Command Block Wrapper - TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) ); - - return true; -} - -// Handle class control request -// return false to stall control endpoint (e.g unsupported request) -bool mscd_control_request(uint8_t rhport, tusb_control_request_t const * p_request) -{ - // Handle class request only - TU_VERIFY(p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); - - switch ( p_request->bRequest ) - { - case MSC_REQ_RESET: - // TODO: Actually reset interface. - tud_control_status(rhport, p_request); - break; - - case MSC_REQ_GET_MAX_LUN: - { - uint8_t maxlun = 1; - if (tud_msc_get_maxlun_cb) maxlun = tud_msc_get_maxlun_cb(); - TU_VERIFY(maxlun); - - // MAX LUN is minus 1 by specs - maxlun--; - - tud_control_xfer(rhport, p_request, &maxlun, 1); - } - break; - - default: return false; // stall unsupported request - } - - return true; -} - -// Invoked when class request DATA stage is finished. -// return false to stall control endpoint (e.g Host send non-sense DATA) -bool mscd_control_complete(uint8_t rhport, tusb_control_request_t const * request) -{ - (void) rhport; - (void) request; - - // nothing to do - return true; -} - -// return response's length (copied to buffer). Negative if it is not an built-in command or indicate Failed status (CSW) -// In case of a failed status, sense key must be set for reason of failure -int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize) -{ - (void) bufsize; // TODO refractor later - int32_t resplen; - - switch ( scsi_cmd[0] ) - { - case SCSI_CMD_TEST_UNIT_READY: - resplen = 0; - if ( !tud_msc_test_unit_ready_cb(lun) ) - { - // Failed status response - resplen = - 1; - - // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable - if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00); - } - break; - - case SCSI_CMD_START_STOP_UNIT: - resplen = 0; - - if (tud_msc_start_stop_cb) - { - scsi_start_stop_unit_t const * start_stop = (scsi_start_stop_unit_t const *) scsi_cmd; - if ( !tud_msc_start_stop_cb(lun, start_stop->power_condition, start_stop->start, start_stop->load_eject) ) - { - // Failed status response - resplen = - 1; - - // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable - if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00); - } - } - break; - - case SCSI_CMD_READ_CAPACITY_10: - { - uint32_t block_count; - uint32_t block_size; - uint16_t block_size_u16; - - tud_msc_capacity_cb(lun, &block_count, &block_size_u16); - block_size = (uint32_t) block_size_u16; - - // Invalid block size/count from callback, possibly unit is not ready - // stall this request, set sense key to NOT READY - if (block_count == 0 || block_size == 0) - { - resplen = -1; - - // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable - if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00); - }else - { - scsi_read_capacity10_resp_t read_capa10; - - read_capa10.last_lba = tu_htonl(block_count-1); - read_capa10.block_size = tu_htonl(block_size); - - resplen = sizeof(read_capa10); - memcpy(buffer, &read_capa10, resplen); - } - } - break; - - case SCSI_CMD_READ_FORMAT_CAPACITY: - { - scsi_read_format_capacity_data_t read_fmt_capa = - { - .list_length = 8, - .block_num = 0, - .descriptor_type = 2, // formatted media - .block_size_u16 = 0 - }; - - uint32_t block_count; - uint16_t block_size; - - tud_msc_capacity_cb(lun, &block_count, &block_size); - - // Invalid block size/count from callback, possibly unit is not ready - // stall this request, set sense key to NOT READY - if (block_count == 0 || block_size == 0) - { - resplen = -1; - - // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable - if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00); - }else - { - read_fmt_capa.block_num = tu_htonl(block_count); - read_fmt_capa.block_size_u16 = tu_htons(block_size); - - resplen = sizeof(read_fmt_capa); - memcpy(buffer, &read_fmt_capa, resplen); - } - } - break; - - case SCSI_CMD_INQUIRY: - { - scsi_inquiry_resp_t inquiry_rsp = - { - .is_removable = 1, - .version = 2, - .response_data_format = 2, - }; - - // vendor_id, product_id, product_rev is space padded string - memset(inquiry_rsp.vendor_id , ' ', sizeof(inquiry_rsp.vendor_id)); - memset(inquiry_rsp.product_id , ' ', sizeof(inquiry_rsp.product_id)); - memset(inquiry_rsp.product_rev, ' ', sizeof(inquiry_rsp.product_rev)); - - tud_msc_inquiry_cb(lun, inquiry_rsp.vendor_id, inquiry_rsp.product_id, inquiry_rsp.product_rev); - - resplen = sizeof(inquiry_rsp); - memcpy(buffer, &inquiry_rsp, resplen); - } - break; - - case SCSI_CMD_MODE_SENSE_6: - { - scsi_mode_sense6_resp_t mode_resp = - { - .data_len = 3, - .medium_type = 0, - .write_protected = false, - .reserved = 0, - .block_descriptor_len = 0 // no block descriptor are included - }; - - bool writable = true; - if (tud_msc_is_writable_cb) { - writable = tud_msc_is_writable_cb(lun); - } - mode_resp.write_protected = !writable; - - resplen = sizeof(mode_resp); - memcpy(buffer, &mode_resp, resplen); - } - break; - - case SCSI_CMD_REQUEST_SENSE: - { - scsi_sense_fixed_resp_t sense_rsp = - { - .response_code = 0x70, - .valid = 1 - }; - - sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8; - - sense_rsp.sense_key = _mscd_itf.sense_key; - sense_rsp.add_sense_code = _mscd_itf.add_sense_code; - sense_rsp.add_sense_qualifier = _mscd_itf.add_sense_qualifier; - - resplen = sizeof(sense_rsp); - memcpy(buffer, &sense_rsp, resplen); - - // Clear sense data after copy - tud_msc_set_sense(lun, 0, 0, 0); - } - break; - - default: resplen = -1; break; - } - - return resplen; -} - -bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) -{ - mscd_interface_t* p_msc = &_mscd_itf; - msc_cbw_t const * p_cbw = &p_msc->cbw; - msc_csw_t * p_csw = &p_msc->csw; - - switch (p_msc->stage) - { - case MSC_STAGE_CMD: - //------------- new CBW received -------------// - // Complete IN while waiting for CMD is usually Status of previous SCSI op, ignore it - if(ep_addr != p_msc->ep_out) return true; - - TU_ASSERT( event == XFER_RESULT_SUCCESS && - xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE ); - - TU_LOG2(" SCSI Command: %s\n", lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0])); - // TU_LOG2_MEM(p_cbw, xferred_bytes, 2); - - p_csw->signature = MSC_CSW_SIGNATURE; - p_csw->tag = p_cbw->tag; - p_csw->data_residue = 0; - - /*------------- Parse command and prepare DATA -------------*/ - p_msc->stage = MSC_STAGE_DATA; - p_msc->total_len = p_cbw->total_bytes; - p_msc->xferred_len = 0; - - if (SCSI_CMD_READ_10 == p_cbw->command[0]) - { - proc_read10_cmd(rhport, p_msc); - } - else if (SCSI_CMD_WRITE_10 == p_cbw->command[0]) - { - proc_write10_cmd(rhport, p_msc); - } - else - { - // For other SCSI commands - // 1. OUT : queue transfer (invoke app callback after done) - // 2. IN & Zero: Process if is built-in, else Invoke app callback. Skip DATA if zero length - if ( (p_cbw->total_bytes > 0 ) && !tu_bit_test(p_cbw->dir, 7) ) - { - // queue transfer - TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, p_msc->total_len) ); - }else - { - int32_t resplen; - - // First process if it is a built-in commands - resplen = proc_builtin_scsi(p_cbw->lun, p_cbw->command, _mscd_buf, sizeof(_mscd_buf)); - - // Not built-in, invoke user callback - if ( (resplen < 0) && (p_msc->sense_key == 0) ) - { - resplen = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, p_msc->total_len); - } - - if ( resplen < 0 ) - { - p_msc->total_len = 0; - p_csw->status = MSC_CSW_STATUS_FAILED; - p_msc->stage = MSC_STAGE_STATUS; - - // failed but senskey is not set: default to Illegal Request - if ( p_msc->sense_key == 0 ) tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); - - // Stall bulk In if needed - if (p_cbw->total_bytes) usbd_edpt_stall(rhport, p_msc->ep_in); - } - else - { - p_msc->total_len = (uint32_t) resplen; - p_csw->status = MSC_CSW_STATUS_PASSED; - - if (p_msc->total_len) - { - TU_ASSERT( p_cbw->total_bytes >= p_msc->total_len ); // cannot return more than host expect - TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, p_msc->total_len) ); - }else - { - p_msc->stage = MSC_STAGE_STATUS; - } - } - } - } - break; - - case MSC_STAGE_DATA: - TU_LOG2(" SCSI Data\n"); - //TU_LOG2_MEM(_mscd_buf, xferred_bytes, 2); - - // OUT transfer, invoke callback if needed - if ( !tu_bit_test(p_cbw->dir, 7) ) - { - if ( SCSI_CMD_WRITE_10 != p_cbw->command[0] ) - { - int32_t cb_result = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, p_msc->total_len); - - if ( cb_result < 0 ) - { - p_csw->status = MSC_CSW_STATUS_FAILED; - tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation - }else - { - p_csw->status = MSC_CSW_STATUS_PASSED; - } - } - else - { - uint16_t const block_sz = p_cbw->total_bytes / rdwr10_get_blockcount(p_cbw->command); - - // Adjust lba with transferred bytes - uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz); - - // Application can consume smaller bytes - int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, p_msc->xferred_len % block_sz, _mscd_buf, xferred_bytes); - - if ( nbytes < 0 ) - { - // negative means error -> skip to status phase, status in CSW set to failed - p_csw->data_residue = p_cbw->total_bytes - p_msc->xferred_len; - p_csw->status = MSC_CSW_STATUS_FAILED; - p_msc->stage = MSC_STAGE_STATUS; - - tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation - break; - }else - { - // Application consume less than what we got (including zero) - if ( nbytes < (int32_t) xferred_bytes ) - { - if ( nbytes > 0 ) - { - p_msc->xferred_len += nbytes; - memmove(_mscd_buf, _mscd_buf+nbytes, xferred_bytes-nbytes); - } - - // simulate an transfer complete with adjusted parameters --> this driver callback will fired again - dcd_event_xfer_complete(rhport, p_msc->ep_out, xferred_bytes-nbytes, XFER_RESULT_SUCCESS, false); - - return true; // skip the rest - } - else - { - // Application consume all bytes in our buffer. Nothing to do, process with normal flow - } - } - } - } - - // Accumulate data so far - p_msc->xferred_len += xferred_bytes; - - if ( p_msc->xferred_len >= p_msc->total_len ) - { - // Data Stage is complete - p_msc->stage = MSC_STAGE_STATUS; - } - else - { - // READ10 & WRITE10 Can be executed with large bulk of data e.g write 8K bytes (several flash write) - // We break it into multiple smaller command whose data size is up to CFG_TUD_MSC_BUFSIZE - if (SCSI_CMD_READ_10 == p_cbw->command[0]) - { - proc_read10_cmd(rhport, p_msc); - } - else if (SCSI_CMD_WRITE_10 == p_cbw->command[0]) - { - proc_write10_cmd(rhport, p_msc); - }else - { - // No other command take more than one transfer yet -> unlikely error - TU_BREAKPOINT(); - } - } - break; - - case MSC_STAGE_STATUS: - // processed immediately after this switch, supposedly to be empty - break; - - case MSC_STAGE_STATUS_SENT: - // Wait for the Status phase to complete - if( (ep_addr == p_msc->ep_in) && (xferred_bytes == sizeof(msc_csw_t)) ) - { - TU_LOG2(" SCSI Status: %u\n", p_csw->status); - // TU_LOG2_MEM(p_csw, xferred_bytes, 2); - - // Move to default CMD stage - p_msc->stage = MSC_STAGE_CMD; - - // Queue for the next CBW - TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) ); - } - break; - - default : break; - } - - if ( p_msc->stage == MSC_STAGE_STATUS ) - { - // Either endpoints is stalled, need to wait until it is cleared by host - if ( usbd_edpt_stalled(rhport, p_msc->ep_in) || usbd_edpt_stalled(rhport, p_msc->ep_out) ) - { - // simulate an transfer complete with adjusted parameters --> this driver callback will fired again - // and response with status phase after halted endpoints are cleared. - // note: use ep_out to prevent confusing with STATUS complete - dcd_event_xfer_complete(rhport, p_msc->ep_out, 0, XFER_RESULT_SUCCESS, false); - } - else - { - // Invoke complete callback if defined - switch(p_cbw->command[0]) - { - case SCSI_CMD_READ_10: - if ( tud_msc_read10_complete_cb ) tud_msc_read10_complete_cb(p_cbw->lun); - break; - - case SCSI_CMD_WRITE_10: - if ( tud_msc_write10_complete_cb ) tud_msc_write10_complete_cb(p_cbw->lun); - break; - - default: - if ( tud_msc_scsi_complete_cb ) tud_msc_scsi_complete_cb(p_cbw->lun, p_cbw->command); - break; - } - - // Move to Status Sent stage - p_msc->stage = MSC_STAGE_STATUS_SENT; - - // Send SCSI Status - TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_in , (uint8_t*) &p_msc->csw, sizeof(msc_csw_t))); - } - } - - return true; -} - -/*------------------------------------------------------------------*/ -/* SCSI Command Process - *------------------------------------------------------------------*/ -static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc) -{ - msc_cbw_t const * p_cbw = &p_msc->cbw; - msc_csw_t * p_csw = &p_msc->csw; - - uint16_t const block_cnt = rdwr10_get_blockcount(p_cbw->command); - TU_ASSERT(block_cnt, ); // prevent div by zero - - uint16_t const block_sz = p_cbw->total_bytes / block_cnt; - TU_ASSERT(block_sz, ); // prevent div by zero - - // Adjust lba with transferred bytes - uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz); - - // remaining bytes capped at class buffer - int32_t nbytes = (int32_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len); - - // Application can consume smaller bytes - nbytes = tud_msc_read10_cb(p_cbw->lun, lba, p_msc->xferred_len % block_sz, _mscd_buf, (uint32_t) nbytes); - - if ( nbytes < 0 ) - { - // negative means error -> pipe is stalled & status in CSW set to failed - p_csw->data_residue = p_cbw->total_bytes - p_msc->xferred_len; - p_csw->status = MSC_CSW_STATUS_FAILED; - - tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation - usbd_edpt_stall(rhport, p_msc->ep_in); - } - else if ( nbytes == 0 ) - { - // zero means not ready -> simulate an transfer complete so that this driver callback will fired again - dcd_event_xfer_complete(rhport, p_msc->ep_in, 0, XFER_RESULT_SUCCESS, false); - } - else - { - TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, nbytes), ); - } -} - -static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc) -{ - msc_cbw_t const * p_cbw = &p_msc->cbw; - bool writable = true; - if (tud_msc_is_writable_cb) { - writable = tud_msc_is_writable_cb(p_cbw->lun); - } - if (!writable) { - msc_csw_t* p_csw = &p_msc->csw; - p_csw->data_residue = p_cbw->total_bytes; - p_csw->status = MSC_CSW_STATUS_FAILED; - - tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_DATA_PROTECT, 0x27, 0x00); // Sense = Write protected - usbd_edpt_stall(rhport, p_msc->ep_out); - return; - } - - // remaining bytes capped at class buffer - int32_t nbytes = (int32_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len); - - // Write10 callback will be called later when usb transfer complete - TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, nbytes), ); -} - -#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 && CFG_TUD_MSC) + +#include "common/tusb_common.h" +#include "msc_device.h" +#include "device/usbd_pvt.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +enum +{ + MSC_STAGE_CMD = 0, + MSC_STAGE_DATA, + MSC_STAGE_STATUS, + MSC_STAGE_STATUS_SENT +}; + +typedef struct +{ + // TODO optimize alignment + CFG_TUSB_MEM_ALIGN msc_cbw_t cbw; + CFG_TUSB_MEM_ALIGN msc_csw_t csw; + + uint8_t itf_num; + uint8_t ep_in; + uint8_t ep_out; + + // Bulk Only Transfer (BOT) Protocol + uint8_t stage; + uint32_t total_len; + uint32_t xferred_len; // numbered of bytes transferred so far in the Data Stage + + // Sense Response Data + uint8_t sense_key; + uint8_t add_sense_code; + uint8_t add_sense_qualifier; +}mscd_interface_t; + +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static mscd_interface_t _mscd_itf; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _mscd_buf[CFG_TUD_MSC_BUFSIZE]; + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ +static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc); +static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc); + +static inline uint32_t rdwr10_get_lba(uint8_t const command[]) +{ + // read10 & write10 has the same format + scsi_write10_t* p_rdwr10 = (scsi_write10_t*) command; + + // copy first to prevent mis-aligned access + uint32_t lba; + memcpy(&lba, &p_rdwr10->lba, 4); + + // lba is in Big Endian format + return tu_ntohl(lba); +} + +static inline uint16_t rdwr10_get_blockcount(uint8_t const command[]) +{ + // read10 & write10 has the same format + scsi_write10_t* p_rdwr10 = (scsi_write10_t*) command; + + // copy first to prevent mis-aligned access + uint16_t block_count; + memcpy(&block_count, &p_rdwr10->block_count, 2); + + return tu_ntohs(block_count); +} + +//--------------------------------------------------------------------+ +// Debug +//--------------------------------------------------------------------+ +#if CFG_TUSB_DEBUG >= 2 + +static lookup_entry_t const _msc_scsi_cmd_lookup[] = +{ + { .key = SCSI_CMD_TEST_UNIT_READY , .data = "Test Unit Ready" }, + { .key = SCSI_CMD_INQUIRY , .data = "Inquiry" }, + { .key = SCSI_CMD_MODE_SELECT_6 , .data = "Mode_Select 6" }, + { .key = SCSI_CMD_MODE_SENSE_6 , .data = "Mode_Sense 6" }, + { .key = SCSI_CMD_START_STOP_UNIT , .data = "Start Stop Unit" }, + { .key = SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL , .data = "Prevent Allow Medium Removal" }, + { .key = SCSI_CMD_READ_CAPACITY_10 , .data = "Read Capacity10" }, + { .key = SCSI_CMD_REQUEST_SENSE , .data = "Request Sense" }, + { .key = SCSI_CMD_READ_FORMAT_CAPACITY , .data = "Read Format Capacity" }, + { .key = SCSI_CMD_READ_10 , .data = "Read10" }, + { .key = SCSI_CMD_WRITE_10 , .data = "Write10" } +}; + +static lookup_table_t const _msc_scsi_cmd_table = +{ + .count = TU_ARRAY_SIZE(_msc_scsi_cmd_lookup), + .items = _msc_scsi_cmd_lookup +}; + +#endif + +//--------------------------------------------------------------------+ +// APPLICATION API +//--------------------------------------------------------------------+ +bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier) +{ + (void) lun; + + _mscd_itf.sense_key = sense_key; + _mscd_itf.add_sense_code = add_sense_code; + _mscd_itf.add_sense_qualifier = add_sense_qualifier; + + return true; +} + +//--------------------------------------------------------------------+ +// USBD Driver API +//--------------------------------------------------------------------+ +void mscd_init(void) +{ + tu_memclr(&_mscd_itf, sizeof(mscd_interface_t)); +} + +void mscd_reset(uint8_t rhport) +{ + (void) rhport; + tu_memclr(&_mscd_itf, sizeof(mscd_interface_t)); +} + +bool mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_len) +{ + // only support SCSI's BOT protocol + TU_ASSERT(MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass && + MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol); + + mscd_interface_t * p_msc = &_mscd_itf; + + // Open endpoint pair + TU_ASSERT( usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in) ); + + p_msc->itf_num = itf_desc->bInterfaceNumber; + (*p_len) = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t); + + // Prepare for Command Block Wrapper + TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) ); + + return true; +} + +// Handle class control request +// return false to stall control endpoint (e.g unsupported request) +bool mscd_control_request(uint8_t rhport, tusb_control_request_t const * p_request) +{ + // Handle class request only + TU_VERIFY(p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); + + switch ( p_request->bRequest ) + { + case MSC_REQ_RESET: + // TODO: Actually reset interface. + tud_control_status(rhport, p_request); + break; + + case MSC_REQ_GET_MAX_LUN: + { + uint8_t maxlun = 1; + if (tud_msc_get_maxlun_cb) maxlun = tud_msc_get_maxlun_cb(); + TU_VERIFY(maxlun); + + // MAX LUN is minus 1 by specs + maxlun--; + + tud_control_xfer(rhport, p_request, &maxlun, 1); + } + break; + + default: return false; // stall unsupported request + } + + return true; +} + +// Invoked when class request DATA stage is finished. +// return false to stall control endpoint (e.g Host send non-sense DATA) +bool mscd_control_complete(uint8_t rhport, tusb_control_request_t const * request) +{ + (void) rhport; + (void) request; + + // nothing to do + return true; +} + +// return response's length (copied to buffer). Negative if it is not an built-in command or indicate Failed status (CSW) +// In case of a failed status, sense key must be set for reason of failure +int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize) +{ + (void) bufsize; // TODO refractor later + int32_t resplen; + + switch ( scsi_cmd[0] ) + { + case SCSI_CMD_TEST_UNIT_READY: + resplen = 0; + if ( !tud_msc_test_unit_ready_cb(lun) ) + { + // Failed status response + resplen = - 1; + + // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable + if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00); + } + break; + + case SCSI_CMD_START_STOP_UNIT: + resplen = 0; + + if (tud_msc_start_stop_cb) + { + scsi_start_stop_unit_t const * start_stop = (scsi_start_stop_unit_t const *) scsi_cmd; + if ( !tud_msc_start_stop_cb(lun, start_stop->power_condition, start_stop->start, start_stop->load_eject) ) + { + // Failed status response + resplen = - 1; + + // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable + if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00); + } + } + break; + + case SCSI_CMD_READ_CAPACITY_10: + { + uint32_t block_count; + uint32_t block_size; + uint16_t block_size_u16; + + tud_msc_capacity_cb(lun, &block_count, &block_size_u16); + block_size = (uint32_t) block_size_u16; + + // Invalid block size/count from callback, possibly unit is not ready + // stall this request, set sense key to NOT READY + if (block_count == 0 || block_size == 0) + { + resplen = -1; + + // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable + if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00); + }else + { + scsi_read_capacity10_resp_t read_capa10; + + read_capa10.last_lba = tu_htonl(block_count-1); + read_capa10.block_size = tu_htonl(block_size); + + resplen = sizeof(read_capa10); + memcpy(buffer, &read_capa10, resplen); + } + } + break; + + case SCSI_CMD_READ_FORMAT_CAPACITY: + { + scsi_read_format_capacity_data_t read_fmt_capa = + { + .list_length = 8, + .block_num = 0, + .descriptor_type = 2, // formatted media + .block_size_u16 = 0 + }; + + uint32_t block_count; + uint16_t block_size; + + tud_msc_capacity_cb(lun, &block_count, &block_size); + + // Invalid block size/count from callback, possibly unit is not ready + // stall this request, set sense key to NOT READY + if (block_count == 0 || block_size == 0) + { + resplen = -1; + + // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable + if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00); + }else + { + read_fmt_capa.block_num = tu_htonl(block_count); + read_fmt_capa.block_size_u16 = tu_htons(block_size); + + resplen = sizeof(read_fmt_capa); + memcpy(buffer, &read_fmt_capa, resplen); + } + } + break; + + case SCSI_CMD_INQUIRY: + { + scsi_inquiry_resp_t inquiry_rsp = + { + .is_removable = 1, + .version = 2, + .response_data_format = 2, + }; + + // vendor_id, product_id, product_rev is space padded string + memset(inquiry_rsp.vendor_id , ' ', sizeof(inquiry_rsp.vendor_id)); + memset(inquiry_rsp.product_id , ' ', sizeof(inquiry_rsp.product_id)); + memset(inquiry_rsp.product_rev, ' ', sizeof(inquiry_rsp.product_rev)); + + tud_msc_inquiry_cb(lun, inquiry_rsp.vendor_id, inquiry_rsp.product_id, inquiry_rsp.product_rev); + + resplen = sizeof(inquiry_rsp); + memcpy(buffer, &inquiry_rsp, resplen); + } + break; + + case SCSI_CMD_MODE_SENSE_6: + { + scsi_mode_sense6_resp_t mode_resp = + { + .data_len = 3, + .medium_type = 0, + .write_protected = false, + .reserved = 0, + .block_descriptor_len = 0 // no block descriptor are included + }; + + bool writable = true; + if (tud_msc_is_writable_cb) { + writable = tud_msc_is_writable_cb(lun); + } + mode_resp.write_protected = !writable; + + resplen = sizeof(mode_resp); + memcpy(buffer, &mode_resp, resplen); + } + break; + + case SCSI_CMD_REQUEST_SENSE: + { + scsi_sense_fixed_resp_t sense_rsp = + { + .response_code = 0x70, + .valid = 1 + }; + + sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8; + + sense_rsp.sense_key = _mscd_itf.sense_key; + sense_rsp.add_sense_code = _mscd_itf.add_sense_code; + sense_rsp.add_sense_qualifier = _mscd_itf.add_sense_qualifier; + + resplen = sizeof(sense_rsp); + memcpy(buffer, &sense_rsp, resplen); + + // Clear sense data after copy + tud_msc_set_sense(lun, 0, 0, 0); + } + break; + + default: resplen = -1; break; + } + + return resplen; +} + +bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) +{ + mscd_interface_t* p_msc = &_mscd_itf; + msc_cbw_t const * p_cbw = &p_msc->cbw; + msc_csw_t * p_csw = &p_msc->csw; + + switch (p_msc->stage) + { + case MSC_STAGE_CMD: + //------------- new CBW received -------------// + // Complete IN while waiting for CMD is usually Status of previous SCSI op, ignore it + if(ep_addr != p_msc->ep_out) return true; + + TU_ASSERT( event == XFER_RESULT_SUCCESS && + xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE ); + + TU_LOG2(" SCSI Command: %s\n", lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0])); + // TU_LOG2_MEM(p_cbw, xferred_bytes, 2); + + p_csw->signature = MSC_CSW_SIGNATURE; + p_csw->tag = p_cbw->tag; + p_csw->data_residue = 0; + + /*------------- Parse command and prepare DATA -------------*/ + p_msc->stage = MSC_STAGE_DATA; + p_msc->total_len = p_cbw->total_bytes; + p_msc->xferred_len = 0; + + if (SCSI_CMD_READ_10 == p_cbw->command[0]) + { + proc_read10_cmd(rhport, p_msc); + } + else if (SCSI_CMD_WRITE_10 == p_cbw->command[0]) + { + proc_write10_cmd(rhport, p_msc); + } + else + { + // For other SCSI commands + // 1. OUT : queue transfer (invoke app callback after done) + // 2. IN & Zero: Process if is built-in, else Invoke app callback. Skip DATA if zero length + if ( (p_cbw->total_bytes > 0 ) && !tu_bit_test(p_cbw->dir, 7) ) + { + // queue transfer + TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, p_msc->total_len) ); + }else + { + int32_t resplen; + + // First process if it is a built-in commands + resplen = proc_builtin_scsi(p_cbw->lun, p_cbw->command, _mscd_buf, sizeof(_mscd_buf)); + + // Not built-in, invoke user callback + if ( (resplen < 0) && (p_msc->sense_key == 0) ) + { + resplen = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, p_msc->total_len); + } + + if ( resplen < 0 ) + { + p_msc->total_len = 0; + p_csw->status = MSC_CSW_STATUS_FAILED; + p_msc->stage = MSC_STAGE_STATUS; + + // failed but senskey is not set: default to Illegal Request + if ( p_msc->sense_key == 0 ) tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); + + // Stall bulk In if needed + if (p_cbw->total_bytes) usbd_edpt_stall(rhport, p_msc->ep_in); + } + else + { + p_msc->total_len = (uint32_t) resplen; + p_csw->status = MSC_CSW_STATUS_PASSED; + + if (p_msc->total_len) + { + TU_ASSERT( p_cbw->total_bytes >= p_msc->total_len ); // cannot return more than host expect + TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, p_msc->total_len) ); + }else + { + p_msc->stage = MSC_STAGE_STATUS; + } + } + } + } + break; + + case MSC_STAGE_DATA: + TU_LOG2(" SCSI Data\n"); + //TU_LOG2_MEM(_mscd_buf, xferred_bytes, 2); + + // OUT transfer, invoke callback if needed + if ( !tu_bit_test(p_cbw->dir, 7) ) + { + if ( SCSI_CMD_WRITE_10 != p_cbw->command[0] ) + { + int32_t cb_result = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, p_msc->total_len); + + if ( cb_result < 0 ) + { + p_csw->status = MSC_CSW_STATUS_FAILED; + tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation + }else + { + p_csw->status = MSC_CSW_STATUS_PASSED; + } + } + else + { + uint16_t const block_sz = p_cbw->total_bytes / rdwr10_get_blockcount(p_cbw->command); + + // Adjust lba with transferred bytes + uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz); + + // Application can consume smaller bytes + int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, p_msc->xferred_len % block_sz, _mscd_buf, xferred_bytes); + + if ( nbytes < 0 ) + { + // negative means error -> skip to status phase, status in CSW set to failed + p_csw->data_residue = p_cbw->total_bytes - p_msc->xferred_len; + p_csw->status = MSC_CSW_STATUS_FAILED; + p_msc->stage = MSC_STAGE_STATUS; + + tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation + break; + }else + { + // Application consume less than what we got (including zero) + if ( nbytes < (int32_t) xferred_bytes ) + { + if ( nbytes > 0 ) + { + p_msc->xferred_len += nbytes; + memmove(_mscd_buf, _mscd_buf+nbytes, xferred_bytes-nbytes); + } + + // simulate an transfer complete with adjusted parameters --> this driver callback will fired again + dcd_event_xfer_complete(rhport, p_msc->ep_out, xferred_bytes-nbytes, XFER_RESULT_SUCCESS, false); + + return true; // skip the rest + } + else + { + // Application consume all bytes in our buffer. Nothing to do, process with normal flow + } + } + } + } + + // Accumulate data so far + p_msc->xferred_len += xferred_bytes; + + if ( p_msc->xferred_len >= p_msc->total_len ) + { + // Data Stage is complete + p_msc->stage = MSC_STAGE_STATUS; + } + else + { + // READ10 & WRITE10 Can be executed with large bulk of data e.g write 8K bytes (several flash write) + // We break it into multiple smaller command whose data size is up to CFG_TUD_MSC_BUFSIZE + if (SCSI_CMD_READ_10 == p_cbw->command[0]) + { + proc_read10_cmd(rhport, p_msc); + } + else if (SCSI_CMD_WRITE_10 == p_cbw->command[0]) + { + proc_write10_cmd(rhport, p_msc); + }else + { + // No other command take more than one transfer yet -> unlikely error + TU_BREAKPOINT(); + } + } + break; + + case MSC_STAGE_STATUS: + // processed immediately after this switch, supposedly to be empty + break; + + case MSC_STAGE_STATUS_SENT: + // Wait for the Status phase to complete + if( (ep_addr == p_msc->ep_in) && (xferred_bytes == sizeof(msc_csw_t)) ) + { + TU_LOG2(" SCSI Status: %u\n", p_csw->status); + // TU_LOG2_MEM(p_csw, xferred_bytes, 2); + + // Move to default CMD stage + p_msc->stage = MSC_STAGE_CMD; + + // Queue for the next CBW + TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) ); + } + break; + + default : break; + } + + if ( p_msc->stage == MSC_STAGE_STATUS ) + { + // Either endpoints is stalled, need to wait until it is cleared by host + if ( usbd_edpt_stalled(rhport, p_msc->ep_in) || usbd_edpt_stalled(rhport, p_msc->ep_out) ) + { + // simulate an transfer complete with adjusted parameters --> this driver callback will fired again + // and response with status phase after halted endpoints are cleared. + // note: use ep_out to prevent confusing with STATUS complete + dcd_event_xfer_complete(rhport, p_msc->ep_out, 0, XFER_RESULT_SUCCESS, false); + } + else + { + // Invoke complete callback if defined + switch(p_cbw->command[0]) + { + case SCSI_CMD_READ_10: + if ( tud_msc_read10_complete_cb ) tud_msc_read10_complete_cb(p_cbw->lun); + break; + + case SCSI_CMD_WRITE_10: + if ( tud_msc_write10_complete_cb ) tud_msc_write10_complete_cb(p_cbw->lun); + break; + + default: + if ( tud_msc_scsi_complete_cb ) tud_msc_scsi_complete_cb(p_cbw->lun, p_cbw->command); + break; + } + + // Move to Status Sent stage + p_msc->stage = MSC_STAGE_STATUS_SENT; + + // Send SCSI Status + TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_in , (uint8_t*) &p_msc->csw, sizeof(msc_csw_t))); + } + } + + return true; +} + +/*------------------------------------------------------------------*/ +/* SCSI Command Process + *------------------------------------------------------------------*/ +static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc) +{ + msc_cbw_t const * p_cbw = &p_msc->cbw; + msc_csw_t * p_csw = &p_msc->csw; + + uint16_t const block_cnt = rdwr10_get_blockcount(p_cbw->command); + TU_ASSERT(block_cnt, ); // prevent div by zero + + uint16_t const block_sz = p_cbw->total_bytes / block_cnt; + TU_ASSERT(block_sz, ); // prevent div by zero + + // Adjust lba with transferred bytes + uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz); + + // remaining bytes capped at class buffer + int32_t nbytes = (int32_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len); + + // Application can consume smaller bytes + nbytes = tud_msc_read10_cb(p_cbw->lun, lba, p_msc->xferred_len % block_sz, _mscd_buf, (uint32_t) nbytes); + + if ( nbytes < 0 ) + { + // negative means error -> pipe is stalled & status in CSW set to failed + p_csw->data_residue = p_cbw->total_bytes - p_msc->xferred_len; + p_csw->status = MSC_CSW_STATUS_FAILED; + + tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation + usbd_edpt_stall(rhport, p_msc->ep_in); + } + else if ( nbytes == 0 ) + { + // zero means not ready -> simulate an transfer complete so that this driver callback will fired again + dcd_event_xfer_complete(rhport, p_msc->ep_in, 0, XFER_RESULT_SUCCESS, false); + } + else + { + TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, nbytes), ); + } +} + +static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc) +{ + msc_cbw_t const * p_cbw = &p_msc->cbw; + bool writable = true; + if (tud_msc_is_writable_cb) { + writable = tud_msc_is_writable_cb(p_cbw->lun); + } + if (!writable) { + msc_csw_t* p_csw = &p_msc->csw; + p_csw->data_residue = p_cbw->total_bytes; + p_csw->status = MSC_CSW_STATUS_FAILED; + + tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_DATA_PROTECT, 0x27, 0x00); // Sense = Write protected + usbd_edpt_stall(rhport, p_msc->ep_out); + return; + } + + // remaining bytes capped at class buffer + int32_t nbytes = (int32_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len); + + // Write10 callback will be called later when usb transfer complete + TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, nbytes), ); +} + +#endif diff --git a/src/class/msc/msc_device.h b/src/class/msc/msc_device.h index 8da9cfaa3..66180777e 100644 --- a/src/class/msc/msc_device.h +++ b/src/class/msc/msc_device.h @@ -1,165 +1,165 @@ -/* - * 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. - */ - -#ifndef _TUSB_MSC_DEVICE_H_ -#define _TUSB_MSC_DEVICE_H_ - -#include "common/tusb_common.h" -#include "device/usbd.h" -#include "msc.h" - -#ifdef __cplusplus - extern "C" { -#endif - -//--------------------------------------------------------------------+ -// Class Driver Configuration -//--------------------------------------------------------------------+ -TU_VERIFY_STATIC(CFG_TUD_MSC_BUFSIZE < UINT16_MAX, "Size is not correct"); - -#ifndef CFG_TUD_MSC_BUFSIZE - #error CFG_TUD_MSC_BUFSIZE must be defined, value of a block size should work well, the more the better -#endif - -/** \addtogroup ClassDriver_MSC - * @{ - * \defgroup MSC_Device Device - * @{ */ - -bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier); - -//--------------------------------------------------------------------+ -// Application Callbacks (WEAK is optional) -//--------------------------------------------------------------------+ - -/** - * Invoked when received \ref SCSI_CMD_READ_10 command - * \param[in] lun Logical unit number - * \param[in] lba Logical Block Address to be read - * \param[in] offset Byte offset from LBA - * \param[out] buffer Buffer which application need to update with the response data. - * \param[in] bufsize Requested bytes - * - * \return Number of byte read, if it is less than requested bytes by \a \b bufsize. Tinyusb will transfer - * this amount first and invoked this again for remaining data. - * - * \retval zero Indicate application is not ready yet to response e.g disk I/O is not complete. - * tinyusb will invoke this callback with the same parameters again some time later. - * - * \retval negative Indicate error e.g reading disk I/O. tinyusb will \b STALL the corresponding - * endpoint and return failed status in command status wrapper phase. - */ -int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize); - -/** - * Invoked when received \ref SCSI_CMD_WRITE_10 command - * \param[in] lun Logical unit number - * \param[in] lba Logical Block Address to be write - * \param[in] offset Byte offset from LBA - * \param[out] buffer Buffer which holds written data. - * \param[in] bufsize Requested bytes - * - * \return Number of byte written, if it is less than requested bytes by \a \b bufsize. Tinyusb will proceed with - * other work and invoked this again with adjusted parameters. - * - * \retval zero Indicate application is not ready yet e.g disk I/O is not complete. - * Tinyusb will invoke this callback with the same parameters again some time later. - * - * \retval negative Indicate error writing disk I/O. Tinyusb will \b STALL the corresponding - * endpoint and return failed status in command status wrapper phase. - */ -int32_t tud_msc_write10_cb (uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize); - -// Invoked when received SCSI_CMD_INQUIRY -// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively -void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]); - -// Invoked when received Test Unit Ready command. -// return true allowing host to read/write this LUN e.g SD card inserted -bool tud_msc_test_unit_ready_cb(uint8_t lun); - -// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size -// Application update block count and block size -void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size); - -/** - * Invoked when received an SCSI command not in built-in list below. - * - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, TEST_UNIT_READY, START_STOP_UNIT, MODE_SENSE6, REQUEST_SENSE - * - READ10 and WRITE10 has their own callbacks - * - * \param[in] lun Logical unit number - * \param[in] scsi_cmd SCSI command contents which application must examine to response accordingly - * \param[out] buffer Buffer for SCSI Data Stage. - * - For INPUT: application must fill this with response. - * - For OUTPUT it holds the Data from host - * \param[in] bufsize Buffer's length. - * - * \return Actual bytes processed, can be zero for no-data command. - * \retval negative Indicate error e.g unsupported command, tinyusb will \b STALL the corresponding - * endpoint and return failed status in command status wrapper phase. - */ -int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize); - -/*------------- Optional callbacks -------------*/ - -// Invoked when received GET_MAX_LUN request, required for multiple LUNs implementation -TU_ATTR_WEAK uint8_t tud_msc_get_maxlun_cb(void); - -// Invoked when received Start Stop Unit command -// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage -// - Start = 1 : active mode, if load_eject = 1 : load disk storage -TU_ATTR_WEAK bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject); - -// Invoked when Read10 command is complete -TU_ATTR_WEAK void tud_msc_read10_complete_cb(uint8_t lun); - -// Invoke when Write10 command is complete, can be used to flush flash caching -TU_ATTR_WEAK void tud_msc_write10_complete_cb(uint8_t lun); - -// Invoked when command in tud_msc_scsi_cb is complete -TU_ATTR_WEAK void tud_msc_scsi_complete_cb(uint8_t lun, uint8_t const scsi_cmd[16]); - -// Hook to make a mass storage device read-only. TODO remove -TU_ATTR_WEAK bool tud_msc_is_writable_cb(uint8_t lun); - -/** @} */ -/** @} */ - -//--------------------------------------------------------------------+ -// Internal Class Driver API -//--------------------------------------------------------------------+ -void mscd_init (void); -void mscd_reset (uint8_t rhport); -bool mscd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length); -bool mscd_control_request (uint8_t rhport, tusb_control_request_t const * p_request); -bool mscd_control_complete (uint8_t rhport, tusb_control_request_t const * p_request); -bool mscd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_MSC_DEVICE_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. + */ + +#ifndef _TUSB_MSC_DEVICE_H_ +#define _TUSB_MSC_DEVICE_H_ + +#include "common/tusb_common.h" +#include "device/usbd.h" +#include "msc.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Class Driver Configuration +//--------------------------------------------------------------------+ +TU_VERIFY_STATIC(CFG_TUD_MSC_BUFSIZE < UINT16_MAX, "Size is not correct"); + +#ifndef CFG_TUD_MSC_BUFSIZE + #error CFG_TUD_MSC_BUFSIZE must be defined, value of a block size should work well, the more the better +#endif + +/** \addtogroup ClassDriver_MSC + * @{ + * \defgroup MSC_Device Device + * @{ */ + +bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier); + +//--------------------------------------------------------------------+ +// Application Callbacks (WEAK is optional) +//--------------------------------------------------------------------+ + +/** + * Invoked when received \ref SCSI_CMD_READ_10 command + * \param[in] lun Logical unit number + * \param[in] lba Logical Block Address to be read + * \param[in] offset Byte offset from LBA + * \param[out] buffer Buffer which application need to update with the response data. + * \param[in] bufsize Requested bytes + * + * \return Number of byte read, if it is less than requested bytes by \a \b bufsize. Tinyusb will transfer + * this amount first and invoked this again for remaining data. + * + * \retval zero Indicate application is not ready yet to response e.g disk I/O is not complete. + * tinyusb will invoke this callback with the same parameters again some time later. + * + * \retval negative Indicate error e.g reading disk I/O. tinyusb will \b STALL the corresponding + * endpoint and return failed status in command status wrapper phase. + */ +int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize); + +/** + * Invoked when received \ref SCSI_CMD_WRITE_10 command + * \param[in] lun Logical unit number + * \param[in] lba Logical Block Address to be write + * \param[in] offset Byte offset from LBA + * \param[out] buffer Buffer which holds written data. + * \param[in] bufsize Requested bytes + * + * \return Number of byte written, if it is less than requested bytes by \a \b bufsize. Tinyusb will proceed with + * other work and invoked this again with adjusted parameters. + * + * \retval zero Indicate application is not ready yet e.g disk I/O is not complete. + * Tinyusb will invoke this callback with the same parameters again some time later. + * + * \retval negative Indicate error writing disk I/O. Tinyusb will \b STALL the corresponding + * endpoint and return failed status in command status wrapper phase. + */ +int32_t tud_msc_write10_cb (uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize); + +// Invoked when received SCSI_CMD_INQUIRY +// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively +void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]); + +// Invoked when received Test Unit Ready command. +// return true allowing host to read/write this LUN e.g SD card inserted +bool tud_msc_test_unit_ready_cb(uint8_t lun); + +// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size +// Application update block count and block size +void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size); + +/** + * Invoked when received an SCSI command not in built-in list below. + * - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, TEST_UNIT_READY, START_STOP_UNIT, MODE_SENSE6, REQUEST_SENSE + * - READ10 and WRITE10 has their own callbacks + * + * \param[in] lun Logical unit number + * \param[in] scsi_cmd SCSI command contents which application must examine to response accordingly + * \param[out] buffer Buffer for SCSI Data Stage. + * - For INPUT: application must fill this with response. + * - For OUTPUT it holds the Data from host + * \param[in] bufsize Buffer's length. + * + * \return Actual bytes processed, can be zero for no-data command. + * \retval negative Indicate error e.g unsupported command, tinyusb will \b STALL the corresponding + * endpoint and return failed status in command status wrapper phase. + */ +int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize); + +/*------------- Optional callbacks -------------*/ + +// Invoked when received GET_MAX_LUN request, required for multiple LUNs implementation +TU_ATTR_WEAK uint8_t tud_msc_get_maxlun_cb(void); + +// Invoked when received Start Stop Unit command +// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage +// - Start = 1 : active mode, if load_eject = 1 : load disk storage +TU_ATTR_WEAK bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject); + +// Invoked when Read10 command is complete +TU_ATTR_WEAK void tud_msc_read10_complete_cb(uint8_t lun); + +// Invoke when Write10 command is complete, can be used to flush flash caching +TU_ATTR_WEAK void tud_msc_write10_complete_cb(uint8_t lun); + +// Invoked when command in tud_msc_scsi_cb is complete +TU_ATTR_WEAK void tud_msc_scsi_complete_cb(uint8_t lun, uint8_t const scsi_cmd[16]); + +// Hook to make a mass storage device read-only. TODO remove +TU_ATTR_WEAK bool tud_msc_is_writable_cb(uint8_t lun); + +/** @} */ +/** @} */ + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +void mscd_init (void); +void mscd_reset (uint8_t rhport); +bool mscd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length); +bool mscd_control_request (uint8_t rhport, tusb_control_request_t const * p_request); +bool mscd_control_complete (uint8_t rhport, tusb_control_request_t const * p_request); +bool mscd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_MSC_DEVICE_H_ */ diff --git a/src/class/msc/msc_host.c b/src/class/msc/msc_host.c index 0507c5f6d..e20969f57 100644 --- a/src/class/msc/msc_host.c +++ b/src/class/msc/msc_host.c @@ -1,413 +1,413 @@ -/* - * 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_HOST_ENABLED & CFG_TUH_MSC - -//--------------------------------------------------------------------+ -// INCLUDE -//--------------------------------------------------------------------+ -#include "common/tusb_common.h" -#include "msc_host.h" - -//--------------------------------------------------------------------+ -// MACRO CONSTANT TYPEDEF -//--------------------------------------------------------------------+ -CFG_TUSB_MEM_SECTION static msch_interface_t msch_data[CFG_TUSB_HOST_DEVICE_MAX]; - -//------------- Initalization Data -------------// -static osal_semaphore_def_t msch_sem_def; -static osal_semaphore_t msch_sem_hdl; - -// buffer used to read scsi information when mounted, largest response data currently is inquiry -CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t msch_buffer[sizeof(scsi_inquiry_resp_t)]; - -//--------------------------------------------------------------------+ -// INTERNAL OBJECT & FUNCTION DECLARATION -//--------------------------------------------------------------------+ - -//--------------------------------------------------------------------+ -// PUBLIC API -//--------------------------------------------------------------------+ -bool tuh_msc_is_mounted(uint8_t dev_addr) -{ - return tuh_device_is_configured(dev_addr) && // is configured can be omitted - msch_data[dev_addr-1].is_initialized; -} - -bool tuh_msc_is_busy(uint8_t dev_addr) -{ - return msch_data[dev_addr-1].is_initialized && - hcd_edpt_busy(dev_addr, msch_data[dev_addr-1].ep_in); -} - -uint8_t const* tuh_msc_get_vendor_name(uint8_t dev_addr) -{ - return msch_data[dev_addr-1].is_initialized ? msch_data[dev_addr-1].vendor_id : NULL; -} - -uint8_t const* tuh_msc_get_product_name(uint8_t dev_addr) -{ - return msch_data[dev_addr-1].is_initialized ? msch_data[dev_addr-1].product_id : NULL; -} - -tusb_error_t tuh_msc_get_capacity(uint8_t dev_addr, uint32_t* p_last_lba, uint32_t* p_block_size) -{ - if ( !msch_data[dev_addr-1].is_initialized ) return TUSB_ERROR_MSCH_DEVICE_NOT_MOUNTED; - TU_ASSERT(p_last_lba != NULL && p_block_size != NULL, TUSB_ERROR_INVALID_PARA); - - (*p_last_lba) = msch_data[dev_addr-1].last_lba; - (*p_block_size) = (uint32_t) msch_data[dev_addr-1].block_size; - - return TUSB_ERROR_NONE; -} - -//--------------------------------------------------------------------+ -// PUBLIC API: SCSI COMMAND -//--------------------------------------------------------------------+ -static inline void msc_cbw_add_signature(msc_cbw_t *p_cbw, uint8_t lun) -{ - p_cbw->signature = MSC_CBW_SIGNATURE; - p_cbw->tag = 0xCAFECAFE; - p_cbw->lun = lun; -} - -static tusb_error_t msch_command_xfer(uint8_t dev_addr, msch_interface_t * p_msch, void* p_buffer) -{ - if ( NULL != p_buffer) - { // there is data phase - if (p_msch->cbw.dir & TUSB_DIR_IN_MASK) - { - TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t), false), TUSB_ERROR_FAILED ); - TU_ASSERT( hcd_pipe_queue_xfer(dev_addr, p_msch->ep_in , p_buffer, p_msch->cbw.total_bytes), TUSB_ERROR_FAILED ); - }else - { - TU_ASSERT( hcd_pipe_queue_xfer(dev_addr, p_msch->ep_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t)), TUSB_ERROR_FAILED ); - TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_out , p_buffer, p_msch->cbw.total_bytes, false), TUSB_ERROR_FAILED ); - } - } - - TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_in , (uint8_t*) &p_msch->csw, sizeof(msc_csw_t), true), TUSB_ERROR_FAILED); - - return TUSB_ERROR_NONE; -} - -tusb_error_t tusbh_msc_inquiry(uint8_t dev_addr, uint8_t lun, uint8_t *p_data) -{ - msch_interface_t* p_msch = &msch_data[dev_addr-1]; - - //------------- Command Block Wrapper -------------// - msc_cbw_add_signature(&p_msch->cbw, lun); - p_msch->cbw.total_bytes = sizeof(scsi_inquiry_resp_t); - p_msch->cbw.dir = TUSB_DIR_IN_MASK; - p_msch->cbw.cmd_len = sizeof(scsi_inquiry_t); - - //------------- SCSI command -------------// - scsi_inquiry_t cmd_inquiry = - { - .cmd_code = SCSI_CMD_INQUIRY, - .alloc_length = sizeof(scsi_inquiry_resp_t) - }; - - memcpy(p_msch->cbw.command, &cmd_inquiry, p_msch->cbw.cmd_len); - - TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_data) ); - - return TUSB_ERROR_NONE; -} - -tusb_error_t tusbh_msc_read_capacity10(uint8_t dev_addr, uint8_t lun, uint8_t *p_data) -{ - msch_interface_t* p_msch = &msch_data[dev_addr-1]; - - //------------- Command Block Wrapper -------------// - msc_cbw_add_signature(&p_msch->cbw, lun); - p_msch->cbw.total_bytes = sizeof(scsi_read_capacity10_resp_t); - p_msch->cbw.dir = TUSB_DIR_IN_MASK; - p_msch->cbw.cmd_len = sizeof(scsi_read_capacity10_t); - - //------------- SCSI command -------------// - scsi_read_capacity10_t cmd_read_capacity10 = - { - .cmd_code = SCSI_CMD_READ_CAPACITY_10, - .lba = 0, - .partial_medium_indicator = 0 - }; - - memcpy(p_msch->cbw.command, &cmd_read_capacity10, p_msch->cbw.cmd_len); - - TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_data) ); - - return TUSB_ERROR_NONE; -} - -tusb_error_t tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, uint8_t *p_data) -{ - (void) lun; // TODO [MSCH] multiple lun support - - msch_interface_t* p_msch = &msch_data[dev_addr-1]; - - //------------- Command Block Wrapper -------------// - p_msch->cbw.total_bytes = 18; - p_msch->cbw.dir = TUSB_DIR_IN_MASK; - p_msch->cbw.cmd_len = sizeof(scsi_request_sense_t); - - //------------- SCSI command -------------// - scsi_request_sense_t cmd_request_sense = - { - .cmd_code = SCSI_CMD_REQUEST_SENSE, - .alloc_length = 18 - }; - - memcpy(p_msch->cbw.command, &cmd_request_sense, p_msch->cbw.cmd_len); - - TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_data) ); - - return TUSB_ERROR_NONE; -} - -tusb_error_t tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, msc_csw_t * p_csw) -{ - msch_interface_t* p_msch = &msch_data[dev_addr-1]; - - //------------- Command Block Wrapper -------------// - msc_cbw_add_signature(&p_msch->cbw, lun); - - p_msch->cbw.total_bytes = 0; // Number of bytes - p_msch->cbw.dir = TUSB_DIR_OUT; - p_msch->cbw.cmd_len = sizeof(scsi_test_unit_ready_t); - - //------------- SCSI command -------------// - scsi_test_unit_ready_t cmd_test_unit_ready = - { - .cmd_code = SCSI_CMD_TEST_UNIT_READY, - .lun = lun // according to wiki - }; - - memcpy(p_msch->cbw.command, &cmd_test_unit_ready, p_msch->cbw.cmd_len); - - // TODO MSCH refractor test uinit ready - TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t), false), TUSB_ERROR_FAILED ); - TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_in , (uint8_t*) p_csw, sizeof(msc_csw_t), true), TUSB_ERROR_FAILED ); - - return TUSB_ERROR_NONE; -} - -tusb_error_t tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * p_buffer, uint32_t lba, uint16_t block_count) -{ - msch_interface_t* p_msch = &msch_data[dev_addr-1]; - - //------------- Command Block Wrapper -------------// - msc_cbw_add_signature(&p_msch->cbw, lun); - - p_msch->cbw.total_bytes = p_msch->block_size*block_count; // Number of bytes - p_msch->cbw.dir = TUSB_DIR_IN_MASK; - p_msch->cbw.cmd_len = sizeof(scsi_read10_t); - - //------------- SCSI command -------------// - scsi_read10_t cmd_read10 = - { - .cmd_code = SCSI_CMD_READ_10, - .lba = tu_htonl(lba), - .block_count = tu_htons(block_count) - }; - - memcpy(p_msch->cbw.command, &cmd_read10, p_msch->cbw.cmd_len); - - TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_buffer)); - - return TUSB_ERROR_NONE; -} - -tusb_error_t tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * p_buffer, uint32_t lba, uint16_t block_count) -{ - msch_interface_t* p_msch = &msch_data[dev_addr-1]; - - //------------- Command Block Wrapper -------------// - msc_cbw_add_signature(&p_msch->cbw, lun); - - p_msch->cbw.total_bytes = p_msch->block_size*block_count; // Number of bytes - p_msch->cbw.dir = TUSB_DIR_OUT; - p_msch->cbw.cmd_len = sizeof(scsi_write10_t); - - //------------- SCSI command -------------// - scsi_write10_t cmd_write10 = - { - .cmd_code = SCSI_CMD_WRITE_10, - .lba = tu_htonl(lba), - .block_count = tu_htons(block_count) - }; - - memcpy(p_msch->cbw.command, &cmd_write10, p_msch->cbw.cmd_len); - - TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, (void*) p_buffer)); - - return TUSB_ERROR_NONE; -} - -//--------------------------------------------------------------------+ -// CLASS-USBH API (don't require to verify parameters) -//--------------------------------------------------------------------+ -void msch_init(void) -{ - tu_memclr(msch_data, sizeof(msch_interface_t)*CFG_TUSB_HOST_DEVICE_MAX); - msch_sem_hdl = osal_semaphore_create(&msch_sem_def); -} - -bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length) -{ - TU_VERIFY (MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass && - MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol); - - msch_interface_t* p_msc = &msch_data[dev_addr-1]; - - //------------- Open Data Pipe -------------// - tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); - - for(uint32_t i=0; i<2; i++) - { - TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType); - TU_ASSERT(TUSB_XFER_BULK == ep_desc->bmAttributes.xfer); - - TU_ASSERT(hcd_edpt_open(rhport, dev_addr, ep_desc)); - - if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN ) - { - p_msc->ep_in = ep_desc->bEndpointAddress; - }else - { - p_msc->ep_out = ep_desc->bEndpointAddress; - } - - ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(ep_desc); - } - - p_msc->itf_numr = itf_desc->bInterfaceNumber; - (*p_length) += sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t); - - //------------- Get Max Lun -------------// - tusb_control_request_t request = { - .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_IN }, - .bRequest = MSC_REQ_GET_MAX_LUN, - .wValue = 0, - .wIndex = p_msc->itf_numr, - .wLength = 1 - }; - // TODO STALL means zero - TU_ASSERT( usbh_control_xfer( dev_addr, &request, msch_buffer ) ); - p_msc->max_lun = msch_buffer[0]; - -#if 0 - //------------- Reset -------------// - request = (tusb_control_request_t) { - .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT }, - .bRequest = MSC_REQ_RESET, - .wValue = 0, - .wIndex = p_msc->itf_numr, - .wLength = 0 - }; - TU_ASSERT( usbh_control_xfer( dev_addr, &request, NULL ) ); -#endif - - enum { SCSI_XFER_TIMEOUT = 2000 }; - //------------- SCSI Inquiry -------------// - tusbh_msc_inquiry(dev_addr, 0, msch_buffer); - TU_ASSERT( osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT) ); - - memcpy(p_msc->vendor_id , ((scsi_inquiry_resp_t*) msch_buffer)->vendor_id , 8); - memcpy(p_msc->product_id, ((scsi_inquiry_resp_t*) msch_buffer)->product_id, 16); - - //------------- SCSI Read Capacity 10 -------------// - tusbh_msc_read_capacity10(dev_addr, 0, msch_buffer); - TU_ASSERT( osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT)); - - // NOTE: my toshiba thumb-drive stall the first Read Capacity and require the sequence - // Read Capacity --> Stalled --> Clear Stall --> Request Sense --> Read Capacity (2) to work - if ( hcd_edpt_stalled(dev_addr, p_msc->ep_in) ) - { - // clear stall TODO abstract clear stall function - request = (tusb_control_request_t) { - .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_ENDPOINT, .type = TUSB_REQ_TYPE_STANDARD, .direction = TUSB_DIR_OUT }, - .bRequest = TUSB_REQ_CLEAR_FEATURE, - .wValue = 0, - .wIndex = p_msc->ep_in, - .wLength = 0 - }; - - TU_ASSERT(usbh_control_xfer( dev_addr, &request, NULL )); - - hcd_edpt_clear_stall(dev_addr, p_msc->ep_in); - TU_ASSERT( osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT) ); // wait for SCSI status - - //------------- SCSI Request Sense -------------// - (void) tuh_msc_request_sense(dev_addr, 0, msch_buffer); - TU_ASSERT(osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT)); - - //------------- Re-read SCSI Read Capactity -------------// - tusbh_msc_read_capacity10(dev_addr, 0, msch_buffer); - TU_ASSERT(osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT)); - } - - p_msc->last_lba = tu_ntohl( ((scsi_read_capacity10_resp_t*)msch_buffer)->last_lba ); - p_msc->block_size = (uint16_t) tu_ntohl( ((scsi_read_capacity10_resp_t*)msch_buffer)->block_size ); - - p_msc->is_initialized = true; - - tuh_msc_mounted_cb(dev_addr); - - return true; -} - -void msch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) -{ - msch_interface_t* p_msc = &msch_data[dev_addr-1]; - if ( ep_addr == p_msc->ep_in ) - { - if (p_msc->is_initialized) - { - tuh_msc_isr(dev_addr, event, xferred_bytes); - }else - { // still initializing under open subtask - osal_semaphore_post(msch_sem_hdl, true); - } - } -} - -void msch_close(uint8_t dev_addr) -{ - tu_memclr(&msch_data[dev_addr-1], sizeof(msch_interface_t)); - osal_semaphore_reset(msch_sem_hdl); - - tuh_msc_unmounted_cb(dev_addr); // invoke Application Callback -} - -//--------------------------------------------------------------------+ -// INTERNAL & HELPER -//--------------------------------------------------------------------+ - - -#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_HOST_ENABLED & CFG_TUH_MSC + +//--------------------------------------------------------------------+ +// INCLUDE +//--------------------------------------------------------------------+ +#include "common/tusb_common.h" +#include "msc_host.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +CFG_TUSB_MEM_SECTION static msch_interface_t msch_data[CFG_TUSB_HOST_DEVICE_MAX]; + +//------------- Initalization Data -------------// +static osal_semaphore_def_t msch_sem_def; +static osal_semaphore_t msch_sem_hdl; + +// buffer used to read scsi information when mounted, largest response data currently is inquiry +CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t msch_buffer[sizeof(scsi_inquiry_resp_t)]; + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ + +//--------------------------------------------------------------------+ +// PUBLIC API +//--------------------------------------------------------------------+ +bool tuh_msc_is_mounted(uint8_t dev_addr) +{ + return tuh_device_is_configured(dev_addr) && // is configured can be omitted + msch_data[dev_addr-1].is_initialized; +} + +bool tuh_msc_is_busy(uint8_t dev_addr) +{ + return msch_data[dev_addr-1].is_initialized && + hcd_edpt_busy(dev_addr, msch_data[dev_addr-1].ep_in); +} + +uint8_t const* tuh_msc_get_vendor_name(uint8_t dev_addr) +{ + return msch_data[dev_addr-1].is_initialized ? msch_data[dev_addr-1].vendor_id : NULL; +} + +uint8_t const* tuh_msc_get_product_name(uint8_t dev_addr) +{ + return msch_data[dev_addr-1].is_initialized ? msch_data[dev_addr-1].product_id : NULL; +} + +tusb_error_t tuh_msc_get_capacity(uint8_t dev_addr, uint32_t* p_last_lba, uint32_t* p_block_size) +{ + if ( !msch_data[dev_addr-1].is_initialized ) return TUSB_ERROR_MSCH_DEVICE_NOT_MOUNTED; + TU_ASSERT(p_last_lba != NULL && p_block_size != NULL, TUSB_ERROR_INVALID_PARA); + + (*p_last_lba) = msch_data[dev_addr-1].last_lba; + (*p_block_size) = (uint32_t) msch_data[dev_addr-1].block_size; + + return TUSB_ERROR_NONE; +} + +//--------------------------------------------------------------------+ +// PUBLIC API: SCSI COMMAND +//--------------------------------------------------------------------+ +static inline void msc_cbw_add_signature(msc_cbw_t *p_cbw, uint8_t lun) +{ + p_cbw->signature = MSC_CBW_SIGNATURE; + p_cbw->tag = 0xCAFECAFE; + p_cbw->lun = lun; +} + +static tusb_error_t msch_command_xfer(uint8_t dev_addr, msch_interface_t * p_msch, void* p_buffer) +{ + if ( NULL != p_buffer) + { // there is data phase + if (p_msch->cbw.dir & TUSB_DIR_IN_MASK) + { + TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t), false), TUSB_ERROR_FAILED ); + TU_ASSERT( hcd_pipe_queue_xfer(dev_addr, p_msch->ep_in , p_buffer, p_msch->cbw.total_bytes), TUSB_ERROR_FAILED ); + }else + { + TU_ASSERT( hcd_pipe_queue_xfer(dev_addr, p_msch->ep_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t)), TUSB_ERROR_FAILED ); + TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_out , p_buffer, p_msch->cbw.total_bytes, false), TUSB_ERROR_FAILED ); + } + } + + TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_in , (uint8_t*) &p_msch->csw, sizeof(msc_csw_t), true), TUSB_ERROR_FAILED); + + return TUSB_ERROR_NONE; +} + +tusb_error_t tusbh_msc_inquiry(uint8_t dev_addr, uint8_t lun, uint8_t *p_data) +{ + msch_interface_t* p_msch = &msch_data[dev_addr-1]; + + //------------- Command Block Wrapper -------------// + msc_cbw_add_signature(&p_msch->cbw, lun); + p_msch->cbw.total_bytes = sizeof(scsi_inquiry_resp_t); + p_msch->cbw.dir = TUSB_DIR_IN_MASK; + p_msch->cbw.cmd_len = sizeof(scsi_inquiry_t); + + //------------- SCSI command -------------// + scsi_inquiry_t cmd_inquiry = + { + .cmd_code = SCSI_CMD_INQUIRY, + .alloc_length = sizeof(scsi_inquiry_resp_t) + }; + + memcpy(p_msch->cbw.command, &cmd_inquiry, p_msch->cbw.cmd_len); + + TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_data) ); + + return TUSB_ERROR_NONE; +} + +tusb_error_t tusbh_msc_read_capacity10(uint8_t dev_addr, uint8_t lun, uint8_t *p_data) +{ + msch_interface_t* p_msch = &msch_data[dev_addr-1]; + + //------------- Command Block Wrapper -------------// + msc_cbw_add_signature(&p_msch->cbw, lun); + p_msch->cbw.total_bytes = sizeof(scsi_read_capacity10_resp_t); + p_msch->cbw.dir = TUSB_DIR_IN_MASK; + p_msch->cbw.cmd_len = sizeof(scsi_read_capacity10_t); + + //------------- SCSI command -------------// + scsi_read_capacity10_t cmd_read_capacity10 = + { + .cmd_code = SCSI_CMD_READ_CAPACITY_10, + .lba = 0, + .partial_medium_indicator = 0 + }; + + memcpy(p_msch->cbw.command, &cmd_read_capacity10, p_msch->cbw.cmd_len); + + TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_data) ); + + return TUSB_ERROR_NONE; +} + +tusb_error_t tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, uint8_t *p_data) +{ + (void) lun; // TODO [MSCH] multiple lun support + + msch_interface_t* p_msch = &msch_data[dev_addr-1]; + + //------------- Command Block Wrapper -------------// + p_msch->cbw.total_bytes = 18; + p_msch->cbw.dir = TUSB_DIR_IN_MASK; + p_msch->cbw.cmd_len = sizeof(scsi_request_sense_t); + + //------------- SCSI command -------------// + scsi_request_sense_t cmd_request_sense = + { + .cmd_code = SCSI_CMD_REQUEST_SENSE, + .alloc_length = 18 + }; + + memcpy(p_msch->cbw.command, &cmd_request_sense, p_msch->cbw.cmd_len); + + TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_data) ); + + return TUSB_ERROR_NONE; +} + +tusb_error_t tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, msc_csw_t * p_csw) +{ + msch_interface_t* p_msch = &msch_data[dev_addr-1]; + + //------------- Command Block Wrapper -------------// + msc_cbw_add_signature(&p_msch->cbw, lun); + + p_msch->cbw.total_bytes = 0; // Number of bytes + p_msch->cbw.dir = TUSB_DIR_OUT; + p_msch->cbw.cmd_len = sizeof(scsi_test_unit_ready_t); + + //------------- SCSI command -------------// + scsi_test_unit_ready_t cmd_test_unit_ready = + { + .cmd_code = SCSI_CMD_TEST_UNIT_READY, + .lun = lun // according to wiki + }; + + memcpy(p_msch->cbw.command, &cmd_test_unit_ready, p_msch->cbw.cmd_len); + + // TODO MSCH refractor test uinit ready + TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t), false), TUSB_ERROR_FAILED ); + TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_in , (uint8_t*) p_csw, sizeof(msc_csw_t), true), TUSB_ERROR_FAILED ); + + return TUSB_ERROR_NONE; +} + +tusb_error_t tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * p_buffer, uint32_t lba, uint16_t block_count) +{ + msch_interface_t* p_msch = &msch_data[dev_addr-1]; + + //------------- Command Block Wrapper -------------// + msc_cbw_add_signature(&p_msch->cbw, lun); + + p_msch->cbw.total_bytes = p_msch->block_size*block_count; // Number of bytes + p_msch->cbw.dir = TUSB_DIR_IN_MASK; + p_msch->cbw.cmd_len = sizeof(scsi_read10_t); + + //------------- SCSI command -------------// + scsi_read10_t cmd_read10 = + { + .cmd_code = SCSI_CMD_READ_10, + .lba = tu_htonl(lba), + .block_count = tu_htons(block_count) + }; + + memcpy(p_msch->cbw.command, &cmd_read10, p_msch->cbw.cmd_len); + + TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_buffer)); + + return TUSB_ERROR_NONE; +} + +tusb_error_t tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * p_buffer, uint32_t lba, uint16_t block_count) +{ + msch_interface_t* p_msch = &msch_data[dev_addr-1]; + + //------------- Command Block Wrapper -------------// + msc_cbw_add_signature(&p_msch->cbw, lun); + + p_msch->cbw.total_bytes = p_msch->block_size*block_count; // Number of bytes + p_msch->cbw.dir = TUSB_DIR_OUT; + p_msch->cbw.cmd_len = sizeof(scsi_write10_t); + + //------------- SCSI command -------------// + scsi_write10_t cmd_write10 = + { + .cmd_code = SCSI_CMD_WRITE_10, + .lba = tu_htonl(lba), + .block_count = tu_htons(block_count) + }; + + memcpy(p_msch->cbw.command, &cmd_write10, p_msch->cbw.cmd_len); + + TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, (void*) p_buffer)); + + return TUSB_ERROR_NONE; +} + +//--------------------------------------------------------------------+ +// CLASS-USBH API (don't require to verify parameters) +//--------------------------------------------------------------------+ +void msch_init(void) +{ + tu_memclr(msch_data, sizeof(msch_interface_t)*CFG_TUSB_HOST_DEVICE_MAX); + msch_sem_hdl = osal_semaphore_create(&msch_sem_def); +} + +bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length) +{ + TU_VERIFY (MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass && + MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol); + + msch_interface_t* p_msc = &msch_data[dev_addr-1]; + + //------------- Open Data Pipe -------------// + tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); + + for(uint32_t i=0; i<2; i++) + { + TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType); + TU_ASSERT(TUSB_XFER_BULK == ep_desc->bmAttributes.xfer); + + TU_ASSERT(hcd_edpt_open(rhport, dev_addr, ep_desc)); + + if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN ) + { + p_msc->ep_in = ep_desc->bEndpointAddress; + }else + { + p_msc->ep_out = ep_desc->bEndpointAddress; + } + + ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(ep_desc); + } + + p_msc->itf_numr = itf_desc->bInterfaceNumber; + (*p_length) += sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t); + + //------------- Get Max Lun -------------// + tusb_control_request_t request = { + .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_IN }, + .bRequest = MSC_REQ_GET_MAX_LUN, + .wValue = 0, + .wIndex = p_msc->itf_numr, + .wLength = 1 + }; + // TODO STALL means zero + TU_ASSERT( usbh_control_xfer( dev_addr, &request, msch_buffer ) ); + p_msc->max_lun = msch_buffer[0]; + +#if 0 + //------------- Reset -------------// + request = (tusb_control_request_t) { + .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT }, + .bRequest = MSC_REQ_RESET, + .wValue = 0, + .wIndex = p_msc->itf_numr, + .wLength = 0 + }; + TU_ASSERT( usbh_control_xfer( dev_addr, &request, NULL ) ); +#endif + + enum { SCSI_XFER_TIMEOUT = 2000 }; + //------------- SCSI Inquiry -------------// + tusbh_msc_inquiry(dev_addr, 0, msch_buffer); + TU_ASSERT( osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT) ); + + memcpy(p_msc->vendor_id , ((scsi_inquiry_resp_t*) msch_buffer)->vendor_id , 8); + memcpy(p_msc->product_id, ((scsi_inquiry_resp_t*) msch_buffer)->product_id, 16); + + //------------- SCSI Read Capacity 10 -------------// + tusbh_msc_read_capacity10(dev_addr, 0, msch_buffer); + TU_ASSERT( osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT)); + + // NOTE: my toshiba thumb-drive stall the first Read Capacity and require the sequence + // Read Capacity --> Stalled --> Clear Stall --> Request Sense --> Read Capacity (2) to work + if ( hcd_edpt_stalled(dev_addr, p_msc->ep_in) ) + { + // clear stall TODO abstract clear stall function + request = (tusb_control_request_t) { + .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_ENDPOINT, .type = TUSB_REQ_TYPE_STANDARD, .direction = TUSB_DIR_OUT }, + .bRequest = TUSB_REQ_CLEAR_FEATURE, + .wValue = 0, + .wIndex = p_msc->ep_in, + .wLength = 0 + }; + + TU_ASSERT(usbh_control_xfer( dev_addr, &request, NULL )); + + hcd_edpt_clear_stall(dev_addr, p_msc->ep_in); + TU_ASSERT( osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT) ); // wait for SCSI status + + //------------- SCSI Request Sense -------------// + (void) tuh_msc_request_sense(dev_addr, 0, msch_buffer); + TU_ASSERT(osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT)); + + //------------- Re-read SCSI Read Capactity -------------// + tusbh_msc_read_capacity10(dev_addr, 0, msch_buffer); + TU_ASSERT(osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT)); + } + + p_msc->last_lba = tu_ntohl( ((scsi_read_capacity10_resp_t*)msch_buffer)->last_lba ); + p_msc->block_size = (uint16_t) tu_ntohl( ((scsi_read_capacity10_resp_t*)msch_buffer)->block_size ); + + p_msc->is_initialized = true; + + tuh_msc_mounted_cb(dev_addr); + + return true; +} + +void msch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) +{ + msch_interface_t* p_msc = &msch_data[dev_addr-1]; + if ( ep_addr == p_msc->ep_in ) + { + if (p_msc->is_initialized) + { + tuh_msc_isr(dev_addr, event, xferred_bytes); + }else + { // still initializing under open subtask + osal_semaphore_post(msch_sem_hdl, true); + } + } +} + +void msch_close(uint8_t dev_addr) +{ + tu_memclr(&msch_data[dev_addr-1], sizeof(msch_interface_t)); + osal_semaphore_reset(msch_sem_hdl); + + tuh_msc_unmounted_cb(dev_addr); // invoke Application Callback +} + +//--------------------------------------------------------------------+ +// INTERNAL & HELPER +//--------------------------------------------------------------------+ + + +#endif diff --git a/src/class/msc/msc_host.h b/src/class/msc/msc_host.h index da817fbe2..76cf86853 100644 --- a/src/class/msc/msc_host.h +++ b/src/class/msc/msc_host.h @@ -1,206 +1,206 @@ -/* - * 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. - */ - -#ifndef _TUSB_MSC_HOST_H_ -#define _TUSB_MSC_HOST_H_ - -#include "common/tusb_common.h" -#include "host/usbh.h" -#include "msc.h" - -#ifdef __cplusplus - extern "C" { -#endif - -/** \addtogroup ClassDriver_MSC - * @{ - * \defgroup MSC_Host Host - * The interface API includes status checking function, data transferring function and callback functions - * @{ */ -//--------------------------------------------------------------------+ -// MASS STORAGE Application API -//--------------------------------------------------------------------+ -/** \brief Check if device supports MassStorage interface or not - * \param[in] dev_addr device address - * \retval true if device supports - * \retval false if device does not support or is not mounted - */ -bool tuh_msc_is_mounted(uint8_t dev_addr); - -/** \brief Check if the interface is currently busy or not - * \param[in] dev_addr device address - * \retval true if the interface is busy meaning the stack is still transferring/waiting data from/to device - * \retval false if the interface is not busy meaning the stack successfully transferred data from/to device - * \note This function is used to check if previous transfer is complete (success or error), so that the next transfer - * can be scheduled. User needs to make sure the corresponding interface is mounted (by \ref tuh_msc_is_mounted) - * before calling this function - */ -bool tuh_msc_is_busy(uint8_t dev_addr); - -/** \brief Get SCSI vendor's name of MassStorage device - * \param[in] dev_addr device address - * \return pointer to vendor's name or NULL if specified device does not support MassStorage - * \note SCSI vendor's name is 8-byte length field in \ref scsi_inquiry_data_t. During enumeration, the stack has already - * retrieved (via SCSI INQUIRY) and store this information internally. There is no need for application to re-send SCSI INQUIRY - * command or allocate buffer for this. - */ -uint8_t const* tuh_msc_get_vendor_name(uint8_t dev_addr); - -/** \brief Get SCSI product's name of MassStorage device - * \param[in] dev_addr device address - * \return pointer to product's name or NULL if specified device does not support MassStorage - * \note SCSI product's name is 16-byte length field in \ref scsi_inquiry_data_t. During enumeration, the stack has already - * retrieved (via SCSI INQUIRY) and store this information internally. There is no need for application to re-send SCSI INQUIRY - * command or allocate buffer for this. - */ -uint8_t const* tuh_msc_get_product_name(uint8_t dev_addr); - -/** \brief Get SCSI Capacity of MassStorage device - * \param[in] dev_addr device address - * \param[out] p_last_lba Last Logical Block Address of device - * \param[out] p_block_size Block Size of device in bytes - * \retval pointer to product's name or NULL if specified device does not support MassStorage - * \note MassStorage's capacity can be computed by last LBA x block size (in bytes). During enumeration, the stack has already - * retrieved (via SCSI READ CAPACITY 10) and store this information internally. There is no need for application - * to re-send SCSI READ CAPACITY 10 command - */ -tusb_error_t tuh_msc_get_capacity(uint8_t dev_addr, uint32_t* p_last_lba, uint32_t* p_block_size); - -/** \brief Perform SCSI READ 10 command to read data from MassStorage device - * \param[in] dev_addr device address - * \param[in] lun Targeted Logical Unit - * \param[out] p_buffer Buffer used to store data read from device. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION) - * \param[in] lba Starting Logical Block Address to be read - * \param[in] block_count Number of Block to be read - * \retval TUSB_ERROR_NONE on success - * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device - * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) - * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct - * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function - */ -tusb_error_t tuh_msc_read10 (uint8_t dev_addr, uint8_t lun, void * p_buffer, uint32_t lba, uint16_t block_count); - -/** \brief Perform SCSI WRITE 10 command to write data to MassStorage device - * \param[in] dev_addr device address - * \param[in] lun Targeted Logical Unit - * \param[in] p_buffer Buffer containing data. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION) - * \param[in] lba Starting Logical Block Address to be written - * \param[in] block_count Number of Block to be written - * \retval TUSB_ERROR_NONE on success - * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device - * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) - * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct - * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function - */ -tusb_error_t tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * p_buffer, uint32_t lba, uint16_t block_count); - -/** \brief Perform SCSI REQUEST SENSE command, used to retrieve sense data from MassStorage device - * \param[in] dev_addr device address - * \param[in] lun Targeted Logical Unit - * \param[in] p_data Buffer to store response's data from device. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION) - * \retval TUSB_ERROR_NONE on success - * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device - * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) - * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct - * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function - */ -tusb_error_t tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, uint8_t *p_data); - -/** \brief Perform SCSI TEST UNIT READY command to test if MassStorage device is ready - * \param[in] dev_addr device address - * \param[in] lun Targeted Logical Unit - * \retval TUSB_ERROR_NONE on success - * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device - * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) - * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct - * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function - */ -tusb_error_t tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, msc_csw_t * p_csw); // TODO to be refractor - -//tusb_error_t tusbh_msc_scsi_send(uint8_t dev_addr, uint8_t lun, bool is_direction_in, -// uint8_t const * p_command, uint8_t cmd_len, -// uint8_t * p_response, uint32_t resp_len); - -//------------- Application Callback -------------// -/** \brief Callback function that will be invoked when a device with MassStorage interface is mounted - * \param[in] dev_addr Address of newly mounted device - * \note This callback should be used by Application to set-up interface-related data - */ -void tuh_msc_mounted_cb(uint8_t dev_addr); - -/** \brief Callback function that will be invoked when a device with MassStorage interface is unmounted - * \param[in] dev_addr Address of newly unmounted device - * \note This callback should be used by Application to tear-down interface-related data - */ -void tuh_msc_unmounted_cb(uint8_t dev_addr); - -/** \brief Callback function that is invoked when an transferring event occurred - * \param[in] dev_addr Address of device - * \param[in] event an value from \ref xfer_result_t - * \param[in] xferred_bytes Number of bytes transferred via USB bus - * \note event can be one of following - * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully. - * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error. - * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device. - * \note - */ -void tuh_msc_isr(uint8_t dev_addr, xfer_result_t event, uint32_t xferred_bytes); - - -//--------------------------------------------------------------------+ -// Internal Class Driver API -//--------------------------------------------------------------------+ -typedef struct -{ - uint8_t itf_numr; - uint8_t ep_in; - uint8_t ep_out; - - uint8_t max_lun; - uint16_t block_size; - uint32_t last_lba; // last logical block address - - volatile bool is_initialized; - uint8_t vendor_id[8]; - uint8_t product_id[16]; - - msc_cbw_t cbw; - msc_csw_t csw; -}msch_interface_t; - -void msch_init(void); -bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length); -void msch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); -void msch_close(uint8_t dev_addr); - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_MSC_HOST_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. + */ + +#ifndef _TUSB_MSC_HOST_H_ +#define _TUSB_MSC_HOST_H_ + +#include "common/tusb_common.h" +#include "host/usbh.h" +#include "msc.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/** \addtogroup ClassDriver_MSC + * @{ + * \defgroup MSC_Host Host + * The interface API includes status checking function, data transferring function and callback functions + * @{ */ +//--------------------------------------------------------------------+ +// MASS STORAGE Application API +//--------------------------------------------------------------------+ +/** \brief Check if device supports MassStorage interface or not + * \param[in] dev_addr device address + * \retval true if device supports + * \retval false if device does not support or is not mounted + */ +bool tuh_msc_is_mounted(uint8_t dev_addr); + +/** \brief Check if the interface is currently busy or not + * \param[in] dev_addr device address + * \retval true if the interface is busy meaning the stack is still transferring/waiting data from/to device + * \retval false if the interface is not busy meaning the stack successfully transferred data from/to device + * \note This function is used to check if previous transfer is complete (success or error), so that the next transfer + * can be scheduled. User needs to make sure the corresponding interface is mounted (by \ref tuh_msc_is_mounted) + * before calling this function + */ +bool tuh_msc_is_busy(uint8_t dev_addr); + +/** \brief Get SCSI vendor's name of MassStorage device + * \param[in] dev_addr device address + * \return pointer to vendor's name or NULL if specified device does not support MassStorage + * \note SCSI vendor's name is 8-byte length field in \ref scsi_inquiry_data_t. During enumeration, the stack has already + * retrieved (via SCSI INQUIRY) and store this information internally. There is no need for application to re-send SCSI INQUIRY + * command or allocate buffer for this. + */ +uint8_t const* tuh_msc_get_vendor_name(uint8_t dev_addr); + +/** \brief Get SCSI product's name of MassStorage device + * \param[in] dev_addr device address + * \return pointer to product's name or NULL if specified device does not support MassStorage + * \note SCSI product's name is 16-byte length field in \ref scsi_inquiry_data_t. During enumeration, the stack has already + * retrieved (via SCSI INQUIRY) and store this information internally. There is no need for application to re-send SCSI INQUIRY + * command or allocate buffer for this. + */ +uint8_t const* tuh_msc_get_product_name(uint8_t dev_addr); + +/** \brief Get SCSI Capacity of MassStorage device + * \param[in] dev_addr device address + * \param[out] p_last_lba Last Logical Block Address of device + * \param[out] p_block_size Block Size of device in bytes + * \retval pointer to product's name or NULL if specified device does not support MassStorage + * \note MassStorage's capacity can be computed by last LBA x block size (in bytes). During enumeration, the stack has already + * retrieved (via SCSI READ CAPACITY 10) and store this information internally. There is no need for application + * to re-send SCSI READ CAPACITY 10 command + */ +tusb_error_t tuh_msc_get_capacity(uint8_t dev_addr, uint32_t* p_last_lba, uint32_t* p_block_size); + +/** \brief Perform SCSI READ 10 command to read data from MassStorage device + * \param[in] dev_addr device address + * \param[in] lun Targeted Logical Unit + * \param[out] p_buffer Buffer used to store data read from device. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION) + * \param[in] lba Starting Logical Block Address to be read + * \param[in] block_count Number of Block to be read + * \retval TUSB_ERROR_NONE on success + * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device + * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) + * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct + * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function + */ +tusb_error_t tuh_msc_read10 (uint8_t dev_addr, uint8_t lun, void * p_buffer, uint32_t lba, uint16_t block_count); + +/** \brief Perform SCSI WRITE 10 command to write data to MassStorage device + * \param[in] dev_addr device address + * \param[in] lun Targeted Logical Unit + * \param[in] p_buffer Buffer containing data. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION) + * \param[in] lba Starting Logical Block Address to be written + * \param[in] block_count Number of Block to be written + * \retval TUSB_ERROR_NONE on success + * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device + * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) + * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct + * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function + */ +tusb_error_t tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * p_buffer, uint32_t lba, uint16_t block_count); + +/** \brief Perform SCSI REQUEST SENSE command, used to retrieve sense data from MassStorage device + * \param[in] dev_addr device address + * \param[in] lun Targeted Logical Unit + * \param[in] p_data Buffer to store response's data from device. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION) + * \retval TUSB_ERROR_NONE on success + * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device + * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) + * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct + * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function + */ +tusb_error_t tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, uint8_t *p_data); + +/** \brief Perform SCSI TEST UNIT READY command to test if MassStorage device is ready + * \param[in] dev_addr device address + * \param[in] lun Targeted Logical Unit + * \retval TUSB_ERROR_NONE on success + * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device + * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) + * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct + * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function + */ +tusb_error_t tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, msc_csw_t * p_csw); // TODO to be refractor + +//tusb_error_t tusbh_msc_scsi_send(uint8_t dev_addr, uint8_t lun, bool is_direction_in, +// uint8_t const * p_command, uint8_t cmd_len, +// uint8_t * p_response, uint32_t resp_len); + +//------------- Application Callback -------------// +/** \brief Callback function that will be invoked when a device with MassStorage interface is mounted + * \param[in] dev_addr Address of newly mounted device + * \note This callback should be used by Application to set-up interface-related data + */ +void tuh_msc_mounted_cb(uint8_t dev_addr); + +/** \brief Callback function that will be invoked when a device with MassStorage interface is unmounted + * \param[in] dev_addr Address of newly unmounted device + * \note This callback should be used by Application to tear-down interface-related data + */ +void tuh_msc_unmounted_cb(uint8_t dev_addr); + +/** \brief Callback function that is invoked when an transferring event occurred + * \param[in] dev_addr Address of device + * \param[in] event an value from \ref xfer_result_t + * \param[in] xferred_bytes Number of bytes transferred via USB bus + * \note event can be one of following + * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully. + * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error. + * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device. + * \note + */ +void tuh_msc_isr(uint8_t dev_addr, xfer_result_t event, uint32_t xferred_bytes); + + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +typedef struct +{ + uint8_t itf_numr; + uint8_t ep_in; + uint8_t ep_out; + + uint8_t max_lun; + uint16_t block_size; + uint32_t last_lba; // last logical block address + + volatile bool is_initialized; + uint8_t vendor_id[8]; + uint8_t product_id[16]; + + msc_cbw_t cbw; + msc_csw_t csw; +}msch_interface_t; + +void msch_init(void); +bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length); +void msch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); +void msch_close(uint8_t dev_addr); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_MSC_HOST_H_ */ + +/// @} +/// @} diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h index 839989c92..5e6a8bb10 100644 --- a/src/common/tusb_common.h +++ b/src/common/tusb_common.h @@ -1,275 +1,275 @@ -/* - * 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_Common - * \defgroup Group_CommonH common.h - * @{ */ - -#ifndef _TUSB_COMMON_H_ -#define _TUSB_COMMON_H_ - -#ifdef __cplusplus - extern "C" { -#endif - -//--------------------------------------------------------------------+ -// Macros Helper -//--------------------------------------------------------------------+ -#define TU_ARRAY_SIZE(_arr) ( sizeof(_arr) / sizeof(_arr[0]) ) -#define TU_MIN(_x, _y) ( ( (_x) < (_y) ) ? (_x) : (_y) ) -#define TU_MAX(_x, _y) ( ( (_x) > (_y) ) ? (_x) : (_y) ) - -#define TU_U16_HIGH(u16) ((uint8_t) (((u16) >> 8) & 0x00ff)) -#define TU_U16_LOW(u16) ((uint8_t) ((u16) & 0x00ff)) -#define U16_TO_U8S_BE(u16) TU_U16_HIGH(u16), TU_U16_LOW(u16) -#define U16_TO_U8S_LE(u16) TU_U16_LOW(u16), TU_U16_HIGH(u16) - -#define U32_B1_U8(u32) ((uint8_t) (((u32) >> 24) & 0x000000ff)) // MSB -#define U32_B2_U8(u32) ((uint8_t) (((u32) >> 16) & 0x000000ff)) -#define U32_B3_U8(u32) ((uint8_t) (((u32) >> 8) & 0x000000ff)) -#define U32_B4_U8(u32) ((uint8_t) ((u32) & 0x000000ff)) // LSB - -#define U32_TO_U8S_BE(u32) U32_B1_U8(u32), U32_B2_U8(u32), U32_B3_U8(u32), U32_B4_U8(u32) -#define U32_TO_U8S_LE(u32) U32_B4_U8(u32), U32_B3_U8(u32), U32_B2_U8(u32), U32_B1_U8(u32) - -#define TU_BIT(n) (1U << (n)) - -//--------------------------------------------------------------------+ -// Includes -//--------------------------------------------------------------------+ - -// Standard Headers -#include -#include -#include -#include -#include - -// Tinyusb Common Headers -#include "tusb_option.h" -#include "tusb_compiler.h" -#include "tusb_verify.h" -#include "tusb_error.h" // TODO remove -#include "tusb_timeout.h" -#include "tusb_types.h" - -//--------------------------------------------------------------------+ -// Inline Functions -//--------------------------------------------------------------------+ -#define tu_memclr(buffer, size) memset((buffer), 0, (size)) -#define tu_varclr(_var) tu_memclr(_var, sizeof(*(_var))) - -static inline uint32_t tu_u32(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4) -{ - return ( ((uint32_t) b1) << 24) + ( ((uint32_t) b2) << 16) + ( ((uint32_t) b3) << 8) + b4; -} - -static inline uint16_t tu_u16(uint8_t high, uint8_t low) -{ - return (uint16_t)((((uint16_t) high) << 8) + low); -} - -static inline uint8_t tu_u16_high(uint16_t u16) { return (uint8_t) (((uint16_t) (u16 >> 8)) & 0x00ff); } -static inline uint8_t tu_u16_low (uint16_t u16) { return (uint8_t) (u16 & 0x00ff); } - -// Min -static inline uint8_t tu_min8 (uint8_t x, uint8_t y ) { return (x < y) ? x : y; } -static inline uint16_t tu_min16 (uint16_t x, uint16_t y) { return (x < y) ? x : y; } -static inline uint32_t tu_min32 (uint32_t x, uint32_t y) { return (x < y) ? x : y; } - -// Max -static inline uint8_t tu_max8 (uint8_t x, uint8_t y ) { return (x > y) ? x : y; } -static inline uint16_t tu_max16 (uint16_t x, uint16_t y) { return (x > y) ? x : y; } -static inline uint32_t tu_max32 (uint32_t x, uint32_t y) { return (x > y) ? x : y; } - -// Align -static inline uint32_t tu_align_n(uint32_t value, uint32_t alignment) -{ - return value & ((uint32_t) ~(alignment-1)); -} - -static inline uint32_t tu_align32 (uint32_t value) { return (value & 0xFFFFFFE0UL); } -static inline uint32_t tu_align16 (uint32_t value) { return (value & 0xFFFFFFF0UL); } -static inline uint32_t tu_align4k (uint32_t value) { return (value & 0xFFFFF000UL); } -static inline uint32_t tu_offset4k(uint32_t value) { return (value & 0xFFFUL); } - -//------------- Mathematics -------------// -static inline uint32_t tu_abs(int32_t value) { return (uint32_t)((value < 0) ? (-value) : value); } - -/// inclusive range checking -static inline bool tu_within(uint32_t lower, uint32_t value, uint32_t upper) -{ - return (lower <= value) && (value <= upper); -} - -// log2 of a value is its MSB's position -// TODO use clz TODO remove -static inline uint8_t tu_log2(uint32_t value) -{ - uint8_t result = 0; - - while (value >>= 1) - { - result++; - } - return result; -} - -// Bit -static inline uint32_t tu_bit_set (uint32_t value, uint8_t pos) { return value | TU_BIT(pos); } -static inline uint32_t tu_bit_clear(uint32_t value, uint8_t pos) { return value & (~TU_BIT(pos)); } -static inline bool tu_bit_test (uint32_t value, uint8_t pos) { return (value & TU_BIT(pos)) ? true : false; } - -/*------------------------------------------------------------------*/ -/* Count number of arguments of __VA_ARGS__ - * - reference https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s - * - _GET_NTH_ARG() takes args >= N (64) but only expand to Nth one (64th) - * - _RSEQ_N() is reverse sequential to N to add padding to have - * Nth position is the same as the number of arguments - * - ##__VA_ARGS__ is used to deal with 0 paramerter (swallows comma) - *------------------------------------------------------------------*/ -#ifndef TU_ARGS_NUM - -#define TU_ARGS_NUM(...) NARG_(_0, ##__VA_ARGS__,_RSEQ_N()) - -#define NARG_(...) _GET_NTH_ARG(__VA_ARGS__) -#define _GET_NTH_ARG( \ - _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ - _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ - _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ - _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ - _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ - _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ - _61,_62,_63,N,...) N -#define _RSEQ_N() \ - 62,61,60, \ - 59,58,57,56,55,54,53,52,51,50, \ - 49,48,47,46,45,44,43,42,41,40, \ - 39,38,37,36,35,34,33,32,31,30, \ - 29,28,27,26,25,24,23,22,21,20, \ - 19,18,17,16,15,14,13,12,11,10, \ - 9,8,7,6,5,4,3,2,1,0 -#endif - -// To be removed -//------------- Binary constant -------------// -#if defined(__GNUC__) && !defined(__CC_ARM) - -#define TU_BIN8(x) ((uint8_t) (0b##x)) -#define TU_BIN16(b1, b2) ((uint16_t) (0b##b1##b2)) -#define TU_BIN32(b1, b2, b3, b4) ((uint32_t) (0b##b1##b2##b3##b4)) - -#else - -// internal macro of B8, B16, B32 -#define _B8__(x) (((x&0x0000000FUL)?1:0) \ - +((x&0x000000F0UL)?2:0) \ - +((x&0x00000F00UL)?4:0) \ - +((x&0x0000F000UL)?8:0) \ - +((x&0x000F0000UL)?16:0) \ - +((x&0x00F00000UL)?32:0) \ - +((x&0x0F000000UL)?64:0) \ - +((x&0xF0000000UL)?128:0)) - -#define TU_BIN8(d) ((uint8_t) _B8__(0x##d##UL)) -#define TU_BIN16(dmsb,dlsb) (((uint16_t)TU_BIN8(dmsb)<<8) + TU_BIN8(dlsb)) -#define TU_BIN32(dmsb,db2,db3,dlsb) \ - (((uint32_t)TU_BIN8(dmsb)<<24) \ - + ((uint32_t)TU_BIN8(db2)<<16) \ - + ((uint32_t)TU_BIN8(db3)<<8) \ - + TU_BIN8(dlsb)) -#endif - -//--------------------------------------------------------------------+ -// Debug Function -//--------------------------------------------------------------------+ - -// CFG_TUSB_DEBUG for debugging -// 0 : no debug -// 1 : print when there is error -// 2 : print out log -#if CFG_TUSB_DEBUG - -void tu_print_mem(void const *buf, uint16_t count, uint8_t indent); - -#ifndef tu_printf - #define tu_printf printf -#endif - -// Log with debug level 1 -#define TU_LOG1 tu_printf -#define TU_LOG1_MEM tu_print_mem -#define TU_LOG1_LOCATION() tu_printf("%s: %d:\n", __PRETTY_FUNCTION__, __LINE__) - -// Log with debug level 2 -#if CFG_TUSB_DEBUG > 1 - #define TU_LOG2 TU_LOG1 - #define TU_LOG2_MEM TU_LOG1_MEM - #define TU_LOG2_LOCATION() TU_LOG1_LOCATION() -#endif - - -typedef struct -{ - uint32_t key; - char const * data; -}lookup_entry_t; - -typedef struct -{ - uint16_t count; - lookup_entry_t const* items; -} lookup_table_t; - -static inline char const* lookup_find(lookup_table_t const* p_table, uint32_t key) -{ - for(uint16_t i=0; icount; i++) - { - if (p_table->items[i].key == key) return p_table->items[i].data; - } - - return NULL; -} - -#endif // CFG_TUSB_DEBUG - -#ifndef TU_LOG1 - #define TU_LOG1(...) - #define TU_LOG1_MEM(...) -#endif - -#ifndef TU_LOG2 - #define TU_LOG2(...) - #define TU_LOG2_MEM(...) -#endif - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_COMMON_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_Common + * \defgroup Group_CommonH common.h + * @{ */ + +#ifndef _TUSB_COMMON_H_ +#define _TUSB_COMMON_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Macros Helper +//--------------------------------------------------------------------+ +#define TU_ARRAY_SIZE(_arr) ( sizeof(_arr) / sizeof(_arr[0]) ) +#define TU_MIN(_x, _y) ( ( (_x) < (_y) ) ? (_x) : (_y) ) +#define TU_MAX(_x, _y) ( ( (_x) > (_y) ) ? (_x) : (_y) ) + +#define TU_U16_HIGH(u16) ((uint8_t) (((u16) >> 8) & 0x00ff)) +#define TU_U16_LOW(u16) ((uint8_t) ((u16) & 0x00ff)) +#define U16_TO_U8S_BE(u16) TU_U16_HIGH(u16), TU_U16_LOW(u16) +#define U16_TO_U8S_LE(u16) TU_U16_LOW(u16), TU_U16_HIGH(u16) + +#define U32_B1_U8(u32) ((uint8_t) (((u32) >> 24) & 0x000000ff)) // MSB +#define U32_B2_U8(u32) ((uint8_t) (((u32) >> 16) & 0x000000ff)) +#define U32_B3_U8(u32) ((uint8_t) (((u32) >> 8) & 0x000000ff)) +#define U32_B4_U8(u32) ((uint8_t) ((u32) & 0x000000ff)) // LSB + +#define U32_TO_U8S_BE(u32) U32_B1_U8(u32), U32_B2_U8(u32), U32_B3_U8(u32), U32_B4_U8(u32) +#define U32_TO_U8S_LE(u32) U32_B4_U8(u32), U32_B3_U8(u32), U32_B2_U8(u32), U32_B1_U8(u32) + +#define TU_BIT(n) (1U << (n)) + +//--------------------------------------------------------------------+ +// Includes +//--------------------------------------------------------------------+ + +// Standard Headers +#include +#include +#include +#include +#include + +// Tinyusb Common Headers +#include "tusb_option.h" +#include "tusb_compiler.h" +#include "tusb_verify.h" +#include "tusb_error.h" // TODO remove +#include "tusb_timeout.h" +#include "tusb_types.h" + +//--------------------------------------------------------------------+ +// Inline Functions +//--------------------------------------------------------------------+ +#define tu_memclr(buffer, size) memset((buffer), 0, (size)) +#define tu_varclr(_var) tu_memclr(_var, sizeof(*(_var))) + +static inline uint32_t tu_u32(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4) +{ + return ( ((uint32_t) b1) << 24) + ( ((uint32_t) b2) << 16) + ( ((uint32_t) b3) << 8) + b4; +} + +static inline uint16_t tu_u16(uint8_t high, uint8_t low) +{ + return (uint16_t)((((uint16_t) high) << 8) + low); +} + +static inline uint8_t tu_u16_high(uint16_t u16) { return (uint8_t) (((uint16_t) (u16 >> 8)) & 0x00ff); } +static inline uint8_t tu_u16_low (uint16_t u16) { return (uint8_t) (u16 & 0x00ff); } + +// Min +static inline uint8_t tu_min8 (uint8_t x, uint8_t y ) { return (x < y) ? x : y; } +static inline uint16_t tu_min16 (uint16_t x, uint16_t y) { return (x < y) ? x : y; } +static inline uint32_t tu_min32 (uint32_t x, uint32_t y) { return (x < y) ? x : y; } + +// Max +static inline uint8_t tu_max8 (uint8_t x, uint8_t y ) { return (x > y) ? x : y; } +static inline uint16_t tu_max16 (uint16_t x, uint16_t y) { return (x > y) ? x : y; } +static inline uint32_t tu_max32 (uint32_t x, uint32_t y) { return (x > y) ? x : y; } + +// Align +static inline uint32_t tu_align_n(uint32_t value, uint32_t alignment) +{ + return value & ((uint32_t) ~(alignment-1)); +} + +static inline uint32_t tu_align32 (uint32_t value) { return (value & 0xFFFFFFE0UL); } +static inline uint32_t tu_align16 (uint32_t value) { return (value & 0xFFFFFFF0UL); } +static inline uint32_t tu_align4k (uint32_t value) { return (value & 0xFFFFF000UL); } +static inline uint32_t tu_offset4k(uint32_t value) { return (value & 0xFFFUL); } + +//------------- Mathematics -------------// +static inline uint32_t tu_abs(int32_t value) { return (uint32_t)((value < 0) ? (-value) : value); } + +/// inclusive range checking +static inline bool tu_within(uint32_t lower, uint32_t value, uint32_t upper) +{ + return (lower <= value) && (value <= upper); +} + +// log2 of a value is its MSB's position +// TODO use clz TODO remove +static inline uint8_t tu_log2(uint32_t value) +{ + uint8_t result = 0; + + while (value >>= 1) + { + result++; + } + return result; +} + +// Bit +static inline uint32_t tu_bit_set (uint32_t value, uint8_t pos) { return value | TU_BIT(pos); } +static inline uint32_t tu_bit_clear(uint32_t value, uint8_t pos) { return value & (~TU_BIT(pos)); } +static inline bool tu_bit_test (uint32_t value, uint8_t pos) { return (value & TU_BIT(pos)) ? true : false; } + +/*------------------------------------------------------------------*/ +/* Count number of arguments of __VA_ARGS__ + * - reference https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s + * - _GET_NTH_ARG() takes args >= N (64) but only expand to Nth one (64th) + * - _RSEQ_N() is reverse sequential to N to add padding to have + * Nth position is the same as the number of arguments + * - ##__VA_ARGS__ is used to deal with 0 paramerter (swallows comma) + *------------------------------------------------------------------*/ +#ifndef TU_ARGS_NUM + +#define TU_ARGS_NUM(...) NARG_(_0, ##__VA_ARGS__,_RSEQ_N()) + +#define NARG_(...) _GET_NTH_ARG(__VA_ARGS__) +#define _GET_NTH_ARG( \ + _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ + _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ + _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ + _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ + _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ + _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ + _61,_62,_63,N,...) N +#define _RSEQ_N() \ + 62,61,60, \ + 59,58,57,56,55,54,53,52,51,50, \ + 49,48,47,46,45,44,43,42,41,40, \ + 39,38,37,36,35,34,33,32,31,30, \ + 29,28,27,26,25,24,23,22,21,20, \ + 19,18,17,16,15,14,13,12,11,10, \ + 9,8,7,6,5,4,3,2,1,0 +#endif + +// To be removed +//------------- Binary constant -------------// +#if defined(__GNUC__) && !defined(__CC_ARM) + +#define TU_BIN8(x) ((uint8_t) (0b##x)) +#define TU_BIN16(b1, b2) ((uint16_t) (0b##b1##b2)) +#define TU_BIN32(b1, b2, b3, b4) ((uint32_t) (0b##b1##b2##b3##b4)) + +#else + +// internal macro of B8, B16, B32 +#define _B8__(x) (((x&0x0000000FUL)?1:0) \ + +((x&0x000000F0UL)?2:0) \ + +((x&0x00000F00UL)?4:0) \ + +((x&0x0000F000UL)?8:0) \ + +((x&0x000F0000UL)?16:0) \ + +((x&0x00F00000UL)?32:0) \ + +((x&0x0F000000UL)?64:0) \ + +((x&0xF0000000UL)?128:0)) + +#define TU_BIN8(d) ((uint8_t) _B8__(0x##d##UL)) +#define TU_BIN16(dmsb,dlsb) (((uint16_t)TU_BIN8(dmsb)<<8) + TU_BIN8(dlsb)) +#define TU_BIN32(dmsb,db2,db3,dlsb) \ + (((uint32_t)TU_BIN8(dmsb)<<24) \ + + ((uint32_t)TU_BIN8(db2)<<16) \ + + ((uint32_t)TU_BIN8(db3)<<8) \ + + TU_BIN8(dlsb)) +#endif + +//--------------------------------------------------------------------+ +// Debug Function +//--------------------------------------------------------------------+ + +// CFG_TUSB_DEBUG for debugging +// 0 : no debug +// 1 : print when there is error +// 2 : print out log +#if CFG_TUSB_DEBUG + +void tu_print_mem(void const *buf, uint16_t count, uint8_t indent); + +#ifndef tu_printf + #define tu_printf printf +#endif + +// Log with debug level 1 +#define TU_LOG1 tu_printf +#define TU_LOG1_MEM tu_print_mem +#define TU_LOG1_LOCATION() tu_printf("%s: %d:\n", __PRETTY_FUNCTION__, __LINE__) + +// Log with debug level 2 +#if CFG_TUSB_DEBUG > 1 + #define TU_LOG2 TU_LOG1 + #define TU_LOG2_MEM TU_LOG1_MEM + #define TU_LOG2_LOCATION() TU_LOG1_LOCATION() +#endif + + +typedef struct +{ + uint32_t key; + char const * data; +}lookup_entry_t; + +typedef struct +{ + uint16_t count; + lookup_entry_t const* items; +} lookup_table_t; + +static inline char const* lookup_find(lookup_table_t const* p_table, uint32_t key) +{ + for(uint16_t i=0; icount; i++) + { + if (p_table->items[i].key == key) return p_table->items[i].data; + } + + return NULL; +} + +#endif // CFG_TUSB_DEBUG + +#ifndef TU_LOG1 + #define TU_LOG1(...) + #define TU_LOG1_MEM(...) +#endif + +#ifndef TU_LOG2 + #define TU_LOG2(...) + #define TU_LOG2_MEM(...) +#endif + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_COMMON_H_ */ + +/** @} */ diff --git a/src/common/tusb_compiler.h b/src/common/tusb_compiler.h index 0bac3eae7..e403c749b 100644 --- a/src/common/tusb_compiler.h +++ b/src/common/tusb_compiler.h @@ -1,140 +1,140 @@ -/* - * 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_Common - * \defgroup Group_Compiler Compiler - * \brief Group_Compiler brief - * @{ */ - -#ifndef _TUSB_COMPILER_H_ -#define _TUSB_COMPILER_H_ - -#define TU_STRING(x) #x ///< stringify without expand -#define TU_XSTRING(x) TU_STRING(x) ///< expand then stringify -#define TU_STRCAT(a, b) a##b ///< concat without expand -#define TU_XSTRCAT(a, b) TU_STRCAT(a, b) ///< expand then concat - -#if defined __COUNTER__ && __COUNTER__ != __COUNTER__ - #define _TU_COUNTER_ __COUNTER__ -#else - #define _TU_COUNTER_ __LINE__ -#endif - -// Compile-time Assert -#if __STDC_VERSION__ >= 201112L - #define TU_VERIFY_STATIC _Static_assert -#else - #define TU_VERIFY_STATIC(const_expr, _mess) enum { TU_XSTRCAT(_verify_static_, _TU_COUNTER_) = 1/(!!(const_expr)) } -#endif - -// for declaration of reserved field, make use of _TU_COUNTER_ -#define TU_RESERVED TU_XSTRCAT(reserved, _TU_COUNTER_) - -#define TU_LITTLE_ENDIAN (0x12u) -#define TU_BIG_ENDIAN (0x21u) - -//--------------------------------------------------------------------+ -// Compiler porting with Attribute and Endian -//--------------------------------------------------------------------+ -#if defined(__GNUC__) - #define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes))) - #define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name))) - #define TU_ATTR_PACKED __attribute__ ((packed)) - #define TU_ATTR_PREPACKED - #define TU_ATTR_WEAK __attribute__ ((weak)) - #define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used - #define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused - #define TU_ATTR_USED __attribute__ ((used)) // Function/Variable is meant to be used - - // Endian conversion use well-known host to network (big endian) naming - #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - #define TU_BYTE_ORDER TU_LITTLE_ENDIAN - #else - #define TU_BYTE_ORDER TU_BIG_ENDIAN - #endif - - #define TU_BSWAP16(u16) (__builtin_bswap16(u16)) - #define TU_BSWAP32(u32) (__builtin_bswap32(u32)) - -#elif defined(__TI_COMPILER_VERSION__) - #define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes))) - #define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name))) - #define TU_ATTR_PACKED __attribute__ ((packed)) - #define TU_ATTR_PREPACKED - #define TU_ATTR_WEAK __attribute__ ((weak)) - #define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used - #define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused - #define TU_ATTR_USED __attribute__ ((used)) - - // __BYTE_ORDER is defined in the TI ARM compiler, but not MSP430 (which is little endian) - #if ((__BYTE_ORDER__) == (__ORDER_LITTLE_ENDIAN__)) || defined(__MSP430__) - #define TU_BYTE_ORDER TU_LITTLE_ENDIAN - #else - #define TU_BYTE_ORDER TU_BIG_ENDIAN - #endif - - #define TU_BSWAP16(u16) (__builtin_bswap16(u16)) - #define TU_BSWAP32(u32) (__builtin_bswap32(u32)) - -#else - #error "Compiler attribute porting is required" -#endif - -#if (TU_BYTE_ORDER == TU_LITTLE_ENDIAN) - - #define tu_htons(u16) (TU_BSWAP16(u16)) - #define tu_ntohs(u16) (TU_BSWAP16(u16)) - - #define tu_htonl(u32) (TU_BSWAP32(u32)) - #define tu_ntohl(u32) (TU_BSWAP32(u32)) - - #define tu_htole16(u16) (u16) - #define tu_le16toh(u16) (u16) - - #define tu_htole32(u32) (u32) - #define tu_le32toh(u32) (u32) - -#elif (TU_BYTE_ORDER == TU_BIG_ENDIAN) - - #define tu_htons(u16) (u16) - #define tu_ntohs(u16) (u16) - - #define tu_htonl(u32) (u32) - #define tu_ntohl(u32) (u32) - - #define tu_htole16(u16) (tu_bswap16(u16)) - #define tu_le16toh(u16) (tu_bswap16(u16)) - - #define tu_htole32(u32) (tu_bswap32(u32)) - #define tu_le32toh(u32) (tu_bswap32(u32)) - -#else - #error Byte order is undefined -#endif - -#endif /* _TUSB_COMPILER_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_Common + * \defgroup Group_Compiler Compiler + * \brief Group_Compiler brief + * @{ */ + +#ifndef _TUSB_COMPILER_H_ +#define _TUSB_COMPILER_H_ + +#define TU_STRING(x) #x ///< stringify without expand +#define TU_XSTRING(x) TU_STRING(x) ///< expand then stringify +#define TU_STRCAT(a, b) a##b ///< concat without expand +#define TU_XSTRCAT(a, b) TU_STRCAT(a, b) ///< expand then concat + +#if defined __COUNTER__ && __COUNTER__ != __COUNTER__ + #define _TU_COUNTER_ __COUNTER__ +#else + #define _TU_COUNTER_ __LINE__ +#endif + +// Compile-time Assert +#if __STDC_VERSION__ >= 201112L + #define TU_VERIFY_STATIC _Static_assert +#else + #define TU_VERIFY_STATIC(const_expr, _mess) enum { TU_XSTRCAT(_verify_static_, _TU_COUNTER_) = 1/(!!(const_expr)) } +#endif + +// for declaration of reserved field, make use of _TU_COUNTER_ +#define TU_RESERVED TU_XSTRCAT(reserved, _TU_COUNTER_) + +#define TU_LITTLE_ENDIAN (0x12u) +#define TU_BIG_ENDIAN (0x21u) + +//--------------------------------------------------------------------+ +// Compiler porting with Attribute and Endian +//--------------------------------------------------------------------+ +#if defined(__GNUC__) + #define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes))) + #define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name))) + #define TU_ATTR_PACKED __attribute__ ((packed)) + #define TU_ATTR_PREPACKED + #define TU_ATTR_WEAK __attribute__ ((weak)) + #define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used + #define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused + #define TU_ATTR_USED __attribute__ ((used)) // Function/Variable is meant to be used + + // Endian conversion use well-known host to network (big endian) naming + #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + #define TU_BYTE_ORDER TU_LITTLE_ENDIAN + #else + #define TU_BYTE_ORDER TU_BIG_ENDIAN + #endif + + #define TU_BSWAP16(u16) (__builtin_bswap16(u16)) + #define TU_BSWAP32(u32) (__builtin_bswap32(u32)) + +#elif defined(__TI_COMPILER_VERSION__) + #define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes))) + #define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name))) + #define TU_ATTR_PACKED __attribute__ ((packed)) + #define TU_ATTR_PREPACKED + #define TU_ATTR_WEAK __attribute__ ((weak)) + #define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used + #define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused + #define TU_ATTR_USED __attribute__ ((used)) + + // __BYTE_ORDER is defined in the TI ARM compiler, but not MSP430 (which is little endian) + #if ((__BYTE_ORDER__) == (__ORDER_LITTLE_ENDIAN__)) || defined(__MSP430__) + #define TU_BYTE_ORDER TU_LITTLE_ENDIAN + #else + #define TU_BYTE_ORDER TU_BIG_ENDIAN + #endif + + #define TU_BSWAP16(u16) (__builtin_bswap16(u16)) + #define TU_BSWAP32(u32) (__builtin_bswap32(u32)) + +#else + #error "Compiler attribute porting is required" +#endif + +#if (TU_BYTE_ORDER == TU_LITTLE_ENDIAN) + + #define tu_htons(u16) (TU_BSWAP16(u16)) + #define tu_ntohs(u16) (TU_BSWAP16(u16)) + + #define tu_htonl(u32) (TU_BSWAP32(u32)) + #define tu_ntohl(u32) (TU_BSWAP32(u32)) + + #define tu_htole16(u16) (u16) + #define tu_le16toh(u16) (u16) + + #define tu_htole32(u32) (u32) + #define tu_le32toh(u32) (u32) + +#elif (TU_BYTE_ORDER == TU_BIG_ENDIAN) + + #define tu_htons(u16) (u16) + #define tu_ntohs(u16) (u16) + + #define tu_htonl(u32) (u32) + #define tu_ntohl(u32) (u32) + + #define tu_htole16(u16) (tu_bswap16(u16)) + #define tu_le16toh(u16) (tu_bswap16(u16)) + + #define tu_htole32(u32) (tu_bswap32(u32)) + #define tu_le32toh(u32) (tu_bswap32(u32)) + +#else + #error Byte order is undefined +#endif + +#endif /* _TUSB_COMPILER_H_ */ + +/// @} diff --git a/src/common/tusb_error.h b/src/common/tusb_error.h index e6313f7aa..f600c4ae3 100644 --- a/src/common/tusb_error.h +++ b/src/common/tusb_error.h @@ -1,74 +1,74 @@ -/* - * 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_Common - * \defgroup Group_Error Error Codes - * @{ */ - -#ifndef _TUSB_ERRORS_H_ -#define _TUSB_ERRORS_H_ - -#include "tusb_option.h" - -#ifdef __cplusplus - extern "C" { -#endif - -#define ERROR_ENUM(x) x, -#define ERROR_STRING(x) #x, - -#define ERROR_TABLE(ENTRY) \ - ENTRY(TUSB_ERROR_NONE )\ - ENTRY(TUSB_ERROR_INVALID_PARA )\ - ENTRY(TUSB_ERROR_DEVICE_NOT_READY )\ - ENTRY(TUSB_ERROR_INTERFACE_IS_BUSY )\ - ENTRY(TUSB_ERROR_HCD_OPEN_PIPE_FAILED )\ - ENTRY(TUSB_ERROR_OSAL_TIMEOUT )\ - ENTRY(TUSB_ERROR_CDCH_DEVICE_NOT_MOUNTED )\ - ENTRY(TUSB_ERROR_MSCH_DEVICE_NOT_MOUNTED )\ - ENTRY(TUSB_ERROR_NOT_SUPPORTED )\ - ENTRY(TUSB_ERROR_NOT_ENOUGH_MEMORY )\ - ENTRY(TUSB_ERROR_FAILED )\ - -/// \brief Error Code returned -typedef enum -{ - ERROR_TABLE(ERROR_ENUM) - TUSB_ERROR_COUNT -}tusb_error_t; - -#if CFG_TUSB_DEBUG -/// Enum to String for debugging purposes. Only available if \ref CFG_TUSB_DEBUG > 0 -extern char const* const tusb_strerr[TUSB_ERROR_COUNT]; -#endif - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_ERRORS_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_Common + * \defgroup Group_Error Error Codes + * @{ */ + +#ifndef _TUSB_ERRORS_H_ +#define _TUSB_ERRORS_H_ + +#include "tusb_option.h" + +#ifdef __cplusplus + extern "C" { +#endif + +#define ERROR_ENUM(x) x, +#define ERROR_STRING(x) #x, + +#define ERROR_TABLE(ENTRY) \ + ENTRY(TUSB_ERROR_NONE )\ + ENTRY(TUSB_ERROR_INVALID_PARA )\ + ENTRY(TUSB_ERROR_DEVICE_NOT_READY )\ + ENTRY(TUSB_ERROR_INTERFACE_IS_BUSY )\ + ENTRY(TUSB_ERROR_HCD_OPEN_PIPE_FAILED )\ + ENTRY(TUSB_ERROR_OSAL_TIMEOUT )\ + ENTRY(TUSB_ERROR_CDCH_DEVICE_NOT_MOUNTED )\ + ENTRY(TUSB_ERROR_MSCH_DEVICE_NOT_MOUNTED )\ + ENTRY(TUSB_ERROR_NOT_SUPPORTED )\ + ENTRY(TUSB_ERROR_NOT_ENOUGH_MEMORY )\ + ENTRY(TUSB_ERROR_FAILED )\ + +/// \brief Error Code returned +typedef enum +{ + ERROR_TABLE(ERROR_ENUM) + TUSB_ERROR_COUNT +}tusb_error_t; + +#if CFG_TUSB_DEBUG +/// Enum to String for debugging purposes. Only available if \ref CFG_TUSB_DEBUG > 0 +extern char const* const tusb_strerr[TUSB_ERROR_COUNT]; +#endif + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_ERRORS_H_ */ + +/** @} */ diff --git a/src/common/tusb_fifo.c b/src/common/tusb_fifo.c index 455b1ad34..01e990311 100644 --- a/src/common/tusb_fifo.c +++ b/src/common/tusb_fifo.c @@ -1,288 +1,288 @@ -/* - * 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 - -#include "osal/osal.h" -#include "tusb_fifo.h" - -// implement mutex lock and unlock -#if CFG_FIFO_MUTEX - -static void tu_fifo_lock(tu_fifo_t *f) -{ - if (f->mutex) - { - osal_mutex_lock(f->mutex, OSAL_TIMEOUT_WAIT_FOREVER); - } -} - -static void tu_fifo_unlock(tu_fifo_t *f) -{ - if (f->mutex) - { - osal_mutex_unlock(f->mutex); - } -} - -#else - -#define tu_fifo_lock(_ff) -#define tu_fifo_unlock(_ff) - -#endif - -bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable) -{ - tu_fifo_lock(f); - - f->buffer = (uint8_t*) buffer; - f->depth = depth; - f->item_size = item_size; - f->overwritable = overwritable; - - f->rd_idx = f->wr_idx = f->count = 0; - - tu_fifo_unlock(f); - - return true; -} - -// retrieve data from fifo -static void _tu_ff_pull(tu_fifo_t* f, void * buffer) -{ - memcpy(buffer, - f->buffer + (f->rd_idx * f->item_size), - f->item_size); - - f->rd_idx = (f->rd_idx + 1) % f->depth; - f->count--; -} - -// send data to fifo -static void _tu_ff_push(tu_fifo_t* f, void const * data) -{ - memcpy( f->buffer + (f->wr_idx * f->item_size), - data, - f->item_size); - - f->wr_idx = (f->wr_idx + 1) % f->depth; - - if (tu_fifo_full(f)) - { - f->rd_idx = f->wr_idx; // keep the full state (rd == wr && len = size) - } - else - { - f->count++; - } -} - -/******************************************************************************/ -/*! - @brief Read one byte out of the RX buffer. - - This function will return the byte located at the array index of the - read pointer, and then increment the read pointer index. If the read - pointer exceeds the maximum buffer size, it will roll over to zero. - - @param[in] f - Pointer to the FIFO buffer to manipulate - @param[in] buffer - Pointer to the place holder for data read from the buffer - - @returns TRUE if the queue is not empty -*/ -/******************************************************************************/ -bool tu_fifo_read(tu_fifo_t* f, void * buffer) -{ - if( tu_fifo_empty(f) ) return false; - - tu_fifo_lock(f); - - _tu_ff_pull(f, buffer); - - tu_fifo_unlock(f); - - return true; -} - -/******************************************************************************/ -/*! - @brief This function will read n elements into the array index specified by - the write pointer and increment the write index. If the write index - exceeds the max buffer size, then it will roll over to zero. - - @param[in] f - Pointer to the FIFO buffer to manipulate - @param[in] buffer - The pointer to data location - @param[in] count - Number of element that buffer can afford - - @returns number of items read from the FIFO -*/ -/******************************************************************************/ -uint16_t tu_fifo_read_n (tu_fifo_t* f, void * buffer, uint16_t count) -{ - if( tu_fifo_empty(f) ) return 0; - - tu_fifo_lock(f); - - /* Limit up to fifo's count */ - if ( count > f->count ) count = f->count; - - uint8_t* buf8 = (uint8_t*) buffer; - uint16_t len = 0; - - while (len < count) - { - _tu_ff_pull(f, buf8); - - len++; - buf8 += f->item_size; - } - - tu_fifo_unlock(f); - - return len; -} - -/******************************************************************************/ -/*! - @brief Reads one item without removing it from the FIFO - - @param[in] f - Pointer to the FIFO buffer to manipulate - @param[in] pos - Position to read from in the FIFO buffer - @param[in] p_buffer - Pointer to the place holder for data read from the buffer - - @returns TRUE if the queue is not empty -*/ -/******************************************************************************/ -bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t pos, void * p_buffer) -{ - if ( pos >= f->count ) return false; - - // rd_idx is pos=0 - uint16_t index = (f->rd_idx + pos) % f->depth; - memcpy(p_buffer, - f->buffer + (index * f->item_size), - f->item_size); - - return true; -} - -/******************************************************************************/ -/*! - @brief Write one element into the RX buffer. - - This function will write one element into the array index specified by - the write pointer and increment the write index. If the write index - exceeds the max buffer size, then it will roll over to zero. - - @param[in] f - Pointer to the FIFO buffer to manipulate - @param[in] data - The byte to add to the FIFO - - @returns TRUE if the data was written to the FIFO (overwrittable - FIFO will always return TRUE) -*/ -/******************************************************************************/ -bool tu_fifo_write (tu_fifo_t* f, const void * data) -{ - if ( tu_fifo_full(f) && !f->overwritable ) return false; - - tu_fifo_lock(f); - - _tu_ff_push(f, data); - - tu_fifo_unlock(f); - - return true; -} - -/******************************************************************************/ -/*! - @brief This function will write n elements into the array index specified by - the write pointer and increment the write index. If the write index - exceeds the max buffer size, then it will roll over to zero. - - @param[in] f - Pointer to the FIFO buffer to manipulate - @param[in] data - The pointer to data to add to the FIFO - @param[in] count - Number of element - @return Number of written elements -*/ -/******************************************************************************/ -uint16_t tu_fifo_write_n (tu_fifo_t* f, const void * data, uint16_t count) -{ - if ( count == 0 ) return 0; - - tu_fifo_lock(f); - - // Not overwritable limit up to full - if (!f->overwritable) count = tu_min16(count, tu_fifo_remaining(f)); - - uint8_t const* buf8 = (uint8_t const*) data; - uint16_t len = 0; - - while (len < count) - { - _tu_ff_push(f, buf8); - - len++; - buf8 += f->item_size; - } - - tu_fifo_unlock(f); - - return len; -} - -/******************************************************************************/ -/*! - @brief Clear the fifo read and write pointers and set length to zero - - @param[in] f - Pointer to the FIFO buffer to manipulate -*/ -/******************************************************************************/ -bool tu_fifo_clear(tu_fifo_t *f) -{ - tu_fifo_lock(f); - - f->rd_idx = f->wr_idx = f->count = 0; - - tu_fifo_unlock(f); - - return true; -} +/* + * 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 + +#include "osal/osal.h" +#include "tusb_fifo.h" + +// implement mutex lock and unlock +#if CFG_FIFO_MUTEX + +static void tu_fifo_lock(tu_fifo_t *f) +{ + if (f->mutex) + { + osal_mutex_lock(f->mutex, OSAL_TIMEOUT_WAIT_FOREVER); + } +} + +static void tu_fifo_unlock(tu_fifo_t *f) +{ + if (f->mutex) + { + osal_mutex_unlock(f->mutex); + } +} + +#else + +#define tu_fifo_lock(_ff) +#define tu_fifo_unlock(_ff) + +#endif + +bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable) +{ + tu_fifo_lock(f); + + f->buffer = (uint8_t*) buffer; + f->depth = depth; + f->item_size = item_size; + f->overwritable = overwritable; + + f->rd_idx = f->wr_idx = f->count = 0; + + tu_fifo_unlock(f); + + return true; +} + +// retrieve data from fifo +static void _tu_ff_pull(tu_fifo_t* f, void * buffer) +{ + memcpy(buffer, + f->buffer + (f->rd_idx * f->item_size), + f->item_size); + + f->rd_idx = (f->rd_idx + 1) % f->depth; + f->count--; +} + +// send data to fifo +static void _tu_ff_push(tu_fifo_t* f, void const * data) +{ + memcpy( f->buffer + (f->wr_idx * f->item_size), + data, + f->item_size); + + f->wr_idx = (f->wr_idx + 1) % f->depth; + + if (tu_fifo_full(f)) + { + f->rd_idx = f->wr_idx; // keep the full state (rd == wr && len = size) + } + else + { + f->count++; + } +} + +/******************************************************************************/ +/*! + @brief Read one byte out of the RX buffer. + + This function will return the byte located at the array index of the + read pointer, and then increment the read pointer index. If the read + pointer exceeds the maximum buffer size, it will roll over to zero. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] buffer + Pointer to the place holder for data read from the buffer + + @returns TRUE if the queue is not empty +*/ +/******************************************************************************/ +bool tu_fifo_read(tu_fifo_t* f, void * buffer) +{ + if( tu_fifo_empty(f) ) return false; + + tu_fifo_lock(f); + + _tu_ff_pull(f, buffer); + + tu_fifo_unlock(f); + + return true; +} + +/******************************************************************************/ +/*! + @brief This function will read n elements into the array index specified by + the write pointer and increment the write index. If the write index + exceeds the max buffer size, then it will roll over to zero. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] buffer + The pointer to data location + @param[in] count + Number of element that buffer can afford + + @returns number of items read from the FIFO +*/ +/******************************************************************************/ +uint16_t tu_fifo_read_n (tu_fifo_t* f, void * buffer, uint16_t count) +{ + if( tu_fifo_empty(f) ) return 0; + + tu_fifo_lock(f); + + /* Limit up to fifo's count */ + if ( count > f->count ) count = f->count; + + uint8_t* buf8 = (uint8_t*) buffer; + uint16_t len = 0; + + while (len < count) + { + _tu_ff_pull(f, buf8); + + len++; + buf8 += f->item_size; + } + + tu_fifo_unlock(f); + + return len; +} + +/******************************************************************************/ +/*! + @brief Reads one item without removing it from the FIFO + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] pos + Position to read from in the FIFO buffer + @param[in] p_buffer + Pointer to the place holder for data read from the buffer + + @returns TRUE if the queue is not empty +*/ +/******************************************************************************/ +bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t pos, void * p_buffer) +{ + if ( pos >= f->count ) return false; + + // rd_idx is pos=0 + uint16_t index = (f->rd_idx + pos) % f->depth; + memcpy(p_buffer, + f->buffer + (index * f->item_size), + f->item_size); + + return true; +} + +/******************************************************************************/ +/*! + @brief Write one element into the RX buffer. + + This function will write one element into the array index specified by + the write pointer and increment the write index. If the write index + exceeds the max buffer size, then it will roll over to zero. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] data + The byte to add to the FIFO + + @returns TRUE if the data was written to the FIFO (overwrittable + FIFO will always return TRUE) +*/ +/******************************************************************************/ +bool tu_fifo_write (tu_fifo_t* f, const void * data) +{ + if ( tu_fifo_full(f) && !f->overwritable ) return false; + + tu_fifo_lock(f); + + _tu_ff_push(f, data); + + tu_fifo_unlock(f); + + return true; +} + +/******************************************************************************/ +/*! + @brief This function will write n elements into the array index specified by + the write pointer and increment the write index. If the write index + exceeds the max buffer size, then it will roll over to zero. + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] data + The pointer to data to add to the FIFO + @param[in] count + Number of element + @return Number of written elements +*/ +/******************************************************************************/ +uint16_t tu_fifo_write_n (tu_fifo_t* f, const void * data, uint16_t count) +{ + if ( count == 0 ) return 0; + + tu_fifo_lock(f); + + // Not overwritable limit up to full + if (!f->overwritable) count = tu_min16(count, tu_fifo_remaining(f)); + + uint8_t const* buf8 = (uint8_t const*) data; + uint16_t len = 0; + + while (len < count) + { + _tu_ff_push(f, buf8); + + len++; + buf8 += f->item_size; + } + + tu_fifo_unlock(f); + + return len; +} + +/******************************************************************************/ +/*! + @brief Clear the fifo read and write pointers and set length to zero + + @param[in] f + Pointer to the FIFO buffer to manipulate +*/ +/******************************************************************************/ +bool tu_fifo_clear(tu_fifo_t *f) +{ + tu_fifo_lock(f); + + f->rd_idx = f->wr_idx = f->count = 0; + + tu_fifo_unlock(f); + + return true; +} diff --git a/src/common/tusb_fifo.h b/src/common/tusb_fifo.h index 1b64f1dbd..fb0c896f3 100644 --- a/src/common/tusb_fifo.h +++ b/src/common/tusb_fifo.h @@ -1,131 +1,131 @@ -/* - * 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_Common - * \defgroup group_fifo fifo - * @{ */ - -#ifndef _TUSB_FIFO_H_ -#define _TUSB_FIFO_H_ - -// mutex is only needed for RTOS -// for OS None, we don't get preempted -#define CFG_FIFO_MUTEX (CFG_TUSB_OS != OPT_OS_NONE) - -#include -#include - -#ifdef __cplusplus - extern "C" { -#endif - -#if CFG_FIFO_MUTEX -#define tu_fifo_mutex_t osal_mutex_t -#endif - - -/** \struct tu_fifo_t - * \brief Simple Circular FIFO - */ -typedef struct -{ - uint8_t* buffer ; ///< buffer pointer - uint16_t depth ; ///< max items - uint16_t item_size ; ///< size of each item - bool overwritable ; - - volatile uint16_t count ; ///< number of items in queue - volatile uint16_t wr_idx ; ///< write pointer - volatile uint16_t rd_idx ; ///< read pointer - -#if CFG_FIFO_MUTEX - tu_fifo_mutex_t mutex; -#endif - -} tu_fifo_t; - -#define TU_FIFO_DEF(_name, _depth, _type, _overwritable) \ - uint8_t _name##_buf[_depth*sizeof(_type)]; \ - tu_fifo_t _name = { \ - .buffer = _name##_buf, \ - .depth = _depth, \ - .item_size = sizeof(_type), \ - .overwritable = _overwritable, \ - } - -bool tu_fifo_clear(tu_fifo_t *f); -bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable); - -#if CFG_FIFO_MUTEX -static inline void tu_fifo_config_mutex(tu_fifo_t *f, tu_fifo_mutex_t mutex_hdl) -{ - f->mutex = mutex_hdl; -} -#endif - -bool tu_fifo_write (tu_fifo_t* f, void const * p_data); -uint16_t tu_fifo_write_n (tu_fifo_t* f, void const * p_data, uint16_t count); - -bool tu_fifo_read (tu_fifo_t* f, void * p_buffer); -uint16_t tu_fifo_read_n (tu_fifo_t* f, void * p_buffer, uint16_t count); - -bool tu_fifo_peek_at (tu_fifo_t* f, uint16_t pos, void * p_buffer); - -static inline bool tu_fifo_peek(tu_fifo_t* f, void * p_buffer) -{ - return tu_fifo_peek_at(f, 0, p_buffer); -} - -static inline bool tu_fifo_empty(tu_fifo_t* f) -{ - return (f->count == 0); -} - -static inline bool tu_fifo_full(tu_fifo_t* f) -{ - return (f->count == f->depth); -} - -static inline uint16_t tu_fifo_count(tu_fifo_t* f) -{ - return f->count; -} - -static inline uint16_t tu_fifo_remaining(tu_fifo_t* f) -{ - return f->depth - f->count; -} - -static inline uint16_t tu_fifo_depth(tu_fifo_t* f) -{ - return f->depth; -} - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_FIFO_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_Common + * \defgroup group_fifo fifo + * @{ */ + +#ifndef _TUSB_FIFO_H_ +#define _TUSB_FIFO_H_ + +// mutex is only needed for RTOS +// for OS None, we don't get preempted +#define CFG_FIFO_MUTEX (CFG_TUSB_OS != OPT_OS_NONE) + +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif + +#if CFG_FIFO_MUTEX +#define tu_fifo_mutex_t osal_mutex_t +#endif + + +/** \struct tu_fifo_t + * \brief Simple Circular FIFO + */ +typedef struct +{ + uint8_t* buffer ; ///< buffer pointer + uint16_t depth ; ///< max items + uint16_t item_size ; ///< size of each item + bool overwritable ; + + volatile uint16_t count ; ///< number of items in queue + volatile uint16_t wr_idx ; ///< write pointer + volatile uint16_t rd_idx ; ///< read pointer + +#if CFG_FIFO_MUTEX + tu_fifo_mutex_t mutex; +#endif + +} tu_fifo_t; + +#define TU_FIFO_DEF(_name, _depth, _type, _overwritable) \ + uint8_t _name##_buf[_depth*sizeof(_type)]; \ + tu_fifo_t _name = { \ + .buffer = _name##_buf, \ + .depth = _depth, \ + .item_size = sizeof(_type), \ + .overwritable = _overwritable, \ + } + +bool tu_fifo_clear(tu_fifo_t *f); +bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable); + +#if CFG_FIFO_MUTEX +static inline void tu_fifo_config_mutex(tu_fifo_t *f, tu_fifo_mutex_t mutex_hdl) +{ + f->mutex = mutex_hdl; +} +#endif + +bool tu_fifo_write (tu_fifo_t* f, void const * p_data); +uint16_t tu_fifo_write_n (tu_fifo_t* f, void const * p_data, uint16_t count); + +bool tu_fifo_read (tu_fifo_t* f, void * p_buffer); +uint16_t tu_fifo_read_n (tu_fifo_t* f, void * p_buffer, uint16_t count); + +bool tu_fifo_peek_at (tu_fifo_t* f, uint16_t pos, void * p_buffer); + +static inline bool tu_fifo_peek(tu_fifo_t* f, void * p_buffer) +{ + return tu_fifo_peek_at(f, 0, p_buffer); +} + +static inline bool tu_fifo_empty(tu_fifo_t* f) +{ + return (f->count == 0); +} + +static inline bool tu_fifo_full(tu_fifo_t* f) +{ + return (f->count == f->depth); +} + +static inline uint16_t tu_fifo_count(tu_fifo_t* f) +{ + return f->count; +} + +static inline uint16_t tu_fifo_remaining(tu_fifo_t* f) +{ + return f->depth - f->count; +} + +static inline uint16_t tu_fifo_depth(tu_fifo_t* f) +{ + return f->depth; +} + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_FIFO_H_ */ diff --git a/src/common/tusb_timeout.h b/src/common/tusb_timeout.h index 558742ff6..ce53955f0 100644 --- a/src/common/tusb_timeout.h +++ b/src/common/tusb_timeout.h @@ -1,80 +1,80 @@ -/* - * 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_Common Common Files - * \defgroup Group_TimeoutTimer timeout timer - * @{ */ - -#ifndef _TUSB_TIMEOUT_H_ -#define _TUSB_TIMEOUT_H_ - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - uint32_t start; - uint32_t interval; -}tu_timeout_t; - -#if 0 - -extern uint32_t tusb_hal_millis(void); - -static inline void tu_timeout_set(tu_timeout_t* tt, uint32_t msec) -{ - tt->interval = msec; - tt->start = tusb_hal_millis(); -} - -static inline bool tu_timeout_expired(tu_timeout_t* tt) -{ - return ( tusb_hal_millis() - tt->start ) >= tt->interval; -} - -// For used with periodic event to prevent drift -static inline void tu_timeout_reset(tu_timeout_t* tt) -{ - tt->start += tt->interval; -} - -static inline void tu_timeout_restart(tu_timeout_t* tt) -{ - tt->start = tusb_hal_millis(); -} - -#endif - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_TIMEOUT_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_Common Common Files + * \defgroup Group_TimeoutTimer timeout timer + * @{ */ + +#ifndef _TUSB_TIMEOUT_H_ +#define _TUSB_TIMEOUT_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint32_t start; + uint32_t interval; +}tu_timeout_t; + +#if 0 + +extern uint32_t tusb_hal_millis(void); + +static inline void tu_timeout_set(tu_timeout_t* tt, uint32_t msec) +{ + tt->interval = msec; + tt->start = tusb_hal_millis(); +} + +static inline bool tu_timeout_expired(tu_timeout_t* tt) +{ + return ( tusb_hal_millis() - tt->start ) >= tt->interval; +} + +// For used with periodic event to prevent drift +static inline void tu_timeout_reset(tu_timeout_t* tt) +{ + tt->start += tt->interval; +} + +static inline void tu_timeout_restart(tu_timeout_t* tt) +{ + tt->start = tusb_hal_millis(); +} + +#endif + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_TIMEOUT_H_ */ + +/** @} */ diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h index 737b17f76..c37c19bad 100644 --- a/src/common/tusb_types.h +++ b/src/common/tusb_types.h @@ -1,481 +1,481 @@ -/* - * 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_usb_definitions - * \defgroup USBDef_Type USB Types - * @{ */ - -#ifndef _TUSB_TYPES_H_ -#define _TUSB_TYPES_H_ - -#include -#include -#include "tusb_compiler.h" - -#ifdef __cplusplus - extern "C" { -#endif - -/*------------------------------------------------------------------*/ -/* CONSTANTS - *------------------------------------------------------------------*/ - -/// defined base on EHCI specs value for Endpoint Speed -typedef enum -{ - TUSB_SPEED_FULL = 0, - TUSB_SPEED_LOW , - TUSB_SPEED_HIGH -}tusb_speed_t; - -/// defined base on USB Specs Endpoint's bmAttributes -typedef enum -{ - TUSB_XFER_CONTROL = 0 , - TUSB_XFER_ISOCHRONOUS , - TUSB_XFER_BULK , - TUSB_XFER_INTERRUPT -}tusb_xfer_type_t; - -typedef enum -{ - TUSB_DIR_OUT = 0, - TUSB_DIR_IN = 1, - - TUSB_DIR_IN_MASK = 0x80 -}tusb_dir_t; - -/// USB Descriptor Types -typedef enum -{ - TUSB_DESC_DEVICE = 0x01, - TUSB_DESC_CONFIGURATION = 0x02, - TUSB_DESC_STRING = 0x03, - TUSB_DESC_INTERFACE = 0x04, - TUSB_DESC_ENDPOINT = 0x05, - TUSB_DESC_DEVICE_QUALIFIER = 0x06, - TUSB_DESC_OTHER_SPEED_CONFIG = 0x07, - TUSB_DESC_INTERFACE_POWER = 0x08, - TUSB_DESC_OTG = 0x09, - TUSB_DESC_DEBUG = 0x0A, - TUSB_DESC_INTERFACE_ASSOCIATION = 0x0B, - - TUSB_DESC_BOS = 0x0F, - TUSB_DESC_DEVICE_CAPABILITY = 0x10, - - TUSB_DESC_FUNCTIONAL = 0x21, - - // Class Specific Descriptor - TUSB_DESC_CS_DEVICE = 0x21, - TUSB_DESC_CS_CONFIGURATION = 0x22, - TUSB_DESC_CS_STRING = 0x23, - TUSB_DESC_CS_INTERFACE = 0x24, - TUSB_DESC_CS_ENDPOINT = 0x25, - - TUSB_DESC_SUPERSPEED_ENDPOINT_COMPANION = 0x30, - TUSB_DESC_SUPERSPEED_ISO_ENDPOINT_COMPANION = 0x31 -}tusb_desc_type_t; - -typedef enum -{ - TUSB_REQ_GET_STATUS = 0 , - TUSB_REQ_CLEAR_FEATURE = 1 , - TUSB_REQ_RESERVED = 2 , - TUSB_REQ_SET_FEATURE = 3 , - TUSB_REQ_RESERVED2 = 4 , - TUSB_REQ_SET_ADDRESS = 5 , - TUSB_REQ_GET_DESCRIPTOR = 6 , - TUSB_REQ_SET_DESCRIPTOR = 7 , - TUSB_REQ_GET_CONFIGURATION = 8 , - TUSB_REQ_SET_CONFIGURATION = 9 , - TUSB_REQ_GET_INTERFACE = 10 , - TUSB_REQ_SET_INTERFACE = 11 , - TUSB_REQ_SYNCH_FRAME = 12 -}tusb_request_code_t; - -typedef enum -{ - TUSB_REQ_FEATURE_EDPT_HALT = 0, - TUSB_REQ_FEATURE_REMOTE_WAKEUP = 1, - TUSB_REQ_FEATURE_TEST_MODE = 2 -}tusb_request_feature_selector_t; - -typedef enum -{ - TUSB_REQ_TYPE_STANDARD = 0, - TUSB_REQ_TYPE_CLASS, - TUSB_REQ_TYPE_VENDOR, - TUSB_REQ_TYPE_INVALID -} tusb_request_type_t; - -typedef enum -{ - TUSB_REQ_RCPT_DEVICE =0, - TUSB_REQ_RCPT_INTERFACE, - TUSB_REQ_RCPT_ENDPOINT, - TUSB_REQ_RCPT_OTHER -} tusb_request_recipient_t; - -typedef enum -{ - TUSB_CLASS_UNSPECIFIED = 0 , - TUSB_CLASS_AUDIO = 1 , - TUSB_CLASS_CDC = 2 , - TUSB_CLASS_HID = 3 , - TUSB_CLASS_RESERVED_4 = 4 , - TUSB_CLASS_PHYSICAL = 5 , - TUSB_CLASS_IMAGE = 6 , - TUSB_CLASS_PRINTER = 7 , - TUSB_CLASS_MSC = 8 , - TUSB_CLASS_HUB = 9 , - TUSB_CLASS_CDC_DATA = 10 , - TUSB_CLASS_SMART_CARD = 11 , - TUSB_CLASS_RESERVED_12 = 12 , - TUSB_CLASS_CONTENT_SECURITY = 13 , - TUSB_CLASS_VIDEO = 14 , - TUSB_CLASS_PERSONAL_HEALTHCARE = 15 , - TUSB_CLASS_AUDIO_VIDEO = 16 , - - TUSB_CLASS_DIAGNOSTIC = 0xDC , - TUSB_CLASS_WIRELESS_CONTROLLER = 0xE0 , - TUSB_CLASS_MISC = 0xEF , - TUSB_CLASS_APPLICATION_SPECIFIC = 0xFE , - TUSB_CLASS_VENDOR_SPECIFIC = 0xFF -}tusb_class_code_t; - -typedef enum -{ - MISC_SUBCLASS_COMMON = 2 -}misc_subclass_type_t; - -typedef enum -{ - MISC_PROTOCOL_IAD = 1 -}misc_protocol_type_t; - -typedef enum -{ - DEVICE_CAPABILITY_WIRELESS_USB = 0x01, - DEVICE_CAPABILITY_USB20_EXTENSION = 0x02, - DEVICE_CAPABILITY_SUPERSPEED_USB = 0x03, - DEVICE_CAPABILITY_CONTAINER_id = 0x04, - DEVICE_CAPABILITY_PLATFORM = 0x05, - DEVICE_CAPABILITY_POWER_DELIVERY = 0x06, - DEVICE_CAPABILITY_BATTERY_INFO = 0x07, - DEVICE_CAPABILITY_PD_CONSUMER_PORT = 0x08, - DEVICE_CAPABILITY_PD_PROVIDER_PORT = 0x09, - DEVICE_CAPABILITY_SUPERSPEED_PLUS = 0x0A, - DEVICE_CAPABILITY_PRECESION_TIME_MEASUREMENT = 0x0B, - DEVICE_CAPABILITY_WIRELESS_USB_EXT = 0x0C, - DEVICE_CAPABILITY_BILLBOARD = 0x0D, - DEVICE_CAPABILITY_AUTHENTICATION = 0x0E, - DEVICE_CAPABILITY_BILLBOARD_EX = 0x0F, - DEVICE_CAPABILITY_CONFIGURATION_SUMMARY = 0x10 -}device_capability_type_t; - -enum { - TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP = TU_BIT(5), - TUSB_DESC_CONFIG_ATT_SELF_POWERED = TU_BIT(6), -}; - -#define TUSB_DESC_CONFIG_POWER_MA(x) ((x)/2) - -/// Device State TODO remove -typedef enum -{ - TUSB_DEVICE_STATE_UNPLUG = 0 , - TUSB_DEVICE_STATE_CONFIGURED , - TUSB_DEVICE_STATE_SUSPENDED , -}tusb_device_state_t; - -typedef enum -{ - XFER_RESULT_SUCCESS, - XFER_RESULT_FAILED, - XFER_RESULT_STALLED, -}xfer_result_t; - -enum // TODO remove -{ - DESC_OFFSET_LEN = 0, - DESC_OFFSET_TYPE = 1 -}; - -enum -{ - INTERFACE_INVALID_NUMBER = 0xff -}; - - -typedef enum -{ - MS_OS_20_SET_HEADER_DESCRIPTOR = 0x00, - MS_OS_20_SUBSET_HEADER_CONFIGURATION = 0x01, - MS_OS_20_SUBSET_HEADER_FUNCTION = 0x02, - MS_OS_20_FEATURE_COMPATBLE_ID = 0x03, - MS_OS_20_FEATURE_REG_PROPERTY = 0x04, - MS_OS_20_FEATURE_MIN_RESUME_TIME = 0x05, - MS_OS_20_FEATURE_MODEL_ID = 0x06, - MS_OS_20_FEATURE_CCGP_DEVICE = 0x07, - MS_OS_20_FEATURE_VENDOR_REVISION = 0x08 -} microsoft_os_20_type_t; - -//--------------------------------------------------------------------+ -// USB Descriptors -//--------------------------------------------------------------------+ - -/// USB Device Descriptor -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength ; ///< Size of this descriptor in bytes. - uint8_t bDescriptorType ; ///< DEVICE Descriptor Type. - uint16_t bcdUSB ; ///< BUSB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H). This field identifies the release of the USB Specification with which the device and its descriptors are compliant. - - uint8_t bDeviceClass ; ///< Class code (assigned by the USB-IF). \li If this field is reset to zero, each interface within a configuration specifies its own class information and the various interfaces operate independently. \li If this field is set to a value between 1 and FEH, the device supports different class specifications on different interfaces and the interfaces may not operate independently. This value identifies the class definition used for the aggregate interfaces. \li If this field is set to FFH, the device class is vendor-specific. - uint8_t bDeviceSubClass ; ///< Subclass code (assigned by the USB-IF). These codes are qualified by the value of the bDeviceClass field. \li If the bDeviceClass field is reset to zero, this field must also be reset to zero. \li If the bDeviceClass field is not set to FFH, all values are reserved for assignment by the USB-IF. - uint8_t bDeviceProtocol ; ///< Protocol code (assigned by the USB-IF). These codes are qualified by the value of the bDeviceClass and the bDeviceSubClass fields. If a device supports class-specific protocols on a device basis as opposed to an interface basis, this code identifies the protocols that the device uses as defined by the specification of the device class. \li If this field is reset to zero, the device does not use class-specific protocols on a device basis. However, it may use classspecific protocols on an interface basis. \li If this field is set to FFH, the device uses a vendor-specific protocol on a device basis. - uint8_t bMaxPacketSize0 ; ///< Maximum packet size for endpoint zero (only 8, 16, 32, or 64 are valid). For HS devices is fixed to 64. - - uint16_t idVendor ; ///< Vendor ID (assigned by the USB-IF). - uint16_t idProduct ; ///< Product ID (assigned by the manufacturer). - uint16_t bcdDevice ; ///< Device release number in binary-coded decimal. - uint8_t iManufacturer ; ///< Index of string descriptor describing manufacturer. - uint8_t iProduct ; ///< Index of string descriptor describing product. - uint8_t iSerialNumber ; ///< Index of string descriptor describing the device's serial number. - - uint8_t bNumConfigurations ; ///< Number of possible configurations. -} tusb_desc_device_t; - -// USB Binary Device Object Store (BOS) Descriptor -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength ; ///< Size of this descriptor in bytes - uint8_t bDescriptorType ; ///< CONFIGURATION Descriptor Type - uint16_t wTotalLength ; ///< Total length of data returned for this descriptor - uint8_t bNumDeviceCaps ; ///< Number of device capability descriptors in the BOS -} tusb_desc_bos_t; - -/// USB Configuration Descriptor -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength ; ///< Size of this descriptor in bytes - uint8_t bDescriptorType ; ///< CONFIGURATION Descriptor Type - uint16_t wTotalLength ; ///< Total length of data returned for this configuration. Includes the combined length of all descriptors (configuration, interface, endpoint, and class- or vendor-specific) returned for this configuration. - - uint8_t bNumInterfaces ; ///< Number of interfaces supported by this configuration - uint8_t bConfigurationValue ; ///< Value to use as an argument to the SetConfiguration() request to select this configuration. - uint8_t iConfiguration ; ///< Index of string descriptor describing this configuration - uint8_t bmAttributes ; ///< Configuration characteristics \n D7: Reserved (set to one)\n D6: Self-powered \n D5: Remote Wakeup \n D4...0: Reserved (reset to zero) \n D7 is reserved and must be set to one for historical reasons. \n A device configuration that uses power from the bus and a local source reports a non-zero value in bMaxPower to indicate the amount of bus power required and sets D6. The actual power source at runtime may be determined using the GetStatus(DEVICE) request (see USB 2.0 spec Section 9.4.5). \n If a device configuration supports remote wakeup, D5 is set to one. - uint8_t bMaxPower ; ///< Maximum power consumption of the USB device from the bus in this specific configuration when the device is fully operational. Expressed in 2 mA units (i.e., 50 = 100 mA). -} tusb_desc_configuration_t; - -/// USB Interface Descriptor -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength ; ///< Size of this descriptor in bytes - uint8_t bDescriptorType ; ///< INTERFACE Descriptor Type - - uint8_t bInterfaceNumber ; ///< Number of this interface. Zero-based value identifying the index in the array of concurrent interfaces supported by this configuration. - uint8_t bAlternateSetting ; ///< Value used to select this alternate setting for the interface identified in the prior field - uint8_t bNumEndpoints ; ///< Number of endpoints used by this interface (excluding endpoint zero). If this value is zero, this interface only uses the Default Control Pipe. - uint8_t bInterfaceClass ; ///< Class code (assigned by the USB-IF). \li A value of zero is reserved for future standardization. \li If this field is set to FFH, the interface class is vendor-specific. \li All other values are reserved for assignment by the USB-IF. - uint8_t bInterfaceSubClass ; ///< Subclass code (assigned by the USB-IF). \n These codes are qualified by the value of the bInterfaceClass field. \li If the bInterfaceClass field is reset to zero, this field must also be reset to zero. \li If the bInterfaceClass field is not set to FFH, all values are reserved for assignment by the USB-IF. - uint8_t bInterfaceProtocol ; ///< Protocol code (assigned by the USB). \n These codes are qualified by the value of the bInterfaceClass and the bInterfaceSubClass fields. If an interface supports class-specific requests, this code identifies the protocols that the device uses as defined by the specification of the device class. \li If this field is reset to zero, the device does not use a class-specific protocol on this interface. \li If this field is set to FFH, the device uses a vendor-specific protocol for this interface. - uint8_t iInterface ; ///< Index of string descriptor describing this interface -} tusb_desc_interface_t; - -/// USB Endpoint Descriptor -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength ; ///< Size of this descriptor in bytes - uint8_t bDescriptorType ; ///< ENDPOINT Descriptor Type - - uint8_t bEndpointAddress ; ///< The address of the endpoint on the USB device described by this descriptor. The address is encoded as follows: \n Bit 3...0: The endpoint number \n Bit 6...4: Reserved, reset to zero \n Bit 7: Direction, ignored for control endpoints 0 = OUT endpoint 1 = IN endpoint. - - struct TU_ATTR_PACKED { - uint8_t xfer : 2; - uint8_t sync : 2; - uint8_t usage : 2; - uint8_t : 2; - } bmAttributes ; ///< This field describes the endpoint's attributes when it is configured using the bConfigurationValue. \n Bits 1..0: Transfer Type \n- 00 = Control \n- 01 = Isochronous \n- 10 = Bulk \n- 11 = Interrupt \n If not an isochronous endpoint, bits 5..2 are reserved and must be set to zero. If isochronous, they are defined as follows: \n Bits 3..2: Synchronization Type \n- 00 = No Synchronization \n- 01 = Asynchronous \n- 10 = Adaptive \n- 11 = Synchronous \n Bits 5..4: Usage Type \n- 00 = Data endpoint \n- 01 = Feedback endpoint \n- 10 = Implicit feedback Data endpoint \n- 11 = Reserved \n Refer to Chapter 5 of USB 2.0 specification for more information. \n All other bits are reserved and must be reset to zero. Reserved bits must be ignored by the host. - - struct TU_ATTR_PACKED { - uint16_t size : 11; ///< Maximum packet size this endpoint is capable of sending or receiving when this configuration is selected. \n For isochronous endpoints, this value is used to reserve the bus time in the schedule, required for the per-(micro)frame data payloads. The pipe may, on an ongoing basis, actually use less bandwidth than that reserved. The device reports, if necessary, the actual bandwidth used via its normal, non-USB defined mechanisms. \n For all endpoints, bits 10..0 specify the maximum packet size (in bytes). \n For high-speed isochronous and interrupt endpoints: \n Bits 12..11 specify the number of additional transaction opportunities per microframe: \n- 00 = None (1 transaction per microframe) \n- 01 = 1 additional (2 per microframe) \n- 10 = 2 additional (3 per microframe) \n- 11 = Reserved \n Bits 15..13 are reserved and must be set to zero. - uint16_t hs_period_mult : 2; - uint16_t : 0; - }wMaxPacketSize; - - uint8_t bInterval ; ///< Interval for polling endpoint for data transfers. Expressed in frames or microframes depending on the device operating speed (i.e., either 1 millisecond or 125 us units). \n- For full-/high-speed isochronous endpoints, this value must be in the range from 1 to 16. The bInterval value is used as the exponent for a \f$ 2^(bInterval-1) \f$ value; e.g., a bInterval of 4 means a period of 8 (\f$ 2^(4-1) \f$). \n- For full-/low-speed interrupt endpoints, the value of this field may be from 1 to 255. \n- For high-speed interrupt endpoints, the bInterval value is used as the exponent for a \f$ 2^(bInterval-1) \f$ value; e.g., a bInterval of 4 means a period of 8 (\f$ 2^(4-1) \f$) . This value must be from 1 to 16. \n- For high-speed bulk/control OUT endpoints, the bInterval must specify the maximum NAK rate of the endpoint. A value of 0 indicates the endpoint never NAKs. Other values indicate at most 1 NAK each bInterval number of microframes. This value must be in the range from 0 to 255. \n Refer to Chapter 5 of USB 2.0 specification for more information. -} tusb_desc_endpoint_t; - -/// USB Other Speed Configuration Descriptor -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength ; ///< Size of descriptor - uint8_t bDescriptorType ; ///< Other_speed_Configuration Type - uint16_t wTotalLength ; ///< Total length of data returned - - uint8_t bNumInterfaces ; ///< Number of interfaces supported by this speed configuration - uint8_t bConfigurationValue ; ///< Value to use to select configuration - uint8_t IConfiguration ; ///< Index of string descriptor - uint8_t bmAttributes ; ///< Same as Configuration descriptor - uint8_t bMaxPower ; ///< Same as Configuration descriptor -} tusb_desc_other_speed_t; - -/// USB Device Qualifier Descriptor -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength ; ///< Size of descriptor - uint8_t bDescriptorType ; ///< Device Qualifier Type - uint16_t bcdUSB ; ///< USB specification version number (e.g., 0200H for V2.00) - - uint8_t bDeviceClass ; ///< Class Code - uint8_t bDeviceSubClass ; ///< SubClass Code - uint8_t bDeviceProtocol ; ///< Protocol Code - uint8_t bMaxPacketSize0 ; ///< Maximum packet size for other speed - uint8_t bNumConfigurations ; ///< Number of Other-speed Configurations - uint8_t bReserved ; ///< Reserved for future use, must be zero -} tusb_desc_device_qualifier_t; - -/// USB Interface Association Descriptor (IAD ECN) -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength ; ///< Size of descriptor - uint8_t bDescriptorType ; ///< Other_speed_Configuration Type - - uint8_t bFirstInterface ; ///< Index of the first associated interface. - uint8_t bInterfaceCount ; ///< Total number of associated interfaces. - - uint8_t bFunctionClass ; ///< Interface class ID. - uint8_t bFunctionSubClass ; ///< Interface subclass ID. - uint8_t bFunctionProtocol ; ///< Interface protocol ID. - - uint8_t iFunction ; ///< Index of the string descriptor describing the interface association. -} tusb_desc_interface_assoc_t; - -// USB String Descriptor -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength ; ///< Size of this descriptor in bytes - uint8_t bDescriptorType ; ///< Descriptor Type - uint16_t unicode_string[]; -} tusb_desc_string_t; - -// USB Binary Device Object Store (BOS) -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength; - uint8_t bDescriptorType ; - uint8_t bDevCapabilityType; - uint8_t bReserved; - uint8_t PlatformCapabilityUUID[16]; - uint8_t CapabilityData[]; -} tusb_desc_bos_platform_t; - -// USB WebuSB URL Descriptor -typedef struct TU_ATTR_PACKED -{ - uint8_t bLength; - uint8_t bDescriptorType; - uint8_t bScheme; - char url[]; -} tusb_desc_webusb_url_t; - -/*------------------------------------------------------------------*/ -/* Types - *------------------------------------------------------------------*/ -typedef struct TU_ATTR_PACKED{ - union { - struct TU_ATTR_PACKED { - uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t. - uint8_t type : 2; ///< Request type tusb_request_type_t. - uint8_t direction : 1; ///< Direction type. tusb_dir_t - } bmRequestType_bit; - - uint8_t bmRequestType; - }; - - uint8_t bRequest; - uint16_t wValue; - uint16_t wIndex; - uint16_t wLength; -} tusb_control_request_t; - -TU_VERIFY_STATIC( sizeof(tusb_control_request_t) == 8, "mostly compiler option issue"); - -// TODO move to somewhere suitable -static inline uint8_t bm_request_type(uint8_t direction, uint8_t type, uint8_t recipient) -{ - return ((uint8_t) (direction << 7)) | ((uint8_t) (type << 5)) | (recipient); -} - -//--------------------------------------------------------------------+ -// Endpoint helper -//--------------------------------------------------------------------+ - -// Get direction from Endpoint address -static inline tusb_dir_t tu_edpt_dir(uint8_t addr) -{ - return (addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT; -} - -// Get Endpoint number from address -static inline uint8_t tu_edpt_number(uint8_t addr) -{ - return (uint8_t)(addr & (~TUSB_DIR_IN_MASK)); -} - -static inline uint8_t tu_edpt_addr(uint8_t num, uint8_t dir) -{ - return (uint8_t)(num | (dir ? TUSB_DIR_IN_MASK : 0)); -} - -//--------------------------------------------------------------------+ -// Descriptor helper -//--------------------------------------------------------------------+ -static inline uint8_t const * tu_desc_next(void const* desc) -{ - uint8_t const* desc8 = (uint8_t const*) desc; - return desc8 + desc8[DESC_OFFSET_LEN]; -} - -static inline uint8_t tu_desc_type(void const* desc) -{ - return ((uint8_t const*) desc)[DESC_OFFSET_TYPE]; -} - -static inline uint8_t tu_desc_len(void const* desc) -{ - return ((uint8_t const*) desc)[DESC_OFFSET_LEN]; -} - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_TYPES_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_usb_definitions + * \defgroup USBDef_Type USB Types + * @{ */ + +#ifndef _TUSB_TYPES_H_ +#define _TUSB_TYPES_H_ + +#include +#include +#include "tusb_compiler.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/*------------------------------------------------------------------*/ +/* CONSTANTS + *------------------------------------------------------------------*/ + +/// defined base on EHCI specs value for Endpoint Speed +typedef enum +{ + TUSB_SPEED_FULL = 0, + TUSB_SPEED_LOW , + TUSB_SPEED_HIGH +}tusb_speed_t; + +/// defined base on USB Specs Endpoint's bmAttributes +typedef enum +{ + TUSB_XFER_CONTROL = 0 , + TUSB_XFER_ISOCHRONOUS , + TUSB_XFER_BULK , + TUSB_XFER_INTERRUPT +}tusb_xfer_type_t; + +typedef enum +{ + TUSB_DIR_OUT = 0, + TUSB_DIR_IN = 1, + + TUSB_DIR_IN_MASK = 0x80 +}tusb_dir_t; + +/// USB Descriptor Types +typedef enum +{ + TUSB_DESC_DEVICE = 0x01, + TUSB_DESC_CONFIGURATION = 0x02, + TUSB_DESC_STRING = 0x03, + TUSB_DESC_INTERFACE = 0x04, + TUSB_DESC_ENDPOINT = 0x05, + TUSB_DESC_DEVICE_QUALIFIER = 0x06, + TUSB_DESC_OTHER_SPEED_CONFIG = 0x07, + TUSB_DESC_INTERFACE_POWER = 0x08, + TUSB_DESC_OTG = 0x09, + TUSB_DESC_DEBUG = 0x0A, + TUSB_DESC_INTERFACE_ASSOCIATION = 0x0B, + + TUSB_DESC_BOS = 0x0F, + TUSB_DESC_DEVICE_CAPABILITY = 0x10, + + TUSB_DESC_FUNCTIONAL = 0x21, + + // Class Specific Descriptor + TUSB_DESC_CS_DEVICE = 0x21, + TUSB_DESC_CS_CONFIGURATION = 0x22, + TUSB_DESC_CS_STRING = 0x23, + TUSB_DESC_CS_INTERFACE = 0x24, + TUSB_DESC_CS_ENDPOINT = 0x25, + + TUSB_DESC_SUPERSPEED_ENDPOINT_COMPANION = 0x30, + TUSB_DESC_SUPERSPEED_ISO_ENDPOINT_COMPANION = 0x31 +}tusb_desc_type_t; + +typedef enum +{ + TUSB_REQ_GET_STATUS = 0 , + TUSB_REQ_CLEAR_FEATURE = 1 , + TUSB_REQ_RESERVED = 2 , + TUSB_REQ_SET_FEATURE = 3 , + TUSB_REQ_RESERVED2 = 4 , + TUSB_REQ_SET_ADDRESS = 5 , + TUSB_REQ_GET_DESCRIPTOR = 6 , + TUSB_REQ_SET_DESCRIPTOR = 7 , + TUSB_REQ_GET_CONFIGURATION = 8 , + TUSB_REQ_SET_CONFIGURATION = 9 , + TUSB_REQ_GET_INTERFACE = 10 , + TUSB_REQ_SET_INTERFACE = 11 , + TUSB_REQ_SYNCH_FRAME = 12 +}tusb_request_code_t; + +typedef enum +{ + TUSB_REQ_FEATURE_EDPT_HALT = 0, + TUSB_REQ_FEATURE_REMOTE_WAKEUP = 1, + TUSB_REQ_FEATURE_TEST_MODE = 2 +}tusb_request_feature_selector_t; + +typedef enum +{ + TUSB_REQ_TYPE_STANDARD = 0, + TUSB_REQ_TYPE_CLASS, + TUSB_REQ_TYPE_VENDOR, + TUSB_REQ_TYPE_INVALID +} tusb_request_type_t; + +typedef enum +{ + TUSB_REQ_RCPT_DEVICE =0, + TUSB_REQ_RCPT_INTERFACE, + TUSB_REQ_RCPT_ENDPOINT, + TUSB_REQ_RCPT_OTHER +} tusb_request_recipient_t; + +typedef enum +{ + TUSB_CLASS_UNSPECIFIED = 0 , + TUSB_CLASS_AUDIO = 1 , + TUSB_CLASS_CDC = 2 , + TUSB_CLASS_HID = 3 , + TUSB_CLASS_RESERVED_4 = 4 , + TUSB_CLASS_PHYSICAL = 5 , + TUSB_CLASS_IMAGE = 6 , + TUSB_CLASS_PRINTER = 7 , + TUSB_CLASS_MSC = 8 , + TUSB_CLASS_HUB = 9 , + TUSB_CLASS_CDC_DATA = 10 , + TUSB_CLASS_SMART_CARD = 11 , + TUSB_CLASS_RESERVED_12 = 12 , + TUSB_CLASS_CONTENT_SECURITY = 13 , + TUSB_CLASS_VIDEO = 14 , + TUSB_CLASS_PERSONAL_HEALTHCARE = 15 , + TUSB_CLASS_AUDIO_VIDEO = 16 , + + TUSB_CLASS_DIAGNOSTIC = 0xDC , + TUSB_CLASS_WIRELESS_CONTROLLER = 0xE0 , + TUSB_CLASS_MISC = 0xEF , + TUSB_CLASS_APPLICATION_SPECIFIC = 0xFE , + TUSB_CLASS_VENDOR_SPECIFIC = 0xFF +}tusb_class_code_t; + +typedef enum +{ + MISC_SUBCLASS_COMMON = 2 +}misc_subclass_type_t; + +typedef enum +{ + MISC_PROTOCOL_IAD = 1 +}misc_protocol_type_t; + +typedef enum +{ + DEVICE_CAPABILITY_WIRELESS_USB = 0x01, + DEVICE_CAPABILITY_USB20_EXTENSION = 0x02, + DEVICE_CAPABILITY_SUPERSPEED_USB = 0x03, + DEVICE_CAPABILITY_CONTAINER_id = 0x04, + DEVICE_CAPABILITY_PLATFORM = 0x05, + DEVICE_CAPABILITY_POWER_DELIVERY = 0x06, + DEVICE_CAPABILITY_BATTERY_INFO = 0x07, + DEVICE_CAPABILITY_PD_CONSUMER_PORT = 0x08, + DEVICE_CAPABILITY_PD_PROVIDER_PORT = 0x09, + DEVICE_CAPABILITY_SUPERSPEED_PLUS = 0x0A, + DEVICE_CAPABILITY_PRECESION_TIME_MEASUREMENT = 0x0B, + DEVICE_CAPABILITY_WIRELESS_USB_EXT = 0x0C, + DEVICE_CAPABILITY_BILLBOARD = 0x0D, + DEVICE_CAPABILITY_AUTHENTICATION = 0x0E, + DEVICE_CAPABILITY_BILLBOARD_EX = 0x0F, + DEVICE_CAPABILITY_CONFIGURATION_SUMMARY = 0x10 +}device_capability_type_t; + +enum { + TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP = TU_BIT(5), + TUSB_DESC_CONFIG_ATT_SELF_POWERED = TU_BIT(6), +}; + +#define TUSB_DESC_CONFIG_POWER_MA(x) ((x)/2) + +/// Device State TODO remove +typedef enum +{ + TUSB_DEVICE_STATE_UNPLUG = 0 , + TUSB_DEVICE_STATE_CONFIGURED , + TUSB_DEVICE_STATE_SUSPENDED , +}tusb_device_state_t; + +typedef enum +{ + XFER_RESULT_SUCCESS, + XFER_RESULT_FAILED, + XFER_RESULT_STALLED, +}xfer_result_t; + +enum // TODO remove +{ + DESC_OFFSET_LEN = 0, + DESC_OFFSET_TYPE = 1 +}; + +enum +{ + INTERFACE_INVALID_NUMBER = 0xff +}; + + +typedef enum +{ + MS_OS_20_SET_HEADER_DESCRIPTOR = 0x00, + MS_OS_20_SUBSET_HEADER_CONFIGURATION = 0x01, + MS_OS_20_SUBSET_HEADER_FUNCTION = 0x02, + MS_OS_20_FEATURE_COMPATBLE_ID = 0x03, + MS_OS_20_FEATURE_REG_PROPERTY = 0x04, + MS_OS_20_FEATURE_MIN_RESUME_TIME = 0x05, + MS_OS_20_FEATURE_MODEL_ID = 0x06, + MS_OS_20_FEATURE_CCGP_DEVICE = 0x07, + MS_OS_20_FEATURE_VENDOR_REVISION = 0x08 +} microsoft_os_20_type_t; + +//--------------------------------------------------------------------+ +// USB Descriptors +//--------------------------------------------------------------------+ + +/// USB Device Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< DEVICE Descriptor Type. + uint16_t bcdUSB ; ///< BUSB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H). This field identifies the release of the USB Specification with which the device and its descriptors are compliant. + + uint8_t bDeviceClass ; ///< Class code (assigned by the USB-IF). \li If this field is reset to zero, each interface within a configuration specifies its own class information and the various interfaces operate independently. \li If this field is set to a value between 1 and FEH, the device supports different class specifications on different interfaces and the interfaces may not operate independently. This value identifies the class definition used for the aggregate interfaces. \li If this field is set to FFH, the device class is vendor-specific. + uint8_t bDeviceSubClass ; ///< Subclass code (assigned by the USB-IF). These codes are qualified by the value of the bDeviceClass field. \li If the bDeviceClass field is reset to zero, this field must also be reset to zero. \li If the bDeviceClass field is not set to FFH, all values are reserved for assignment by the USB-IF. + uint8_t bDeviceProtocol ; ///< Protocol code (assigned by the USB-IF). These codes are qualified by the value of the bDeviceClass and the bDeviceSubClass fields. If a device supports class-specific protocols on a device basis as opposed to an interface basis, this code identifies the protocols that the device uses as defined by the specification of the device class. \li If this field is reset to zero, the device does not use class-specific protocols on a device basis. However, it may use classspecific protocols on an interface basis. \li If this field is set to FFH, the device uses a vendor-specific protocol on a device basis. + uint8_t bMaxPacketSize0 ; ///< Maximum packet size for endpoint zero (only 8, 16, 32, or 64 are valid). For HS devices is fixed to 64. + + uint16_t idVendor ; ///< Vendor ID (assigned by the USB-IF). + uint16_t idProduct ; ///< Product ID (assigned by the manufacturer). + uint16_t bcdDevice ; ///< Device release number in binary-coded decimal. + uint8_t iManufacturer ; ///< Index of string descriptor describing manufacturer. + uint8_t iProduct ; ///< Index of string descriptor describing product. + uint8_t iSerialNumber ; ///< Index of string descriptor describing the device's serial number. + + uint8_t bNumConfigurations ; ///< Number of possible configurations. +} tusb_desc_device_t; + +// USB Binary Device Object Store (BOS) Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes + uint8_t bDescriptorType ; ///< CONFIGURATION Descriptor Type + uint16_t wTotalLength ; ///< Total length of data returned for this descriptor + uint8_t bNumDeviceCaps ; ///< Number of device capability descriptors in the BOS +} tusb_desc_bos_t; + +/// USB Configuration Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes + uint8_t bDescriptorType ; ///< CONFIGURATION Descriptor Type + uint16_t wTotalLength ; ///< Total length of data returned for this configuration. Includes the combined length of all descriptors (configuration, interface, endpoint, and class- or vendor-specific) returned for this configuration. + + uint8_t bNumInterfaces ; ///< Number of interfaces supported by this configuration + uint8_t bConfigurationValue ; ///< Value to use as an argument to the SetConfiguration() request to select this configuration. + uint8_t iConfiguration ; ///< Index of string descriptor describing this configuration + uint8_t bmAttributes ; ///< Configuration characteristics \n D7: Reserved (set to one)\n D6: Self-powered \n D5: Remote Wakeup \n D4...0: Reserved (reset to zero) \n D7 is reserved and must be set to one for historical reasons. \n A device configuration that uses power from the bus and a local source reports a non-zero value in bMaxPower to indicate the amount of bus power required and sets D6. The actual power source at runtime may be determined using the GetStatus(DEVICE) request (see USB 2.0 spec Section 9.4.5). \n If a device configuration supports remote wakeup, D5 is set to one. + uint8_t bMaxPower ; ///< Maximum power consumption of the USB device from the bus in this specific configuration when the device is fully operational. Expressed in 2 mA units (i.e., 50 = 100 mA). +} tusb_desc_configuration_t; + +/// USB Interface Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes + uint8_t bDescriptorType ; ///< INTERFACE Descriptor Type + + uint8_t bInterfaceNumber ; ///< Number of this interface. Zero-based value identifying the index in the array of concurrent interfaces supported by this configuration. + uint8_t bAlternateSetting ; ///< Value used to select this alternate setting for the interface identified in the prior field + uint8_t bNumEndpoints ; ///< Number of endpoints used by this interface (excluding endpoint zero). If this value is zero, this interface only uses the Default Control Pipe. + uint8_t bInterfaceClass ; ///< Class code (assigned by the USB-IF). \li A value of zero is reserved for future standardization. \li If this field is set to FFH, the interface class is vendor-specific. \li All other values are reserved for assignment by the USB-IF. + uint8_t bInterfaceSubClass ; ///< Subclass code (assigned by the USB-IF). \n These codes are qualified by the value of the bInterfaceClass field. \li If the bInterfaceClass field is reset to zero, this field must also be reset to zero. \li If the bInterfaceClass field is not set to FFH, all values are reserved for assignment by the USB-IF. + uint8_t bInterfaceProtocol ; ///< Protocol code (assigned by the USB). \n These codes are qualified by the value of the bInterfaceClass and the bInterfaceSubClass fields. If an interface supports class-specific requests, this code identifies the protocols that the device uses as defined by the specification of the device class. \li If this field is reset to zero, the device does not use a class-specific protocol on this interface. \li If this field is set to FFH, the device uses a vendor-specific protocol for this interface. + uint8_t iInterface ; ///< Index of string descriptor describing this interface +} tusb_desc_interface_t; + +/// USB Endpoint Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes + uint8_t bDescriptorType ; ///< ENDPOINT Descriptor Type + + uint8_t bEndpointAddress ; ///< The address of the endpoint on the USB device described by this descriptor. The address is encoded as follows: \n Bit 3...0: The endpoint number \n Bit 6...4: Reserved, reset to zero \n Bit 7: Direction, ignored for control endpoints 0 = OUT endpoint 1 = IN endpoint. + + struct TU_ATTR_PACKED { + uint8_t xfer : 2; + uint8_t sync : 2; + uint8_t usage : 2; + uint8_t : 2; + } bmAttributes ; ///< This field describes the endpoint's attributes when it is configured using the bConfigurationValue. \n Bits 1..0: Transfer Type \n- 00 = Control \n- 01 = Isochronous \n- 10 = Bulk \n- 11 = Interrupt \n If not an isochronous endpoint, bits 5..2 are reserved and must be set to zero. If isochronous, they are defined as follows: \n Bits 3..2: Synchronization Type \n- 00 = No Synchronization \n- 01 = Asynchronous \n- 10 = Adaptive \n- 11 = Synchronous \n Bits 5..4: Usage Type \n- 00 = Data endpoint \n- 01 = Feedback endpoint \n- 10 = Implicit feedback Data endpoint \n- 11 = Reserved \n Refer to Chapter 5 of USB 2.0 specification for more information. \n All other bits are reserved and must be reset to zero. Reserved bits must be ignored by the host. + + struct TU_ATTR_PACKED { + uint16_t size : 11; ///< Maximum packet size this endpoint is capable of sending or receiving when this configuration is selected. \n For isochronous endpoints, this value is used to reserve the bus time in the schedule, required for the per-(micro)frame data payloads. The pipe may, on an ongoing basis, actually use less bandwidth than that reserved. The device reports, if necessary, the actual bandwidth used via its normal, non-USB defined mechanisms. \n For all endpoints, bits 10..0 specify the maximum packet size (in bytes). \n For high-speed isochronous and interrupt endpoints: \n Bits 12..11 specify the number of additional transaction opportunities per microframe: \n- 00 = None (1 transaction per microframe) \n- 01 = 1 additional (2 per microframe) \n- 10 = 2 additional (3 per microframe) \n- 11 = Reserved \n Bits 15..13 are reserved and must be set to zero. + uint16_t hs_period_mult : 2; + uint16_t : 0; + }wMaxPacketSize; + + uint8_t bInterval ; ///< Interval for polling endpoint for data transfers. Expressed in frames or microframes depending on the device operating speed (i.e., either 1 millisecond or 125 us units). \n- For full-/high-speed isochronous endpoints, this value must be in the range from 1 to 16. The bInterval value is used as the exponent for a \f$ 2^(bInterval-1) \f$ value; e.g., a bInterval of 4 means a period of 8 (\f$ 2^(4-1) \f$). \n- For full-/low-speed interrupt endpoints, the value of this field may be from 1 to 255. \n- For high-speed interrupt endpoints, the bInterval value is used as the exponent for a \f$ 2^(bInterval-1) \f$ value; e.g., a bInterval of 4 means a period of 8 (\f$ 2^(4-1) \f$) . This value must be from 1 to 16. \n- For high-speed bulk/control OUT endpoints, the bInterval must specify the maximum NAK rate of the endpoint. A value of 0 indicates the endpoint never NAKs. Other values indicate at most 1 NAK each bInterval number of microframes. This value must be in the range from 0 to 255. \n Refer to Chapter 5 of USB 2.0 specification for more information. +} tusb_desc_endpoint_t; + +/// USB Other Speed Configuration Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of descriptor + uint8_t bDescriptorType ; ///< Other_speed_Configuration Type + uint16_t wTotalLength ; ///< Total length of data returned + + uint8_t bNumInterfaces ; ///< Number of interfaces supported by this speed configuration + uint8_t bConfigurationValue ; ///< Value to use to select configuration + uint8_t IConfiguration ; ///< Index of string descriptor + uint8_t bmAttributes ; ///< Same as Configuration descriptor + uint8_t bMaxPower ; ///< Same as Configuration descriptor +} tusb_desc_other_speed_t; + +/// USB Device Qualifier Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of descriptor + uint8_t bDescriptorType ; ///< Device Qualifier Type + uint16_t bcdUSB ; ///< USB specification version number (e.g., 0200H for V2.00) + + uint8_t bDeviceClass ; ///< Class Code + uint8_t bDeviceSubClass ; ///< SubClass Code + uint8_t bDeviceProtocol ; ///< Protocol Code + uint8_t bMaxPacketSize0 ; ///< Maximum packet size for other speed + uint8_t bNumConfigurations ; ///< Number of Other-speed Configurations + uint8_t bReserved ; ///< Reserved for future use, must be zero +} tusb_desc_device_qualifier_t; + +/// USB Interface Association Descriptor (IAD ECN) +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of descriptor + uint8_t bDescriptorType ; ///< Other_speed_Configuration Type + + uint8_t bFirstInterface ; ///< Index of the first associated interface. + uint8_t bInterfaceCount ; ///< Total number of associated interfaces. + + uint8_t bFunctionClass ; ///< Interface class ID. + uint8_t bFunctionSubClass ; ///< Interface subclass ID. + uint8_t bFunctionProtocol ; ///< Interface protocol ID. + + uint8_t iFunction ; ///< Index of the string descriptor describing the interface association. +} tusb_desc_interface_assoc_t; + +// USB String Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes + uint8_t bDescriptorType ; ///< Descriptor Type + uint16_t unicode_string[]; +} tusb_desc_string_t; + +// USB Binary Device Object Store (BOS) +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength; + uint8_t bDescriptorType ; + uint8_t bDevCapabilityType; + uint8_t bReserved; + uint8_t PlatformCapabilityUUID[16]; + uint8_t CapabilityData[]; +} tusb_desc_bos_platform_t; + +// USB WebuSB URL Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bScheme; + char url[]; +} tusb_desc_webusb_url_t; + +/*------------------------------------------------------------------*/ +/* Types + *------------------------------------------------------------------*/ +typedef struct TU_ATTR_PACKED{ + union { + struct TU_ATTR_PACKED { + uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t. + uint8_t type : 2; ///< Request type tusb_request_type_t. + uint8_t direction : 1; ///< Direction type. tusb_dir_t + } bmRequestType_bit; + + uint8_t bmRequestType; + }; + + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} tusb_control_request_t; + +TU_VERIFY_STATIC( sizeof(tusb_control_request_t) == 8, "mostly compiler option issue"); + +// TODO move to somewhere suitable +static inline uint8_t bm_request_type(uint8_t direction, uint8_t type, uint8_t recipient) +{ + return ((uint8_t) (direction << 7)) | ((uint8_t) (type << 5)) | (recipient); +} + +//--------------------------------------------------------------------+ +// Endpoint helper +//--------------------------------------------------------------------+ + +// Get direction from Endpoint address +static inline tusb_dir_t tu_edpt_dir(uint8_t addr) +{ + return (addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT; +} + +// Get Endpoint number from address +static inline uint8_t tu_edpt_number(uint8_t addr) +{ + return (uint8_t)(addr & (~TUSB_DIR_IN_MASK)); +} + +static inline uint8_t tu_edpt_addr(uint8_t num, uint8_t dir) +{ + return (uint8_t)(num | (dir ? TUSB_DIR_IN_MASK : 0)); +} + +//--------------------------------------------------------------------+ +// Descriptor helper +//--------------------------------------------------------------------+ +static inline uint8_t const * tu_desc_next(void const* desc) +{ + uint8_t const* desc8 = (uint8_t const*) desc; + return desc8 + desc8[DESC_OFFSET_LEN]; +} + +static inline uint8_t tu_desc_type(void const* desc) +{ + return ((uint8_t const*) desc)[DESC_OFFSET_TYPE]; +} + +static inline uint8_t tu_desc_len(void const* desc) +{ + return ((uint8_t const*) desc)[DESC_OFFSET_LEN]; +} + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_TYPES_H_ */ + +/** @} */ 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_ */ + +/** @} */ diff --git a/src/host/ehci/ehci.c b/src/host/ehci/ehci.c index 18123a497..9ddffaf2c 100644 --- a/src/host/ehci/ehci.c +++ b/src/host/ehci/ehci.c @@ -1,856 +1,856 @@ -/* - * 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 "common/tusb_common.h" - -#if TUSB_OPT_HOST_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX) -//--------------------------------------------------------------------+ -// INCLUDE -//--------------------------------------------------------------------+ -#include "osal/osal.h" - -#include "../hcd.h" -#include "../usbh_hcd.h" -#include "ehci.h" - -//--------------------------------------------------------------------+ -// MACRO CONSTANT TYPEDEF -//--------------------------------------------------------------------+ - -//--------------------------------------------------------------------+ -// INTERNAL OBJECT & FUNCTION DECLARATION -//--------------------------------------------------------------------+ -// Periodic frame list must be 4K alignment -CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4096) static ehci_data_t ehci_data; - -// EHCI portable -uint32_t hcd_ehci_register_addr(uint8_t rhport); - -//--------------------------------------------------------------------+ -// PROTOTYPE -//--------------------------------------------------------------------+ -static inline ehci_link_t* get_period_head(uint8_t rhport, uint8_t interval_ms) -{ - (void) rhport; - return (ehci_link_t*) &ehci_data.period_head_arr[ tu_log2( tu_min8(EHCI_FRAMELIST_SIZE, interval_ms) ) ]; -} - -static inline ehci_qhd_t* qhd_control(uint8_t dev_addr) -{ - return &ehci_data.control[dev_addr].qhd; -} - -static inline ehci_qhd_t* qhd_async_head(uint8_t rhport) -{ - (void) rhport; - return qhd_control(0); // control qhd of dev0 is used as async head -} - -static inline ehci_qtd_t* qtd_control(uint8_t dev_addr) -{ - return &ehci_data.control[dev_addr].qtd; -} - - -static inline ehci_qhd_t* qhd_next (ehci_qhd_t const * p_qhd); -static inline ehci_qhd_t* qhd_find_free (void); -static inline ehci_qhd_t* qhd_get_from_addr (uint8_t dev_addr, uint8_t ep_addr); - -// determine if a queue head has bus-related error -static inline bool qhd_has_xact_error (ehci_qhd_t * p_qhd) -{ - return (p_qhd->qtd_overlay.buffer_err || p_qhd->qtd_overlay.babble_err || p_qhd->qtd_overlay.xact_err); - //p_qhd->qtd_overlay.non_hs_period_missed_uframe || p_qhd->qtd_overlay.pingstate_err TODO split transaction error -} - -static void qhd_init (ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc); - -static inline ehci_qtd_t* qtd_find_free (void); -static inline ehci_qtd_t* qtd_next (ehci_qtd_t const * p_qtd); -static inline void qtd_insert_to_qhd (ehci_qhd_t *p_qhd, ehci_qtd_t *p_qtd_new); -static inline void qtd_remove_1st_from_qhd (ehci_qhd_t *p_qhd); -static void qtd_init (ehci_qtd_t* p_qtd, void* buffer, uint16_t total_bytes); - -static inline void list_insert (ehci_link_t *current, ehci_link_t *new, uint8_t new_type); -static inline ehci_link_t* list_next (ehci_link_t *p_link_pointer); - -static bool ehci_init (uint8_t rhport); - -//--------------------------------------------------------------------+ -// HCD API -//--------------------------------------------------------------------+ -bool hcd_init(void) -{ - tu_memclr(&ehci_data, sizeof(ehci_data_t)); - return ehci_init(TUH_OPT_RHPORT); -} - -void hcd_port_reset(uint8_t rhport) -{ - (void) rhport; - - ehci_registers_t* regs = ehci_data.regs; - - regs->portsc_bm.port_enabled = 0; // disable port before reset - regs->portsc_bm.port_reset = 1; -} - -bool hcd_port_connect_status(uint8_t rhport) -{ - (void) rhport; - return ehci_data.regs->portsc_bm.current_connect_status; -} - -tusb_speed_t hcd_port_speed_get(uint8_t rhport) -{ - (void) rhport; - return (tusb_speed_t) ehci_data.regs->portsc_bm.nxp_port_speed; // NXP specific port speed -} - -static void list_remove_qhd_by_addr(ehci_link_t* list_head, uint8_t dev_addr) -{ - for(ehci_link_t* prev = list_head; - !prev->terminate && (tu_align32(prev->address) != (uint32_t) list_head); - prev = list_next(prev) ) - { - // TODO check type for ISO iTD and siTD - ehci_qhd_t* qhd = (ehci_qhd_t*) list_next(prev); - if ( qhd->dev_addr == dev_addr ) - { - // TODO deactive all TD, wait for QHD to inactive before removal - prev->address = qhd->next.address; - - // EHCI 4.8.2 link the removed qhd to async head (which always reachable by Host Controller) - qhd->next.address = ((uint32_t) list_head) | (EHCI_QTYPE_QHD << 1); - - if ( qhd->int_smask ) - { - // period list queue element is guarantee to be free in the next frame (1 ms) - qhd->used = 0; - }else - { - // async list use async advance handshake - // mark as removing, will completely re-usable when async advance isr occurs - qhd->removing = 1; - } - } - } -} - -// Close all opened endpoint belong to this device -void hcd_device_close(uint8_t rhport, uint8_t dev_addr) -{ - // skip dev0 - if (dev_addr == 0) return; - - // Remove from async list - list_remove_qhd_by_addr( (ehci_link_t*) qhd_async_head(rhport), dev_addr ); - - // Remove from all interval period list - for(uint8_t i = 0; i < TU_ARRAY_SIZE(ehci_data.period_head_arr); i++) - { - list_remove_qhd_by_addr( (ehci_link_t*) &ehci_data.period_head_arr[i], dev_addr); - } - - // Async doorbell (EHCI 4.8.2 for operational details) - ehci_data.regs->command_bm.async_adv_doorbell = 1; -} - -// EHCI controller init -static bool ehci_init(uint8_t rhport) -{ - ehci_data.regs = (ehci_registers_t* ) hcd_ehci_register_addr(rhport); - - ehci_registers_t* regs = ehci_data.regs; - - //------------- CTRLDSSEGMENT Register (skip) -------------// - //------------- USB INT Register -------------// - regs->inten = 0; // 1. disable all the interrupt - regs->status = EHCI_INT_MASK_ALL; // 2. clear all status - - regs->inten = EHCI_INT_MASK_ERROR | EHCI_INT_MASK_PORT_CHANGE | EHCI_INT_MASK_ASYNC_ADVANCE | - EHCI_INT_MASK_NXP_PERIODIC | EHCI_INT_MASK_NXP_ASYNC ; - - //------------- Asynchronous List -------------// - ehci_qhd_t * const async_head = qhd_async_head(rhport); - tu_memclr(async_head, sizeof(ehci_qhd_t)); - - async_head->next.address = (uint32_t) async_head; // circular list, next is itself - async_head->next.type = EHCI_QTYPE_QHD; - async_head->head_list_flag = 1; - async_head->qtd_overlay.halted = 1; // inactive most of time - async_head->qtd_overlay.next.terminate = 1; // TODO removed if verified - - regs->async_list_addr = (uint32_t) async_head; - - //------------- Periodic List -------------// - // Build the polling interval tree with 1 ms, 2 ms, 4 ms and 8 ms (framesize) only - for(uint32_t i=0; i<4; i++) - { - ehci_data.period_head_arr[i].int_smask = 1; // queue head in period list must have smask non-zero - ehci_data.period_head_arr[i].qtd_overlay.halted = 1; // dummy node, always inactive - } - - ehci_link_t * const framelist = ehci_data.period_framelist; - ehci_link_t * const period_1ms = get_period_head(rhport, 1); - // all links --> period_head_arr[0] (1ms) - // 0, 2, 4, 6 etc --> period_head_arr[1] (2ms) - // 1, 5 --> period_head_arr[2] (4ms) - // 3 --> period_head_arr[3] (8ms) - - // TODO EHCI_FRAMELIST_SIZE with other size than 8 - for(uint32_t i=0; iterminate = 1; - - regs->periodic_list_base = (uint32_t) framelist; - - //------------- TT Control (NXP only) -------------// - regs->nxp_tt_control = 0; - - //------------- USB CMD Register -------------// - regs->command |= TU_BIT(EHCI_USBCMD_POS_RUN_STOP) | TU_BIT(EHCI_USBCMD_POS_ASYNC_ENABLE) - | TU_BIT(EHCI_USBCMD_POS_PERIOD_ENABLE) // TODO enable period list only there is int/iso endpoint - | ((EHCI_CFG_FRAMELIST_SIZE_BITS & TU_BIN8(011)) << EHCI_USBCMD_POS_FRAMELIST_SZIE) - | ((EHCI_CFG_FRAMELIST_SIZE_BITS >> 2) << EHCI_USBCMD_POS_NXP_FRAMELIST_SIZE_MSB); - - //------------- ConfigFlag Register (skip) -------------// - regs->portsc_bm.port_power = 1; // enable port power - - return true; -} - -#if 0 -static void ehci_stop(uint8_t rhport) -{ - (void) rhport; - - ehci_registers_t* regs = ehci_data.regs; - - regs->command_bm.run_stop = 0; - - // USB Spec: controller has to stop within 16 uframe = 2 frames - while( regs->status_bm.hc_halted == 0 ) {} -} -#endif - -//--------------------------------------------------------------------+ -// CONTROL PIPE API -//--------------------------------------------------------------------+ -bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) -{ - (void) rhport; - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - // FIXME control only for now - if ( epnum == 0 ) - { - ehci_qhd_t* qhd = qhd_control(dev_addr); - ehci_qtd_t* qtd = qtd_control(dev_addr); - - qtd_init(qtd, buffer, buflen); - - // first first data toggle is always 1 (data & setup stage) - qtd->data_toggle = 1; - qtd->pid = dir ? EHCI_PID_IN : EHCI_PID_OUT; - qtd->int_on_complete = 1; - qtd->next.terminate = 1; - - // sw region - qhd->p_qtd_list_head = qtd; - qhd->p_qtd_list_tail = qtd; - - // attach TD - qhd->qtd_overlay.next.address = (uint32_t) qtd; - } - - return true; -} - -bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) -{ - (void) rhport; - - ehci_qhd_t* qhd = &ehci_data.control[dev_addr].qhd; - ehci_qtd_t* td = &ehci_data.control[dev_addr].qtd; - - qtd_init(td, (void*) setup_packet, 8); - td->pid = EHCI_PID_SETUP; - td->int_on_complete = 1; - td->next.terminate = 1; - - // sw region - qhd->p_qtd_list_head = td; - qhd->p_qtd_list_tail = td; - - // attach TD - qhd->qtd_overlay.next.address = (uint32_t) td; - - return true; -} - -//--------------------------------------------------------------------+ -// BULK/INT/ISO PIPE API -//--------------------------------------------------------------------+ -bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc) -{ - (void) rhport; - - // TODO not support ISO yet - TU_ASSERT (ep_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS); - - //------------- Prepare Queue Head -------------// - ehci_qhd_t * p_qhd; - - if ( ep_desc->bEndpointAddress == 0 ) - { - p_qhd = qhd_control(dev_addr); - }else - { - p_qhd = qhd_find_free(); - } - TU_ASSERT(p_qhd); - - qhd_init(p_qhd, dev_addr, ep_desc); - - // control of dev0 is always present as async head - if ( dev_addr == 0 ) return true; - - // Insert to list - ehci_link_t * list_head; - - switch (ep_desc->bmAttributes.xfer) - { - case TUSB_XFER_CONTROL: - case TUSB_XFER_BULK: - list_head = (ehci_link_t*) qhd_async_head(rhport); - break; - - case TUSB_XFER_INTERRUPT: - list_head = get_period_head(rhport, p_qhd->interval_ms); - break; - - case TUSB_XFER_ISOCHRONOUS: - // TODO iso is not supported - break; - - default: break; - } - - // TODO might need to disable async/period list - list_insert( list_head, (ehci_link_t*) p_qhd, EHCI_QTYPE_QHD); - - return true; -} - -bool hcd_pipe_queue_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes) -{ - //------------- set up QTD -------------// - ehci_qhd_t *p_qhd = qhd_get_from_addr(dev_addr, ep_addr); - ehci_qtd_t *p_qtd = qtd_find_free(); - - TU_ASSERT(p_qtd); - - qtd_init(p_qtd, buffer, total_bytes); - p_qtd->pid = p_qhd->pid; - - //------------- insert TD to TD list -------------// - qtd_insert_to_qhd(p_qhd, p_qtd); - - return true; -} - -bool hcd_pipe_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete) -{ - TU_ASSERT ( hcd_pipe_queue_xfer(dev_addr, ep_addr, buffer, total_bytes) ); - - ehci_qhd_t *p_qhd = qhd_get_from_addr(dev_addr, ep_addr); - - if ( int_on_complete ) - { // the just added qtd is pointed by list_tail - p_qhd->p_qtd_list_tail->int_on_complete = 1; - } - p_qhd->qtd_overlay.next.address = (uint32_t) p_qhd->p_qtd_list_head; // attach head QTD to QHD start transferring - - return true; -} - -bool hcd_edpt_busy(uint8_t dev_addr, uint8_t ep_addr) -{ - ehci_qhd_t *p_qhd = qhd_get_from_addr(dev_addr, ep_addr); - return !p_qhd->qtd_overlay.halted && (p_qhd->p_qtd_list_head != NULL); -} - -bool hcd_edpt_stalled(uint8_t dev_addr, uint8_t ep_addr) -{ - ehci_qhd_t *p_qhd = qhd_get_from_addr(dev_addr, ep_addr); - return p_qhd->qtd_overlay.halted && !qhd_has_xact_error(p_qhd); -} - -bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr) -{ - ehci_qhd_t *p_qhd = qhd_get_from_addr(dev_addr, ep_addr); - p_qhd->qtd_overlay.halted = 0; - // TODO reset data toggle ? - return true; -} - -//--------------------------------------------------------------------+ -// EHCI Interrupt Handler -//--------------------------------------------------------------------+ - -// async_advance is handshake between usb stack & ehci controller. -// This isr mean it is safe to modify previously removed queue head from async list. -// In tinyusb, queue head is only removed when device is unplugged. -static void async_advance_isr(uint8_t rhport) -{ - (void) rhport; - - ehci_qhd_t* qhd_pool = ehci_data.qhd_pool; - for(uint32_t i = 0; i < HCD_MAX_ENDPOINT; i++) - { - if ( qhd_pool[i].removing ) - { - qhd_pool[i].removing = 0; - qhd_pool[i].used = 0; - } - } -} - -static void port_connect_status_change_isr(uint8_t hostid) -{ - // NOTE There is an sequence plug->unplug->…..-> plug if device is powering with pre-plugged device - if (ehci_data.regs->portsc_bm.current_connect_status) - { - hcd_port_reset(hostid); - hcd_event_device_attach(hostid); - }else // device unplugged - { - hcd_event_device_remove(hostid); - } -} - -static void qhd_xfer_complete_isr(ehci_qhd_t * p_qhd) -{ - // free all TDs from the head td to the first active TD - while(p_qhd->p_qtd_list_head != NULL && !p_qhd->p_qtd_list_head->active) - { - // TD need to be freed and removed from qhd, before invoking callback - bool is_ioc = (p_qhd->p_qtd_list_head->int_on_complete != 0); - p_qhd->total_xferred_bytes += p_qhd->p_qtd_list_head->expected_bytes - p_qhd->p_qtd_list_head->total_bytes; - - p_qhd->p_qtd_list_head->used = 0; // free QTD - qtd_remove_1st_from_qhd(p_qhd); - - if (is_ioc) - { - // end of request - // call USBH callback - hcd_event_xfer_complete(p_qhd->dev_addr, tu_edpt_addr(p_qhd->ep_number, p_qhd->pid == EHCI_PID_IN ? 1 : 0), XFER_RESULT_SUCCESS, p_qhd->total_xferred_bytes); - p_qhd->total_xferred_bytes = 0; - } - } -} - -static void async_list_xfer_complete_isr(ehci_qhd_t * const async_head) -{ - ehci_qhd_t *p_qhd = async_head; - do - { - if ( !p_qhd->qtd_overlay.halted ) // halted or error is processed in error isr - { - qhd_xfer_complete_isr(p_qhd); - } - p_qhd = qhd_next(p_qhd); - }while(p_qhd != async_head); // async list traversal, stop if loop around -} - -static void period_list_xfer_complete_isr(uint8_t hostid, uint8_t interval_ms) -{ - uint8_t max_loop = 0; - uint32_t const period_1ms_addr = (uint32_t) get_period_head(hostid, 1); - ehci_link_t next_item = * get_period_head(hostid, interval_ms); - - // TODO abstract max loop guard for period - while( !next_item.terminate && - !(interval_ms > 1 && period_1ms_addr == tu_align32(next_item.address)) && - max_loop < (HCD_MAX_ENDPOINT + EHCI_MAX_ITD + EHCI_MAX_SITD)*CFG_TUSB_HOST_DEVICE_MAX) - { - switch ( next_item.type ) - { - case EHCI_QTYPE_QHD: - { - ehci_qhd_t *p_qhd_int = (ehci_qhd_t *) tu_align32(next_item.address); - if ( !p_qhd_int->qtd_overlay.halted ) - { - qhd_xfer_complete_isr(p_qhd_int); - } - } - break; - - case EHCI_QTYPE_ITD: // TODO support hs/fs ISO - case EHCI_QTYPE_SITD: - case EHCI_QTYPE_FSTN: - - default: break; - } - - next_item = *list_next(&next_item); - max_loop++; - } -} - -static void qhd_xfer_error_isr(ehci_qhd_t * p_qhd) -{ - if ( (p_qhd->dev_addr != 0 && p_qhd->qtd_overlay.halted) || // addr0 cannot be protocol STALL - qhd_has_xact_error(p_qhd) ) - { - // current qhd has error in transaction - xfer_result_t error_event; - - // no error bits are set, endpoint is halted due to STALL - error_event = qhd_has_xact_error(p_qhd) ? XFER_RESULT_FAILED : XFER_RESULT_STALLED; - - p_qhd->total_xferred_bytes += p_qhd->p_qtd_list_head->expected_bytes - p_qhd->p_qtd_list_head->total_bytes; - -// if ( XFER_RESULT_FAILED == error_event ) TU_BREAKPOINT(); // TODO skip unplugged device - - p_qhd->p_qtd_list_head->used = 0; // free QTD - qtd_remove_1st_from_qhd(p_qhd); - - if ( 0 == p_qhd->ep_number ) - { - // control cannot be halted --> clear all qtd list - p_qhd->p_qtd_list_head = NULL; - p_qhd->p_qtd_list_tail = NULL; - - p_qhd->qtd_overlay.next.terminate = 1; - p_qhd->qtd_overlay.alternate.terminate = 1; - p_qhd->qtd_overlay.halted = 0; - - ehci_qtd_t *p_setup = qtd_control(p_qhd->dev_addr); - p_setup->used = 0; - } - - // call USBH callback - hcd_event_xfer_complete(p_qhd->dev_addr, tu_edpt_addr(p_qhd->ep_number, p_qhd->pid == EHCI_PID_IN ? 1 : 0), error_event, p_qhd->total_xferred_bytes); - - p_qhd->total_xferred_bytes = 0; - } -} - -static void xfer_error_isr(uint8_t hostid) -{ - //------------- async list -------------// - ehci_qhd_t * const async_head = qhd_async_head(hostid); - ehci_qhd_t *p_qhd = async_head; - do - { - qhd_xfer_error_isr( p_qhd ); - p_qhd = qhd_next(p_qhd); - }while(p_qhd != async_head); // async list traversal, stop if loop around - - //------------- TODO refractor period list -------------// - uint32_t const period_1ms_addr = (uint32_t) get_period_head(hostid, 1); - for (uint8_t interval_ms=1; interval_ms <= EHCI_FRAMELIST_SIZE; interval_ms *= 2) - { - ehci_link_t next_item = * get_period_head(hostid, interval_ms); - - // TODO abstract max loop guard for period - while( !next_item.terminate && - !(interval_ms > 1 && period_1ms_addr == tu_align32(next_item.address)) ) - { - switch ( next_item.type ) - { - case EHCI_QTYPE_QHD: - { - ehci_qhd_t *p_qhd_int = (ehci_qhd_t *) tu_align32(next_item.address); - qhd_xfer_error_isr(p_qhd_int); - } - break; - - // TODO support hs/fs ISO - case EHCI_QTYPE_ITD: - case EHCI_QTYPE_SITD: - case EHCI_QTYPE_FSTN: - default: break; - } - - next_item = *list_next(&next_item); - } - } -} - -//------------- Host Controller Driver's Interrupt Handler -------------// -void hcd_isr(uint8_t rhport) -{ - ehci_registers_t* regs = ehci_data.regs; - - uint32_t int_status = regs->status; - int_status &= regs->inten; - - regs->status |= int_status; // Acknowledge handled interrupt - - if (int_status == 0) return; - - if (int_status & EHCI_INT_MASK_PORT_CHANGE) - { - uint32_t port_status = regs->portsc & EHCI_PORTSC_MASK_ALL; - - if (regs->portsc_bm.connect_status_change) - { - port_connect_status_change_isr(rhport); - } - - regs->portsc |= port_status; // Acknowledge change bits in portsc - } - - if (int_status & EHCI_INT_MASK_ERROR) - { - xfer_error_isr(rhport); - } - - //------------- some QTD/SITD/ITD with IOC set is completed -------------// - if (int_status & EHCI_INT_MASK_NXP_ASYNC) - { - async_list_xfer_complete_isr( qhd_async_head(rhport) ); - } - - if (int_status & EHCI_INT_MASK_NXP_PERIODIC) - { - for (uint8_t i=1; i <= EHCI_FRAMELIST_SIZE; i *= 2) - { - period_list_xfer_complete_isr( rhport, i ); - } - } - - //------------- There is some removed async previously -------------// - if (int_status & EHCI_INT_MASK_ASYNC_ADVANCE) // need to place after EHCI_INT_MASK_NXP_ASYNC - { - async_advance_isr(rhport); - } -} - -//--------------------------------------------------------------------+ -// HELPER -//--------------------------------------------------------------------+ - - -//------------- queue head helper -------------// -static inline ehci_qhd_t* qhd_find_free (void) -{ - for (uint32_t i=0; inext.address); -} - -static inline ehci_qhd_t* qhd_get_from_addr(uint8_t dev_addr, uint8_t ep_addr) -{ - ehci_qhd_t* qhd_pool = ehci_data.qhd_pool; - - for(uint32_t i=0; inext.address); -} - -static inline void qtd_remove_1st_from_qhd(ehci_qhd_t *p_qhd) -{ - if (p_qhd->p_qtd_list_head == p_qhd->p_qtd_list_tail) // last TD --> make it NULL - { - p_qhd->p_qtd_list_head = p_qhd->p_qtd_list_tail = NULL; - }else - { - p_qhd->p_qtd_list_head = qtd_next( p_qhd->p_qtd_list_head ); - } -} - -static inline void qtd_insert_to_qhd(ehci_qhd_t *p_qhd, ehci_qtd_t *p_qtd_new) -{ - if (p_qhd->p_qtd_list_head == NULL) // empty list - { - p_qhd->p_qtd_list_head = p_qhd->p_qtd_list_tail = p_qtd_new; - }else - { - p_qhd->p_qtd_list_tail->next.address = (uint32_t) p_qtd_new; - p_qhd->p_qtd_list_tail = p_qtd_new; - } -} - -static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc) -{ - // address 0 is used as async head, which always on the list --> cannot be cleared (ehci halted otherwise) - if (dev_addr != 0) - { - tu_memclr(p_qhd, sizeof(ehci_qhd_t)); - } - - uint8_t const xfer_type = ep_desc->bmAttributes.xfer; - uint8_t const interval = ep_desc->bInterval; - - p_qhd->dev_addr = dev_addr; - p_qhd->fl_inactive_next_xact = 0; - p_qhd->ep_number = tu_edpt_number(ep_desc->bEndpointAddress); - p_qhd->ep_speed = _usbh_devices[dev_addr].speed; - p_qhd->data_toggle_control= (xfer_type == TUSB_XFER_CONTROL) ? 1 : 0; - p_qhd->head_list_flag = (dev_addr == 0) ? 1 : 0; // addr0's endpoint is the static asyn list head - p_qhd->max_packet_size = ep_desc->wMaxPacketSize.size; - p_qhd->fl_ctrl_ep_flag = ((xfer_type == TUSB_XFER_CONTROL) && (p_qhd->ep_speed != TUSB_SPEED_HIGH)) ? 1 : 0; - p_qhd->nak_reload = 0; - - // Bulk/Control -> smask = cmask = 0 - // TODO Isochronous - if (TUSB_XFER_INTERRUPT == xfer_type) - { - if (TUSB_SPEED_HIGH == p_qhd->ep_speed) - { - TU_ASSERT( interval <= 16, ); - if ( interval < 4) // sub milisecond interval - { - p_qhd->interval_ms = 0; - p_qhd->int_smask = (interval == 1) ? TU_BIN8(11111111) : - (interval == 2) ? TU_BIN8(10101010) : TU_BIN8(01000100); - }else - { - p_qhd->interval_ms = (uint8_t) tu_min16( 1 << (interval-4), 255 ); - p_qhd->int_smask = TU_BIT(interval % 8); - } - }else - { - TU_ASSERT( 0 != interval, ); - // Full/Low: 4.12.2.1 (EHCI) case 1 schedule start split at 1 us & complete split at 2,3,4 uframes - p_qhd->int_smask = 0x01; - p_qhd->fl_int_cmask = TU_BIN8(11100); - p_qhd->interval_ms = interval; - } - }else - { - p_qhd->int_smask = p_qhd->fl_int_cmask = 0; - } - - p_qhd->fl_hub_addr = _usbh_devices[dev_addr].hub_addr; - p_qhd->fl_hub_port = _usbh_devices[dev_addr].hub_port; - p_qhd->mult = 1; // TODO not use high bandwidth/park mode yet - - //------------- HCD Management Data -------------// - p_qhd->used = 1; - p_qhd->removing = 0; - p_qhd->p_qtd_list_head = NULL; - p_qhd->p_qtd_list_tail = NULL; - p_qhd->pid = tu_edpt_dir(ep_desc->bEndpointAddress) ? EHCI_PID_IN : EHCI_PID_OUT; // PID for TD under this endpoint - - //------------- active, but no TD list -------------// - p_qhd->qtd_overlay.halted = 0; - p_qhd->qtd_overlay.next.terminate = 1; - p_qhd->qtd_overlay.alternate.terminate = 1; - if (TUSB_XFER_BULK == xfer_type && p_qhd->ep_speed == TUSB_SPEED_HIGH && p_qhd->pid == EHCI_PID_OUT) - { - p_qhd->qtd_overlay.ping_err = 1; // do PING for Highspeed Bulk OUT, EHCI section 4.11 - } -} - -static void qtd_init(ehci_qtd_t* p_qtd, void* buffer, uint16_t total_bytes) -{ - tu_memclr(p_qtd, sizeof(ehci_qtd_t)); - - p_qtd->used = 1; - - p_qtd->next.terminate = 1; // init to null - p_qtd->alternate.terminate = 1; // not used, always set to terminated - p_qtd->active = 1; - p_qtd->err_count = 3; // TODO 3 consecutive errors tolerance - p_qtd->data_toggle = 0; - p_qtd->total_bytes = total_bytes; - p_qtd->expected_bytes = total_bytes; - - p_qtd->buffer[0] = (uint32_t) buffer; - for(uint8_t i=1; i<5; i++) - { - p_qtd->buffer[i] |= tu_align4k( p_qtd->buffer[i-1] ) + 4096; - } -} - -//------------- List Managing Helper -------------// -static inline void list_insert(ehci_link_t *current, ehci_link_t *new, uint8_t new_type) -{ - new->address = current->address; - current->address = ((uint32_t) new) | (new_type << 1); -} - -static inline ehci_link_t* list_next(ehci_link_t *p_link_pointer) -{ - return (ehci_link_t*) tu_align32(p_link_pointer->address); -} - -#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 "common/tusb_common.h" + +#if TUSB_OPT_HOST_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX) +//--------------------------------------------------------------------+ +// INCLUDE +//--------------------------------------------------------------------+ +#include "osal/osal.h" + +#include "../hcd.h" +#include "../usbh_hcd.h" +#include "ehci.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ +// Periodic frame list must be 4K alignment +CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4096) static ehci_data_t ehci_data; + +// EHCI portable +uint32_t hcd_ehci_register_addr(uint8_t rhport); + +//--------------------------------------------------------------------+ +// PROTOTYPE +//--------------------------------------------------------------------+ +static inline ehci_link_t* get_period_head(uint8_t rhport, uint8_t interval_ms) +{ + (void) rhport; + return (ehci_link_t*) &ehci_data.period_head_arr[ tu_log2( tu_min8(EHCI_FRAMELIST_SIZE, interval_ms) ) ]; +} + +static inline ehci_qhd_t* qhd_control(uint8_t dev_addr) +{ + return &ehci_data.control[dev_addr].qhd; +} + +static inline ehci_qhd_t* qhd_async_head(uint8_t rhport) +{ + (void) rhport; + return qhd_control(0); // control qhd of dev0 is used as async head +} + +static inline ehci_qtd_t* qtd_control(uint8_t dev_addr) +{ + return &ehci_data.control[dev_addr].qtd; +} + + +static inline ehci_qhd_t* qhd_next (ehci_qhd_t const * p_qhd); +static inline ehci_qhd_t* qhd_find_free (void); +static inline ehci_qhd_t* qhd_get_from_addr (uint8_t dev_addr, uint8_t ep_addr); + +// determine if a queue head has bus-related error +static inline bool qhd_has_xact_error (ehci_qhd_t * p_qhd) +{ + return (p_qhd->qtd_overlay.buffer_err || p_qhd->qtd_overlay.babble_err || p_qhd->qtd_overlay.xact_err); + //p_qhd->qtd_overlay.non_hs_period_missed_uframe || p_qhd->qtd_overlay.pingstate_err TODO split transaction error +} + +static void qhd_init (ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc); + +static inline ehci_qtd_t* qtd_find_free (void); +static inline ehci_qtd_t* qtd_next (ehci_qtd_t const * p_qtd); +static inline void qtd_insert_to_qhd (ehci_qhd_t *p_qhd, ehci_qtd_t *p_qtd_new); +static inline void qtd_remove_1st_from_qhd (ehci_qhd_t *p_qhd); +static void qtd_init (ehci_qtd_t* p_qtd, void* buffer, uint16_t total_bytes); + +static inline void list_insert (ehci_link_t *current, ehci_link_t *new, uint8_t new_type); +static inline ehci_link_t* list_next (ehci_link_t *p_link_pointer); + +static bool ehci_init (uint8_t rhport); + +//--------------------------------------------------------------------+ +// HCD API +//--------------------------------------------------------------------+ +bool hcd_init(void) +{ + tu_memclr(&ehci_data, sizeof(ehci_data_t)); + return ehci_init(TUH_OPT_RHPORT); +} + +void hcd_port_reset(uint8_t rhport) +{ + (void) rhport; + + ehci_registers_t* regs = ehci_data.regs; + + regs->portsc_bm.port_enabled = 0; // disable port before reset + regs->portsc_bm.port_reset = 1; +} + +bool hcd_port_connect_status(uint8_t rhport) +{ + (void) rhport; + return ehci_data.regs->portsc_bm.current_connect_status; +} + +tusb_speed_t hcd_port_speed_get(uint8_t rhport) +{ + (void) rhport; + return (tusb_speed_t) ehci_data.regs->portsc_bm.nxp_port_speed; // NXP specific port speed +} + +static void list_remove_qhd_by_addr(ehci_link_t* list_head, uint8_t dev_addr) +{ + for(ehci_link_t* prev = list_head; + !prev->terminate && (tu_align32(prev->address) != (uint32_t) list_head); + prev = list_next(prev) ) + { + // TODO check type for ISO iTD and siTD + ehci_qhd_t* qhd = (ehci_qhd_t*) list_next(prev); + if ( qhd->dev_addr == dev_addr ) + { + // TODO deactive all TD, wait for QHD to inactive before removal + prev->address = qhd->next.address; + + // EHCI 4.8.2 link the removed qhd to async head (which always reachable by Host Controller) + qhd->next.address = ((uint32_t) list_head) | (EHCI_QTYPE_QHD << 1); + + if ( qhd->int_smask ) + { + // period list queue element is guarantee to be free in the next frame (1 ms) + qhd->used = 0; + }else + { + // async list use async advance handshake + // mark as removing, will completely re-usable when async advance isr occurs + qhd->removing = 1; + } + } + } +} + +// Close all opened endpoint belong to this device +void hcd_device_close(uint8_t rhport, uint8_t dev_addr) +{ + // skip dev0 + if (dev_addr == 0) return; + + // Remove from async list + list_remove_qhd_by_addr( (ehci_link_t*) qhd_async_head(rhport), dev_addr ); + + // Remove from all interval period list + for(uint8_t i = 0; i < TU_ARRAY_SIZE(ehci_data.period_head_arr); i++) + { + list_remove_qhd_by_addr( (ehci_link_t*) &ehci_data.period_head_arr[i], dev_addr); + } + + // Async doorbell (EHCI 4.8.2 for operational details) + ehci_data.regs->command_bm.async_adv_doorbell = 1; +} + +// EHCI controller init +static bool ehci_init(uint8_t rhport) +{ + ehci_data.regs = (ehci_registers_t* ) hcd_ehci_register_addr(rhport); + + ehci_registers_t* regs = ehci_data.regs; + + //------------- CTRLDSSEGMENT Register (skip) -------------// + //------------- USB INT Register -------------// + regs->inten = 0; // 1. disable all the interrupt + regs->status = EHCI_INT_MASK_ALL; // 2. clear all status + + regs->inten = EHCI_INT_MASK_ERROR | EHCI_INT_MASK_PORT_CHANGE | EHCI_INT_MASK_ASYNC_ADVANCE | + EHCI_INT_MASK_NXP_PERIODIC | EHCI_INT_MASK_NXP_ASYNC ; + + //------------- Asynchronous List -------------// + ehci_qhd_t * const async_head = qhd_async_head(rhport); + tu_memclr(async_head, sizeof(ehci_qhd_t)); + + async_head->next.address = (uint32_t) async_head; // circular list, next is itself + async_head->next.type = EHCI_QTYPE_QHD; + async_head->head_list_flag = 1; + async_head->qtd_overlay.halted = 1; // inactive most of time + async_head->qtd_overlay.next.terminate = 1; // TODO removed if verified + + regs->async_list_addr = (uint32_t) async_head; + + //------------- Periodic List -------------// + // Build the polling interval tree with 1 ms, 2 ms, 4 ms and 8 ms (framesize) only + for(uint32_t i=0; i<4; i++) + { + ehci_data.period_head_arr[i].int_smask = 1; // queue head in period list must have smask non-zero + ehci_data.period_head_arr[i].qtd_overlay.halted = 1; // dummy node, always inactive + } + + ehci_link_t * const framelist = ehci_data.period_framelist; + ehci_link_t * const period_1ms = get_period_head(rhport, 1); + // all links --> period_head_arr[0] (1ms) + // 0, 2, 4, 6 etc --> period_head_arr[1] (2ms) + // 1, 5 --> period_head_arr[2] (4ms) + // 3 --> period_head_arr[3] (8ms) + + // TODO EHCI_FRAMELIST_SIZE with other size than 8 + for(uint32_t i=0; iterminate = 1; + + regs->periodic_list_base = (uint32_t) framelist; + + //------------- TT Control (NXP only) -------------// + regs->nxp_tt_control = 0; + + //------------- USB CMD Register -------------// + regs->command |= TU_BIT(EHCI_USBCMD_POS_RUN_STOP) | TU_BIT(EHCI_USBCMD_POS_ASYNC_ENABLE) + | TU_BIT(EHCI_USBCMD_POS_PERIOD_ENABLE) // TODO enable period list only there is int/iso endpoint + | ((EHCI_CFG_FRAMELIST_SIZE_BITS & TU_BIN8(011)) << EHCI_USBCMD_POS_FRAMELIST_SZIE) + | ((EHCI_CFG_FRAMELIST_SIZE_BITS >> 2) << EHCI_USBCMD_POS_NXP_FRAMELIST_SIZE_MSB); + + //------------- ConfigFlag Register (skip) -------------// + regs->portsc_bm.port_power = 1; // enable port power + + return true; +} + +#if 0 +static void ehci_stop(uint8_t rhport) +{ + (void) rhport; + + ehci_registers_t* regs = ehci_data.regs; + + regs->command_bm.run_stop = 0; + + // USB Spec: controller has to stop within 16 uframe = 2 frames + while( regs->status_bm.hc_halted == 0 ) {} +} +#endif + +//--------------------------------------------------------------------+ +// CONTROL PIPE API +//--------------------------------------------------------------------+ +bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) +{ + (void) rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + // FIXME control only for now + if ( epnum == 0 ) + { + ehci_qhd_t* qhd = qhd_control(dev_addr); + ehci_qtd_t* qtd = qtd_control(dev_addr); + + qtd_init(qtd, buffer, buflen); + + // first first data toggle is always 1 (data & setup stage) + qtd->data_toggle = 1; + qtd->pid = dir ? EHCI_PID_IN : EHCI_PID_OUT; + qtd->int_on_complete = 1; + qtd->next.terminate = 1; + + // sw region + qhd->p_qtd_list_head = qtd; + qhd->p_qtd_list_tail = qtd; + + // attach TD + qhd->qtd_overlay.next.address = (uint32_t) qtd; + } + + return true; +} + +bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) +{ + (void) rhport; + + ehci_qhd_t* qhd = &ehci_data.control[dev_addr].qhd; + ehci_qtd_t* td = &ehci_data.control[dev_addr].qtd; + + qtd_init(td, (void*) setup_packet, 8); + td->pid = EHCI_PID_SETUP; + td->int_on_complete = 1; + td->next.terminate = 1; + + // sw region + qhd->p_qtd_list_head = td; + qhd->p_qtd_list_tail = td; + + // attach TD + qhd->qtd_overlay.next.address = (uint32_t) td; + + return true; +} + +//--------------------------------------------------------------------+ +// BULK/INT/ISO PIPE API +//--------------------------------------------------------------------+ +bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc) +{ + (void) rhport; + + // TODO not support ISO yet + TU_ASSERT (ep_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS); + + //------------- Prepare Queue Head -------------// + ehci_qhd_t * p_qhd; + + if ( ep_desc->bEndpointAddress == 0 ) + { + p_qhd = qhd_control(dev_addr); + }else + { + p_qhd = qhd_find_free(); + } + TU_ASSERT(p_qhd); + + qhd_init(p_qhd, dev_addr, ep_desc); + + // control of dev0 is always present as async head + if ( dev_addr == 0 ) return true; + + // Insert to list + ehci_link_t * list_head; + + switch (ep_desc->bmAttributes.xfer) + { + case TUSB_XFER_CONTROL: + case TUSB_XFER_BULK: + list_head = (ehci_link_t*) qhd_async_head(rhport); + break; + + case TUSB_XFER_INTERRUPT: + list_head = get_period_head(rhport, p_qhd->interval_ms); + break; + + case TUSB_XFER_ISOCHRONOUS: + // TODO iso is not supported + break; + + default: break; + } + + // TODO might need to disable async/period list + list_insert( list_head, (ehci_link_t*) p_qhd, EHCI_QTYPE_QHD); + + return true; +} + +bool hcd_pipe_queue_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes) +{ + //------------- set up QTD -------------// + ehci_qhd_t *p_qhd = qhd_get_from_addr(dev_addr, ep_addr); + ehci_qtd_t *p_qtd = qtd_find_free(); + + TU_ASSERT(p_qtd); + + qtd_init(p_qtd, buffer, total_bytes); + p_qtd->pid = p_qhd->pid; + + //------------- insert TD to TD list -------------// + qtd_insert_to_qhd(p_qhd, p_qtd); + + return true; +} + +bool hcd_pipe_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete) +{ + TU_ASSERT ( hcd_pipe_queue_xfer(dev_addr, ep_addr, buffer, total_bytes) ); + + ehci_qhd_t *p_qhd = qhd_get_from_addr(dev_addr, ep_addr); + + if ( int_on_complete ) + { // the just added qtd is pointed by list_tail + p_qhd->p_qtd_list_tail->int_on_complete = 1; + } + p_qhd->qtd_overlay.next.address = (uint32_t) p_qhd->p_qtd_list_head; // attach head QTD to QHD start transferring + + return true; +} + +bool hcd_edpt_busy(uint8_t dev_addr, uint8_t ep_addr) +{ + ehci_qhd_t *p_qhd = qhd_get_from_addr(dev_addr, ep_addr); + return !p_qhd->qtd_overlay.halted && (p_qhd->p_qtd_list_head != NULL); +} + +bool hcd_edpt_stalled(uint8_t dev_addr, uint8_t ep_addr) +{ + ehci_qhd_t *p_qhd = qhd_get_from_addr(dev_addr, ep_addr); + return p_qhd->qtd_overlay.halted && !qhd_has_xact_error(p_qhd); +} + +bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr) +{ + ehci_qhd_t *p_qhd = qhd_get_from_addr(dev_addr, ep_addr); + p_qhd->qtd_overlay.halted = 0; + // TODO reset data toggle ? + return true; +} + +//--------------------------------------------------------------------+ +// EHCI Interrupt Handler +//--------------------------------------------------------------------+ + +// async_advance is handshake between usb stack & ehci controller. +// This isr mean it is safe to modify previously removed queue head from async list. +// In tinyusb, queue head is only removed when device is unplugged. +static void async_advance_isr(uint8_t rhport) +{ + (void) rhport; + + ehci_qhd_t* qhd_pool = ehci_data.qhd_pool; + for(uint32_t i = 0; i < HCD_MAX_ENDPOINT; i++) + { + if ( qhd_pool[i].removing ) + { + qhd_pool[i].removing = 0; + qhd_pool[i].used = 0; + } + } +} + +static void port_connect_status_change_isr(uint8_t hostid) +{ + // NOTE There is an sequence plug->unplug->…..-> plug if device is powering with pre-plugged device + if (ehci_data.regs->portsc_bm.current_connect_status) + { + hcd_port_reset(hostid); + hcd_event_device_attach(hostid); + }else // device unplugged + { + hcd_event_device_remove(hostid); + } +} + +static void qhd_xfer_complete_isr(ehci_qhd_t * p_qhd) +{ + // free all TDs from the head td to the first active TD + while(p_qhd->p_qtd_list_head != NULL && !p_qhd->p_qtd_list_head->active) + { + // TD need to be freed and removed from qhd, before invoking callback + bool is_ioc = (p_qhd->p_qtd_list_head->int_on_complete != 0); + p_qhd->total_xferred_bytes += p_qhd->p_qtd_list_head->expected_bytes - p_qhd->p_qtd_list_head->total_bytes; + + p_qhd->p_qtd_list_head->used = 0; // free QTD + qtd_remove_1st_from_qhd(p_qhd); + + if (is_ioc) + { + // end of request + // call USBH callback + hcd_event_xfer_complete(p_qhd->dev_addr, tu_edpt_addr(p_qhd->ep_number, p_qhd->pid == EHCI_PID_IN ? 1 : 0), XFER_RESULT_SUCCESS, p_qhd->total_xferred_bytes); + p_qhd->total_xferred_bytes = 0; + } + } +} + +static void async_list_xfer_complete_isr(ehci_qhd_t * const async_head) +{ + ehci_qhd_t *p_qhd = async_head; + do + { + if ( !p_qhd->qtd_overlay.halted ) // halted or error is processed in error isr + { + qhd_xfer_complete_isr(p_qhd); + } + p_qhd = qhd_next(p_qhd); + }while(p_qhd != async_head); // async list traversal, stop if loop around +} + +static void period_list_xfer_complete_isr(uint8_t hostid, uint8_t interval_ms) +{ + uint8_t max_loop = 0; + uint32_t const period_1ms_addr = (uint32_t) get_period_head(hostid, 1); + ehci_link_t next_item = * get_period_head(hostid, interval_ms); + + // TODO abstract max loop guard for period + while( !next_item.terminate && + !(interval_ms > 1 && period_1ms_addr == tu_align32(next_item.address)) && + max_loop < (HCD_MAX_ENDPOINT + EHCI_MAX_ITD + EHCI_MAX_SITD)*CFG_TUSB_HOST_DEVICE_MAX) + { + switch ( next_item.type ) + { + case EHCI_QTYPE_QHD: + { + ehci_qhd_t *p_qhd_int = (ehci_qhd_t *) tu_align32(next_item.address); + if ( !p_qhd_int->qtd_overlay.halted ) + { + qhd_xfer_complete_isr(p_qhd_int); + } + } + break; + + case EHCI_QTYPE_ITD: // TODO support hs/fs ISO + case EHCI_QTYPE_SITD: + case EHCI_QTYPE_FSTN: + + default: break; + } + + next_item = *list_next(&next_item); + max_loop++; + } +} + +static void qhd_xfer_error_isr(ehci_qhd_t * p_qhd) +{ + if ( (p_qhd->dev_addr != 0 && p_qhd->qtd_overlay.halted) || // addr0 cannot be protocol STALL + qhd_has_xact_error(p_qhd) ) + { + // current qhd has error in transaction + xfer_result_t error_event; + + // no error bits are set, endpoint is halted due to STALL + error_event = qhd_has_xact_error(p_qhd) ? XFER_RESULT_FAILED : XFER_RESULT_STALLED; + + p_qhd->total_xferred_bytes += p_qhd->p_qtd_list_head->expected_bytes - p_qhd->p_qtd_list_head->total_bytes; + +// if ( XFER_RESULT_FAILED == error_event ) TU_BREAKPOINT(); // TODO skip unplugged device + + p_qhd->p_qtd_list_head->used = 0; // free QTD + qtd_remove_1st_from_qhd(p_qhd); + + if ( 0 == p_qhd->ep_number ) + { + // control cannot be halted --> clear all qtd list + p_qhd->p_qtd_list_head = NULL; + p_qhd->p_qtd_list_tail = NULL; + + p_qhd->qtd_overlay.next.terminate = 1; + p_qhd->qtd_overlay.alternate.terminate = 1; + p_qhd->qtd_overlay.halted = 0; + + ehci_qtd_t *p_setup = qtd_control(p_qhd->dev_addr); + p_setup->used = 0; + } + + // call USBH callback + hcd_event_xfer_complete(p_qhd->dev_addr, tu_edpt_addr(p_qhd->ep_number, p_qhd->pid == EHCI_PID_IN ? 1 : 0), error_event, p_qhd->total_xferred_bytes); + + p_qhd->total_xferred_bytes = 0; + } +} + +static void xfer_error_isr(uint8_t hostid) +{ + //------------- async list -------------// + ehci_qhd_t * const async_head = qhd_async_head(hostid); + ehci_qhd_t *p_qhd = async_head; + do + { + qhd_xfer_error_isr( p_qhd ); + p_qhd = qhd_next(p_qhd); + }while(p_qhd != async_head); // async list traversal, stop if loop around + + //------------- TODO refractor period list -------------// + uint32_t const period_1ms_addr = (uint32_t) get_period_head(hostid, 1); + for (uint8_t interval_ms=1; interval_ms <= EHCI_FRAMELIST_SIZE; interval_ms *= 2) + { + ehci_link_t next_item = * get_period_head(hostid, interval_ms); + + // TODO abstract max loop guard for period + while( !next_item.terminate && + !(interval_ms > 1 && period_1ms_addr == tu_align32(next_item.address)) ) + { + switch ( next_item.type ) + { + case EHCI_QTYPE_QHD: + { + ehci_qhd_t *p_qhd_int = (ehci_qhd_t *) tu_align32(next_item.address); + qhd_xfer_error_isr(p_qhd_int); + } + break; + + // TODO support hs/fs ISO + case EHCI_QTYPE_ITD: + case EHCI_QTYPE_SITD: + case EHCI_QTYPE_FSTN: + default: break; + } + + next_item = *list_next(&next_item); + } + } +} + +//------------- Host Controller Driver's Interrupt Handler -------------// +void hcd_isr(uint8_t rhport) +{ + ehci_registers_t* regs = ehci_data.regs; + + uint32_t int_status = regs->status; + int_status &= regs->inten; + + regs->status |= int_status; // Acknowledge handled interrupt + + if (int_status == 0) return; + + if (int_status & EHCI_INT_MASK_PORT_CHANGE) + { + uint32_t port_status = regs->portsc & EHCI_PORTSC_MASK_ALL; + + if (regs->portsc_bm.connect_status_change) + { + port_connect_status_change_isr(rhport); + } + + regs->portsc |= port_status; // Acknowledge change bits in portsc + } + + if (int_status & EHCI_INT_MASK_ERROR) + { + xfer_error_isr(rhport); + } + + //------------- some QTD/SITD/ITD with IOC set is completed -------------// + if (int_status & EHCI_INT_MASK_NXP_ASYNC) + { + async_list_xfer_complete_isr( qhd_async_head(rhport) ); + } + + if (int_status & EHCI_INT_MASK_NXP_PERIODIC) + { + for (uint8_t i=1; i <= EHCI_FRAMELIST_SIZE; i *= 2) + { + period_list_xfer_complete_isr( rhport, i ); + } + } + + //------------- There is some removed async previously -------------// + if (int_status & EHCI_INT_MASK_ASYNC_ADVANCE) // need to place after EHCI_INT_MASK_NXP_ASYNC + { + async_advance_isr(rhport); + } +} + +//--------------------------------------------------------------------+ +// HELPER +//--------------------------------------------------------------------+ + + +//------------- queue head helper -------------// +static inline ehci_qhd_t* qhd_find_free (void) +{ + for (uint32_t i=0; inext.address); +} + +static inline ehci_qhd_t* qhd_get_from_addr(uint8_t dev_addr, uint8_t ep_addr) +{ + ehci_qhd_t* qhd_pool = ehci_data.qhd_pool; + + for(uint32_t i=0; inext.address); +} + +static inline void qtd_remove_1st_from_qhd(ehci_qhd_t *p_qhd) +{ + if (p_qhd->p_qtd_list_head == p_qhd->p_qtd_list_tail) // last TD --> make it NULL + { + p_qhd->p_qtd_list_head = p_qhd->p_qtd_list_tail = NULL; + }else + { + p_qhd->p_qtd_list_head = qtd_next( p_qhd->p_qtd_list_head ); + } +} + +static inline void qtd_insert_to_qhd(ehci_qhd_t *p_qhd, ehci_qtd_t *p_qtd_new) +{ + if (p_qhd->p_qtd_list_head == NULL) // empty list + { + p_qhd->p_qtd_list_head = p_qhd->p_qtd_list_tail = p_qtd_new; + }else + { + p_qhd->p_qtd_list_tail->next.address = (uint32_t) p_qtd_new; + p_qhd->p_qtd_list_tail = p_qtd_new; + } +} + +static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc) +{ + // address 0 is used as async head, which always on the list --> cannot be cleared (ehci halted otherwise) + if (dev_addr != 0) + { + tu_memclr(p_qhd, sizeof(ehci_qhd_t)); + } + + uint8_t const xfer_type = ep_desc->bmAttributes.xfer; + uint8_t const interval = ep_desc->bInterval; + + p_qhd->dev_addr = dev_addr; + p_qhd->fl_inactive_next_xact = 0; + p_qhd->ep_number = tu_edpt_number(ep_desc->bEndpointAddress); + p_qhd->ep_speed = _usbh_devices[dev_addr].speed; + p_qhd->data_toggle_control= (xfer_type == TUSB_XFER_CONTROL) ? 1 : 0; + p_qhd->head_list_flag = (dev_addr == 0) ? 1 : 0; // addr0's endpoint is the static asyn list head + p_qhd->max_packet_size = ep_desc->wMaxPacketSize.size; + p_qhd->fl_ctrl_ep_flag = ((xfer_type == TUSB_XFER_CONTROL) && (p_qhd->ep_speed != TUSB_SPEED_HIGH)) ? 1 : 0; + p_qhd->nak_reload = 0; + + // Bulk/Control -> smask = cmask = 0 + // TODO Isochronous + if (TUSB_XFER_INTERRUPT == xfer_type) + { + if (TUSB_SPEED_HIGH == p_qhd->ep_speed) + { + TU_ASSERT( interval <= 16, ); + if ( interval < 4) // sub milisecond interval + { + p_qhd->interval_ms = 0; + p_qhd->int_smask = (interval == 1) ? TU_BIN8(11111111) : + (interval == 2) ? TU_BIN8(10101010) : TU_BIN8(01000100); + }else + { + p_qhd->interval_ms = (uint8_t) tu_min16( 1 << (interval-4), 255 ); + p_qhd->int_smask = TU_BIT(interval % 8); + } + }else + { + TU_ASSERT( 0 != interval, ); + // Full/Low: 4.12.2.1 (EHCI) case 1 schedule start split at 1 us & complete split at 2,3,4 uframes + p_qhd->int_smask = 0x01; + p_qhd->fl_int_cmask = TU_BIN8(11100); + p_qhd->interval_ms = interval; + } + }else + { + p_qhd->int_smask = p_qhd->fl_int_cmask = 0; + } + + p_qhd->fl_hub_addr = _usbh_devices[dev_addr].hub_addr; + p_qhd->fl_hub_port = _usbh_devices[dev_addr].hub_port; + p_qhd->mult = 1; // TODO not use high bandwidth/park mode yet + + //------------- HCD Management Data -------------// + p_qhd->used = 1; + p_qhd->removing = 0; + p_qhd->p_qtd_list_head = NULL; + p_qhd->p_qtd_list_tail = NULL; + p_qhd->pid = tu_edpt_dir(ep_desc->bEndpointAddress) ? EHCI_PID_IN : EHCI_PID_OUT; // PID for TD under this endpoint + + //------------- active, but no TD list -------------// + p_qhd->qtd_overlay.halted = 0; + p_qhd->qtd_overlay.next.terminate = 1; + p_qhd->qtd_overlay.alternate.terminate = 1; + if (TUSB_XFER_BULK == xfer_type && p_qhd->ep_speed == TUSB_SPEED_HIGH && p_qhd->pid == EHCI_PID_OUT) + { + p_qhd->qtd_overlay.ping_err = 1; // do PING for Highspeed Bulk OUT, EHCI section 4.11 + } +} + +static void qtd_init(ehci_qtd_t* p_qtd, void* buffer, uint16_t total_bytes) +{ + tu_memclr(p_qtd, sizeof(ehci_qtd_t)); + + p_qtd->used = 1; + + p_qtd->next.terminate = 1; // init to null + p_qtd->alternate.terminate = 1; // not used, always set to terminated + p_qtd->active = 1; + p_qtd->err_count = 3; // TODO 3 consecutive errors tolerance + p_qtd->data_toggle = 0; + p_qtd->total_bytes = total_bytes; + p_qtd->expected_bytes = total_bytes; + + p_qtd->buffer[0] = (uint32_t) buffer; + for(uint8_t i=1; i<5; i++) + { + p_qtd->buffer[i] |= tu_align4k( p_qtd->buffer[i-1] ) + 4096; + } +} + +//------------- List Managing Helper -------------// +static inline void list_insert(ehci_link_t *current, ehci_link_t *new, uint8_t new_type) +{ + new->address = current->address; + current->address = ((uint32_t) new) | (new_type << 1); +} + +static inline ehci_link_t* list_next(ehci_link_t *p_link_pointer) +{ + return (ehci_link_t*) tu_align32(p_link_pointer->address); +} + +#endif diff --git a/src/host/hcd.h b/src/host/hcd.h index 97dc3ebfd..a39e7fe16 100644 --- a/src/host/hcd.h +++ b/src/host/hcd.h @@ -1,147 +1,147 @@ -/* - * 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_usbh - * \defgroup Group_HCD Host Controller Driver (HCD) - * @{ */ - -#ifndef _TUSB_HCD_H_ -#define _TUSB_HCD_H_ - -#include - -#ifdef __cplusplus - extern "C" { -#endif - - //--------------------------------------------------------------------+ -// MACRO CONSTANT TYPEDEF -//--------------------------------------------------------------------+ -typedef enum -{ - HCD_EVENT_DEVICE_ATTACH, - HCD_EVENT_DEVICE_REMOVE, - HCD_EVENT_XFER_COMPLETE, -} hcd_eventid_t; - -typedef struct -{ - uint8_t rhport; - uint8_t event_id; - - union - { - struct - { - uint8_t hub_addr; - uint8_t hub_port; - } attach, remove; - - struct - { - uint8_t ep_addr; - uint8_t result; - uint32_t len; - } xfer_complete; - }; - -} hcd_event_t; - -#if TUSB_OPT_HOST_ENABLED -// Max number of endpoints per device -enum { - HCD_MAX_ENDPOINT = CFG_TUSB_HOST_DEVICE_MAX*(CFG_TUH_HUB + CFG_TUH_HID_KEYBOARD + CFG_TUH_HID_MOUSE + CFG_TUSB_HOST_HID_GENERIC + - CFG_TUH_MSC*2 + CFG_TUH_CDC*3), - - HCD_MAX_XFER = HCD_MAX_ENDPOINT*2, -}; - -//#define HCD_MAX_ENDPOINT 16 -//#define HCD_MAX_XFER 16 -#endif - -//--------------------------------------------------------------------+ -// HCD API -//--------------------------------------------------------------------+ -bool hcd_init(void); -void hcd_isr(uint8_t hostid); -void hcd_int_enable (uint8_t rhport); -void hcd_int_disable(uint8_t rhport); - -// PORT API -/// return the current connect status of roothub port -bool hcd_port_connect_status(uint8_t hostid); -void hcd_port_reset(uint8_t hostid); -tusb_speed_t hcd_port_speed_get(uint8_t hostid); - -// HCD closes all opened endpoints belong to this device -void hcd_device_close(uint8_t rhport, uint8_t dev_addr); - -//--------------------------------------------------------------------+ -// Event function -//--------------------------------------------------------------------+ -void hcd_event_handler(hcd_event_t const* event, bool in_isr); - -// Helper to send device attach event -void hcd_event_device_attach(uint8_t rhport); - -// Helper to send device removal event -void hcd_event_device_remove(uint8_t rhport); - -// Helper to send USB transfer event -void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); - -//--------------------------------------------------------------------+ -// Endpoints API -//--------------------------------------------------------------------+ -bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]); -bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc); - -bool hcd_edpt_busy(uint8_t dev_addr, uint8_t ep_addr); -bool hcd_edpt_stalled(uint8_t dev_addr, uint8_t ep_addr); -bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr); - -// TODO merge with pipe_xfer -bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen); - -//--------------------------------------------------------------------+ -// PIPE API -//--------------------------------------------------------------------+ -// TODO control xfer should be used via usbh layer -bool hcd_pipe_queue_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes); // only queue, not transferring yet -bool hcd_pipe_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete); - -#if 0 -tusb_error_t hcd_pipe_cancel(); -#endif - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_HCD_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_usbh + * \defgroup Group_HCD Host Controller Driver (HCD) + * @{ */ + +#ifndef _TUSB_HCD_H_ +#define _TUSB_HCD_H_ + +#include + +#ifdef __cplusplus + extern "C" { +#endif + + //--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +typedef enum +{ + HCD_EVENT_DEVICE_ATTACH, + HCD_EVENT_DEVICE_REMOVE, + HCD_EVENT_XFER_COMPLETE, +} hcd_eventid_t; + +typedef struct +{ + uint8_t rhport; + uint8_t event_id; + + union + { + struct + { + uint8_t hub_addr; + uint8_t hub_port; + } attach, remove; + + struct + { + uint8_t ep_addr; + uint8_t result; + uint32_t len; + } xfer_complete; + }; + +} hcd_event_t; + +#if TUSB_OPT_HOST_ENABLED +// Max number of endpoints per device +enum { + HCD_MAX_ENDPOINT = CFG_TUSB_HOST_DEVICE_MAX*(CFG_TUH_HUB + CFG_TUH_HID_KEYBOARD + CFG_TUH_HID_MOUSE + CFG_TUSB_HOST_HID_GENERIC + + CFG_TUH_MSC*2 + CFG_TUH_CDC*3), + + HCD_MAX_XFER = HCD_MAX_ENDPOINT*2, +}; + +//#define HCD_MAX_ENDPOINT 16 +//#define HCD_MAX_XFER 16 +#endif + +//--------------------------------------------------------------------+ +// HCD API +//--------------------------------------------------------------------+ +bool hcd_init(void); +void hcd_isr(uint8_t hostid); +void hcd_int_enable (uint8_t rhport); +void hcd_int_disable(uint8_t rhport); + +// PORT API +/// return the current connect status of roothub port +bool hcd_port_connect_status(uint8_t hostid); +void hcd_port_reset(uint8_t hostid); +tusb_speed_t hcd_port_speed_get(uint8_t hostid); + +// HCD closes all opened endpoints belong to this device +void hcd_device_close(uint8_t rhport, uint8_t dev_addr); + +//--------------------------------------------------------------------+ +// Event function +//--------------------------------------------------------------------+ +void hcd_event_handler(hcd_event_t const* event, bool in_isr); + +// Helper to send device attach event +void hcd_event_device_attach(uint8_t rhport); + +// Helper to send device removal event +void hcd_event_device_remove(uint8_t rhport); + +// Helper to send USB transfer event +void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); + +//--------------------------------------------------------------------+ +// Endpoints API +//--------------------------------------------------------------------+ +bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]); +bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc); + +bool hcd_edpt_busy(uint8_t dev_addr, uint8_t ep_addr); +bool hcd_edpt_stalled(uint8_t dev_addr, uint8_t ep_addr); +bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr); + +// TODO merge with pipe_xfer +bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen); + +//--------------------------------------------------------------------+ +// PIPE API +//--------------------------------------------------------------------+ +// TODO control xfer should be used via usbh layer +bool hcd_pipe_queue_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes); // only queue, not transferring yet +bool hcd_pipe_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete); + +#if 0 +tusb_error_t hcd_pipe_cancel(); +#endif + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_HCD_H_ */ + +/// @} diff --git a/src/host/hub.c b/src/host/hub.c index a2d4e8429..00450b704 100644 --- a/src/host/hub.c +++ b/src/host/hub.c @@ -1,250 +1,250 @@ -/* - * 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_HOST_ENABLED && CFG_TUH_HUB) - -//--------------------------------------------------------------------+ -// INCLUDE -//--------------------------------------------------------------------+ -#include "hub.h" - -//--------------------------------------------------------------------+ -// MACRO CONSTANT TYPEDEF -//--------------------------------------------------------------------+ -typedef struct -{ - uint8_t itf_num; - uint8_t ep_status; - uint8_t port_number; - uint8_t status_change; // data from status change interrupt endpoint -}usbh_hub_t; - -CFG_TUSB_MEM_SECTION static usbh_hub_t hub_data[CFG_TUSB_HOST_DEVICE_MAX]; -TU_ATTR_ALIGNED(4) CFG_TUSB_MEM_SECTION static uint8_t hub_enum_buffer[sizeof(descriptor_hub_desc_t)]; - -//OSAL_SEM_DEF(hub_enum_semaphore); -//static osal_semaphore_handle_t hub_enum_sem_hdl; - -//--------------------------------------------------------------------+ -// HUB -//--------------------------------------------------------------------+ -bool hub_port_clear_feature_subtask(uint8_t hub_addr, uint8_t hub_port, uint8_t feature) -{ - TU_ASSERT(HUB_FEATURE_PORT_CONNECTION_CHANGE <= feature && feature <= HUB_FEATURE_PORT_RESET_CHANGE); - - tusb_control_request_t request = { - .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_OTHER, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT }, - .bRequest = HUB_REQUEST_CLEAR_FEATURE, - .wValue = feature, - .wIndex = hub_port, - .wLength = 0 - }; - - //------------- Clear Port Feature request -------------// - TU_ASSERT( usbh_control_xfer( hub_addr, &request, NULL ) ); - - //------------- Get Port Status to check if feature is cleared -------------// - request = (tusb_control_request_t ) { - .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_OTHER, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_IN }, - .bRequest = HUB_REQUEST_GET_STATUS, - .wValue = 0, - .wIndex = hub_port, - .wLength = 4 - }; - - TU_ASSERT( usbh_control_xfer( hub_addr, &request, hub_enum_buffer ) ); - - //------------- Check if feature is cleared -------------// - hub_port_status_response_t * p_port_status; - p_port_status = (hub_port_status_response_t *) hub_enum_buffer; - - TU_ASSERT( !tu_bit_test(p_port_status->status_change.value, feature-16) ); - - return true; -} - -bool hub_port_reset_subtask(uint8_t hub_addr, uint8_t hub_port) -{ - enum { RESET_DELAY = 200 }; // USB specs say only 50ms but many devices require much longer - - //------------- Set Port Reset -------------// - tusb_control_request_t request = { - .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_OTHER, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT }, - .bRequest = HUB_REQUEST_SET_FEATURE, - .wValue = HUB_FEATURE_PORT_RESET, - .wIndex = hub_port, - .wLength = 0 - }; - - TU_ASSERT( usbh_control_xfer( hub_addr, &request, NULL ) ); - - osal_task_delay(RESET_DELAY); // TODO Hub wait for Status Endpoint on Reset Change - - //------------- Get Port Status to check if port is enabled, powered and reset_change -------------// - request = (tusb_control_request_t ) { - .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_OTHER, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_IN }, - .bRequest = HUB_REQUEST_GET_STATUS, - .wValue = 0, - .wIndex = hub_port, - .wLength = 4 - }; - - TU_ASSERT( usbh_control_xfer( hub_addr, &request, hub_enum_buffer ) ); - - hub_port_status_response_t * p_port_status; - p_port_status = (hub_port_status_response_t *) hub_enum_buffer; - - TU_ASSERT ( p_port_status->status_change.reset && p_port_status->status_current.connect_status && - p_port_status->status_current.port_power && p_port_status->status_current.port_enable); - - return true; -} - -// can only get the speed RIGHT AFTER hub_port_reset_subtask call -tusb_speed_t hub_port_get_speed(void) -{ - hub_port_status_response_t * p_port_status = (hub_port_status_response_t *) hub_enum_buffer; - return (p_port_status->status_current.high_speed_device_attached) ? TUSB_SPEED_HIGH : - (p_port_status->status_current.low_speed_device_attached ) ? TUSB_SPEED_LOW : TUSB_SPEED_FULL; -} - -//--------------------------------------------------------------------+ -// CLASS-USBH API (don't require to verify parameters) -//--------------------------------------------------------------------+ -void hub_init(void) -{ - tu_memclr(hub_data, CFG_TUSB_HOST_DEVICE_MAX*sizeof(usbh_hub_t)); -// hub_enum_sem_hdl = osal_semaphore_create( OSAL_SEM_REF(hub_enum_semaphore) ); -} - -bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length) -{ - // not support multiple TT yet - if ( itf_desc->bInterfaceProtocol > 1 ) return false; - - //------------- Open Interrupt Status Pipe -------------// - tusb_desc_endpoint_t const *ep_desc; - ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); - - TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType); - TU_ASSERT(TUSB_XFER_INTERRUPT == ep_desc->bmAttributes.xfer); - - TU_ASSERT(hcd_edpt_open(rhport, dev_addr, ep_desc)); - - hub_data[dev_addr-1].itf_num = itf_desc->bInterfaceNumber; - hub_data[dev_addr-1].ep_status = ep_desc->bEndpointAddress; - - (*p_length) = sizeof(tusb_desc_interface_t) + sizeof(tusb_desc_endpoint_t); - - //------------- Get Hub Descriptor -------------// - tusb_control_request_t request = { - .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_DEVICE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_IN }, - .bRequest = HUB_REQUEST_GET_DESCRIPTOR, - .wValue = 0, - .wIndex = 0, - .wLength = sizeof(descriptor_hub_desc_t) - }; - - TU_ASSERT( usbh_control_xfer( dev_addr, &request, hub_enum_buffer ) ); - - // only care about this field in hub descriptor - hub_data[dev_addr-1].port_number = ((descriptor_hub_desc_t*) hub_enum_buffer)->bNbrPorts; - - //------------- Set Port_Power on all ports -------------// - // TODO may only power port with attached - request = (tusb_control_request_t ) { - .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_OTHER, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT }, - .bRequest = HUB_REQUEST_SET_FEATURE, - .wValue = HUB_FEATURE_PORT_POWER, - .wIndex = 0, - .wLength = 0 - }; - - for(uint8_t i=1; i <= hub_data[dev_addr-1].port_number; i++) - { - request.wIndex = i; - TU_ASSERT( usbh_control_xfer( dev_addr, &request, NULL ) ); - } - - //------------- Queue the initial Status endpoint transfer -------------// - TU_ASSERT( hcd_pipe_xfer(dev_addr, hub_data[dev_addr-1].ep_status, &hub_data[dev_addr-1].status_change, 1, true) ); - - return true; -} - -// is the response of interrupt endpoint polling -#include "usbh_hcd.h" // FIXME remove -void hub_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) -{ - (void) xferred_bytes; // TODO can be more than 1 for hub with lots of ports - (void) ep_addr; - - usbh_hub_t * p_hub = &hub_data[dev_addr-1]; - - if ( event == XFER_RESULT_SUCCESS ) - { - for (uint8_t port=1; port <= p_hub->port_number; port++) - { - // TODO HUB ignore bit0 hub_status_change - if ( tu_bit_test(p_hub->status_change, port) ) - { - hcd_event_t event = - { - .rhport = _usbh_devices[dev_addr].rhport, - .event_id = HCD_EVENT_DEVICE_ATTACH - }; - - event.attach.hub_addr = dev_addr; - event.attach.hub_port = port; - - hcd_event_handler(&event, true); - break; // handle one port at a time, next port if any will be handled in the next cycle - } - } - // NOTE: next status transfer is queued by usbh.c after handling this request - } - else - { - // TODO [HUB] check if hub is still plugged before polling status endpoint since failed usually mean hub unplugged -// TU_ASSERT ( hub_status_pipe_queue(dev_addr) ); - } -} - -void hub_close(uint8_t dev_addr) -{ - tu_memclr(&hub_data[dev_addr-1], sizeof(usbh_hub_t)); -// osal_semaphore_reset(hub_enum_sem_hdl); -} - -bool hub_status_pipe_queue(uint8_t dev_addr) -{ - return hcd_pipe_xfer(dev_addr, hub_data[dev_addr-1].ep_status, &hub_data[dev_addr-1].status_change, 1, true); -} - - -#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_HOST_ENABLED && CFG_TUH_HUB) + +//--------------------------------------------------------------------+ +// INCLUDE +//--------------------------------------------------------------------+ +#include "hub.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +typedef struct +{ + uint8_t itf_num; + uint8_t ep_status; + uint8_t port_number; + uint8_t status_change; // data from status change interrupt endpoint +}usbh_hub_t; + +CFG_TUSB_MEM_SECTION static usbh_hub_t hub_data[CFG_TUSB_HOST_DEVICE_MAX]; +TU_ATTR_ALIGNED(4) CFG_TUSB_MEM_SECTION static uint8_t hub_enum_buffer[sizeof(descriptor_hub_desc_t)]; + +//OSAL_SEM_DEF(hub_enum_semaphore); +//static osal_semaphore_handle_t hub_enum_sem_hdl; + +//--------------------------------------------------------------------+ +// HUB +//--------------------------------------------------------------------+ +bool hub_port_clear_feature_subtask(uint8_t hub_addr, uint8_t hub_port, uint8_t feature) +{ + TU_ASSERT(HUB_FEATURE_PORT_CONNECTION_CHANGE <= feature && feature <= HUB_FEATURE_PORT_RESET_CHANGE); + + tusb_control_request_t request = { + .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_OTHER, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT }, + .bRequest = HUB_REQUEST_CLEAR_FEATURE, + .wValue = feature, + .wIndex = hub_port, + .wLength = 0 + }; + + //------------- Clear Port Feature request -------------// + TU_ASSERT( usbh_control_xfer( hub_addr, &request, NULL ) ); + + //------------- Get Port Status to check if feature is cleared -------------// + request = (tusb_control_request_t ) { + .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_OTHER, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_IN }, + .bRequest = HUB_REQUEST_GET_STATUS, + .wValue = 0, + .wIndex = hub_port, + .wLength = 4 + }; + + TU_ASSERT( usbh_control_xfer( hub_addr, &request, hub_enum_buffer ) ); + + //------------- Check if feature is cleared -------------// + hub_port_status_response_t * p_port_status; + p_port_status = (hub_port_status_response_t *) hub_enum_buffer; + + TU_ASSERT( !tu_bit_test(p_port_status->status_change.value, feature-16) ); + + return true; +} + +bool hub_port_reset_subtask(uint8_t hub_addr, uint8_t hub_port) +{ + enum { RESET_DELAY = 200 }; // USB specs say only 50ms but many devices require much longer + + //------------- Set Port Reset -------------// + tusb_control_request_t request = { + .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_OTHER, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT }, + .bRequest = HUB_REQUEST_SET_FEATURE, + .wValue = HUB_FEATURE_PORT_RESET, + .wIndex = hub_port, + .wLength = 0 + }; + + TU_ASSERT( usbh_control_xfer( hub_addr, &request, NULL ) ); + + osal_task_delay(RESET_DELAY); // TODO Hub wait for Status Endpoint on Reset Change + + //------------- Get Port Status to check if port is enabled, powered and reset_change -------------// + request = (tusb_control_request_t ) { + .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_OTHER, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_IN }, + .bRequest = HUB_REQUEST_GET_STATUS, + .wValue = 0, + .wIndex = hub_port, + .wLength = 4 + }; + + TU_ASSERT( usbh_control_xfer( hub_addr, &request, hub_enum_buffer ) ); + + hub_port_status_response_t * p_port_status; + p_port_status = (hub_port_status_response_t *) hub_enum_buffer; + + TU_ASSERT ( p_port_status->status_change.reset && p_port_status->status_current.connect_status && + p_port_status->status_current.port_power && p_port_status->status_current.port_enable); + + return true; +} + +// can only get the speed RIGHT AFTER hub_port_reset_subtask call +tusb_speed_t hub_port_get_speed(void) +{ + hub_port_status_response_t * p_port_status = (hub_port_status_response_t *) hub_enum_buffer; + return (p_port_status->status_current.high_speed_device_attached) ? TUSB_SPEED_HIGH : + (p_port_status->status_current.low_speed_device_attached ) ? TUSB_SPEED_LOW : TUSB_SPEED_FULL; +} + +//--------------------------------------------------------------------+ +// CLASS-USBH API (don't require to verify parameters) +//--------------------------------------------------------------------+ +void hub_init(void) +{ + tu_memclr(hub_data, CFG_TUSB_HOST_DEVICE_MAX*sizeof(usbh_hub_t)); +// hub_enum_sem_hdl = osal_semaphore_create( OSAL_SEM_REF(hub_enum_semaphore) ); +} + +bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length) +{ + // not support multiple TT yet + if ( itf_desc->bInterfaceProtocol > 1 ) return false; + + //------------- Open Interrupt Status Pipe -------------// + tusb_desc_endpoint_t const *ep_desc; + ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); + + TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType); + TU_ASSERT(TUSB_XFER_INTERRUPT == ep_desc->bmAttributes.xfer); + + TU_ASSERT(hcd_edpt_open(rhport, dev_addr, ep_desc)); + + hub_data[dev_addr-1].itf_num = itf_desc->bInterfaceNumber; + hub_data[dev_addr-1].ep_status = ep_desc->bEndpointAddress; + + (*p_length) = sizeof(tusb_desc_interface_t) + sizeof(tusb_desc_endpoint_t); + + //------------- Get Hub Descriptor -------------// + tusb_control_request_t request = { + .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_DEVICE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_IN }, + .bRequest = HUB_REQUEST_GET_DESCRIPTOR, + .wValue = 0, + .wIndex = 0, + .wLength = sizeof(descriptor_hub_desc_t) + }; + + TU_ASSERT( usbh_control_xfer( dev_addr, &request, hub_enum_buffer ) ); + + // only care about this field in hub descriptor + hub_data[dev_addr-1].port_number = ((descriptor_hub_desc_t*) hub_enum_buffer)->bNbrPorts; + + //------------- Set Port_Power on all ports -------------// + // TODO may only power port with attached + request = (tusb_control_request_t ) { + .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_OTHER, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT }, + .bRequest = HUB_REQUEST_SET_FEATURE, + .wValue = HUB_FEATURE_PORT_POWER, + .wIndex = 0, + .wLength = 0 + }; + + for(uint8_t i=1; i <= hub_data[dev_addr-1].port_number; i++) + { + request.wIndex = i; + TU_ASSERT( usbh_control_xfer( dev_addr, &request, NULL ) ); + } + + //------------- Queue the initial Status endpoint transfer -------------// + TU_ASSERT( hcd_pipe_xfer(dev_addr, hub_data[dev_addr-1].ep_status, &hub_data[dev_addr-1].status_change, 1, true) ); + + return true; +} + +// is the response of interrupt endpoint polling +#include "usbh_hcd.h" // FIXME remove +void hub_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) +{ + (void) xferred_bytes; // TODO can be more than 1 for hub with lots of ports + (void) ep_addr; + + usbh_hub_t * p_hub = &hub_data[dev_addr-1]; + + if ( event == XFER_RESULT_SUCCESS ) + { + for (uint8_t port=1; port <= p_hub->port_number; port++) + { + // TODO HUB ignore bit0 hub_status_change + if ( tu_bit_test(p_hub->status_change, port) ) + { + hcd_event_t event = + { + .rhport = _usbh_devices[dev_addr].rhport, + .event_id = HCD_EVENT_DEVICE_ATTACH + }; + + event.attach.hub_addr = dev_addr; + event.attach.hub_port = port; + + hcd_event_handler(&event, true); + break; // handle one port at a time, next port if any will be handled in the next cycle + } + } + // NOTE: next status transfer is queued by usbh.c after handling this request + } + else + { + // TODO [HUB] check if hub is still plugged before polling status endpoint since failed usually mean hub unplugged +// TU_ASSERT ( hub_status_pipe_queue(dev_addr) ); + } +} + +void hub_close(uint8_t dev_addr) +{ + tu_memclr(&hub_data[dev_addr-1], sizeof(usbh_hub_t)); +// osal_semaphore_reset(hub_enum_sem_hdl); +} + +bool hub_status_pipe_queue(uint8_t dev_addr) +{ + return hcd_pipe_xfer(dev_addr, hub_data[dev_addr-1].ep_status, &hub_data[dev_addr-1].status_change, 1, true); +} + + +#endif diff --git a/src/host/hub.h b/src/host/hub.h index 51d33d4e5..621356315 100644 --- a/src/host/hub.h +++ b/src/host/hub.h @@ -1,194 +1,194 @@ -/* - * 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_class - * \defgroup ClassDriver_Hub Hub (Host only) - * \details Like most PC's OS, Hub support is completely hidden from Application. In fact, application cannot determine whether - * a device is mounted directly via roothub or via a hub's port. All Hub-related procedures are performed and managed - * by tinyusb stack. Unless you are trying to develop the stack itself, there are nothing else can be used by Application. - * \note Due to my laziness, only 1-level of Hub is supported. In other way, the stack cannot mount a hub via another hub. - * @{ - */ - -#ifndef _TUSB_HUB_H_ -#define _TUSB_HUB_H_ - -#include -#include "usbh.h" - -#ifdef __cplusplus - extern "C" { -#endif - -//D1...D0: Logical Power Switching Mode -//00: Ganged power switching (all ports’power at -//once) -//01: Individual port power switching -//1X: Reserved. Used only on 1.0 compliant hubs -//that implement no power switching -//D2: Identifies a Compound Device -//0: Hub is not part of a compound device. -//1: Hub is part of a compound device. -//D4...D3: Over-current Protection Mode -//00: Global Over-current Protection. The hub -//reports over-current as a summation of all -//ports’current draw, without a breakdown of -//individual port over-current status. -//01: Individual Port Over-current Protection. The -//hub reports over-current on a per-port basis. -//Each port has an over-current status. -//1X: No Over-current Protection. This option is -//allowed only for bus-powered hubs that do not -//implement over-current protection. -// -//D6...D5: TT Think TIme -//00: TT requires at most 8 FS bit times of inter -//transaction gap on a full-/low-speed -//downstream bus. -//01: TT requires at most 16 FS bit times. -//10: TT requires at most 24 FS bit times. -//11: TT requires at most 32 FS bit times. -//D7: Port Indicators Supported -//0: Port Indicators are not supported on its -//downstream facing ports and the -//PORT_INDICATOR request has no effect. -//1: Port Indicators are supported on its -//downstream facing ports and the -//PORT_INDICATOR request controls the -//indicators. See Section 11.5.3. -//D15...D8: Reserved - -typedef struct TU_ATTR_PACKED{ - uint8_t bLength ; ///< Size of descriptor - uint8_t bDescriptorType ; ///< Other_speed_Configuration Type - uint8_t bNbrPorts; - uint16_t wHubCharacteristics; - uint8_t bPwrOn2PwrGood; - uint8_t bHubContrCurrent; - uint8_t DeviceRemovable; // bitmap each bit for a port (from bit1) - uint8_t PortPwrCtrlMask; // just for compatibility, should be 0xff -} descriptor_hub_desc_t; - -TU_VERIFY_STATIC( sizeof(descriptor_hub_desc_t) == 9, "size is not correct"); - -enum { - HUB_REQUEST_GET_STATUS = 0 , - HUB_REQUEST_CLEAR_FEATURE = 1 , - - HUB_REQUEST_SET_FEATURE = 3 , - - HUB_REQUEST_GET_DESCRIPTOR = 6 , - HUB_REQUEST_SET_DESCRIPTOR = 7 , - HUB_REQUEST_CLEAR_TT_BUFFER = 8 , - HUB_REQUEST_RESET_TT = 9 , - HUB_REQUEST_GET_TT_STATE = 10 , - HUB_REQUEST_STOP_TT = 11 -}; - -enum { - HUB_FEATURE_HUB_LOCAL_POWER_CHANGE = 0, - HUB_FEATURE_HUB_OVER_CURRENT_CHANGE -}; - -enum{ - HUB_FEATURE_PORT_CONNECTION = 0, - HUB_FEATURE_PORT_ENABLE = 1, - HUB_FEATURE_PORT_SUSPEND = 2, - HUB_FEATURE_PORT_OVER_CURRENT = 3, - HUB_FEATURE_PORT_RESET = 4, - - HUB_FEATURE_PORT_POWER = 8, - HUB_FEATURE_PORT_LOW_SPEED = 9, - - HUB_FEATURE_PORT_CONNECTION_CHANGE = 16, - HUB_FEATURE_PORT_ENABLE_CHANGE = 17, - HUB_FEATURE_PORT_SUSPEND_CHANGE = 18, - HUB_FEATURE_PORT_OVER_CURRENT_CHANGE = 19, - HUB_FEATURE_PORT_RESET_CHANGE = 20, - HUB_FEATURE_PORT_TEST = 21, - HUB_FEATURE_PORT_INDICATOR = 22 -}; - -// data in response of HUB_REQUEST_GET_STATUS, wIndex = 0 (hub) -typedef struct { - union{ - struct TU_ATTR_PACKED { - uint16_t local_power_source : 1; - uint16_t over_current : 1; - uint16_t : 14; - }; - - uint16_t value; - } status, status_change; -} hub_status_response_t; - -TU_VERIFY_STATIC( sizeof(hub_status_response_t) == 4, "size is not correct"); - -// data in response of HUB_REQUEST_GET_STATUS, wIndex = Port num -typedef struct { - union { - struct TU_ATTR_PACKED { - uint16_t connect_status : 1; - uint16_t port_enable : 1; - uint16_t suspend : 1; - uint16_t over_current : 1; - uint16_t reset : 1; - - uint16_t : 3; - uint16_t port_power : 1; - uint16_t low_speed_device_attached : 1; - uint16_t high_speed_device_attached : 1; - uint16_t port_test_mode : 1; - uint16_t port_indicator_control : 1; - uint16_t : 0; - }; - - uint16_t value; - } status_current, status_change; -} hub_port_status_response_t; - -TU_VERIFY_STATIC( sizeof(hub_port_status_response_t) == 4, "size is not correct"); - -bool hub_port_reset_subtask(uint8_t hub_addr, uint8_t hub_port); -bool hub_port_clear_feature_subtask(uint8_t hub_addr, uint8_t hub_port, uint8_t feature); -tusb_speed_t hub_port_get_speed(void); -bool hub_status_pipe_queue(uint8_t dev_addr); - -//--------------------------------------------------------------------+ -// Internal Class Driver API -//--------------------------------------------------------------------+ -void hub_init(void); -bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length); -void hub_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); -void hub_close(uint8_t dev_addr); - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_HUB_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_class + * \defgroup ClassDriver_Hub Hub (Host only) + * \details Like most PC's OS, Hub support is completely hidden from Application. In fact, application cannot determine whether + * a device is mounted directly via roothub or via a hub's port. All Hub-related procedures are performed and managed + * by tinyusb stack. Unless you are trying to develop the stack itself, there are nothing else can be used by Application. + * \note Due to my laziness, only 1-level of Hub is supported. In other way, the stack cannot mount a hub via another hub. + * @{ + */ + +#ifndef _TUSB_HUB_H_ +#define _TUSB_HUB_H_ + +#include +#include "usbh.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//D1...D0: Logical Power Switching Mode +//00: Ganged power switching (all ports’power at +//once) +//01: Individual port power switching +//1X: Reserved. Used only on 1.0 compliant hubs +//that implement no power switching +//D2: Identifies a Compound Device +//0: Hub is not part of a compound device. +//1: Hub is part of a compound device. +//D4...D3: Over-current Protection Mode +//00: Global Over-current Protection. The hub +//reports over-current as a summation of all +//ports’current draw, without a breakdown of +//individual port over-current status. +//01: Individual Port Over-current Protection. The +//hub reports over-current on a per-port basis. +//Each port has an over-current status. +//1X: No Over-current Protection. This option is +//allowed only for bus-powered hubs that do not +//implement over-current protection. +// +//D6...D5: TT Think TIme +//00: TT requires at most 8 FS bit times of inter +//transaction gap on a full-/low-speed +//downstream bus. +//01: TT requires at most 16 FS bit times. +//10: TT requires at most 24 FS bit times. +//11: TT requires at most 32 FS bit times. +//D7: Port Indicators Supported +//0: Port Indicators are not supported on its +//downstream facing ports and the +//PORT_INDICATOR request has no effect. +//1: Port Indicators are supported on its +//downstream facing ports and the +//PORT_INDICATOR request controls the +//indicators. See Section 11.5.3. +//D15...D8: Reserved + +typedef struct TU_ATTR_PACKED{ + uint8_t bLength ; ///< Size of descriptor + uint8_t bDescriptorType ; ///< Other_speed_Configuration Type + uint8_t bNbrPorts; + uint16_t wHubCharacteristics; + uint8_t bPwrOn2PwrGood; + uint8_t bHubContrCurrent; + uint8_t DeviceRemovable; // bitmap each bit for a port (from bit1) + uint8_t PortPwrCtrlMask; // just for compatibility, should be 0xff +} descriptor_hub_desc_t; + +TU_VERIFY_STATIC( sizeof(descriptor_hub_desc_t) == 9, "size is not correct"); + +enum { + HUB_REQUEST_GET_STATUS = 0 , + HUB_REQUEST_CLEAR_FEATURE = 1 , + + HUB_REQUEST_SET_FEATURE = 3 , + + HUB_REQUEST_GET_DESCRIPTOR = 6 , + HUB_REQUEST_SET_DESCRIPTOR = 7 , + HUB_REQUEST_CLEAR_TT_BUFFER = 8 , + HUB_REQUEST_RESET_TT = 9 , + HUB_REQUEST_GET_TT_STATE = 10 , + HUB_REQUEST_STOP_TT = 11 +}; + +enum { + HUB_FEATURE_HUB_LOCAL_POWER_CHANGE = 0, + HUB_FEATURE_HUB_OVER_CURRENT_CHANGE +}; + +enum{ + HUB_FEATURE_PORT_CONNECTION = 0, + HUB_FEATURE_PORT_ENABLE = 1, + HUB_FEATURE_PORT_SUSPEND = 2, + HUB_FEATURE_PORT_OVER_CURRENT = 3, + HUB_FEATURE_PORT_RESET = 4, + + HUB_FEATURE_PORT_POWER = 8, + HUB_FEATURE_PORT_LOW_SPEED = 9, + + HUB_FEATURE_PORT_CONNECTION_CHANGE = 16, + HUB_FEATURE_PORT_ENABLE_CHANGE = 17, + HUB_FEATURE_PORT_SUSPEND_CHANGE = 18, + HUB_FEATURE_PORT_OVER_CURRENT_CHANGE = 19, + HUB_FEATURE_PORT_RESET_CHANGE = 20, + HUB_FEATURE_PORT_TEST = 21, + HUB_FEATURE_PORT_INDICATOR = 22 +}; + +// data in response of HUB_REQUEST_GET_STATUS, wIndex = 0 (hub) +typedef struct { + union{ + struct TU_ATTR_PACKED { + uint16_t local_power_source : 1; + uint16_t over_current : 1; + uint16_t : 14; + }; + + uint16_t value; + } status, status_change; +} hub_status_response_t; + +TU_VERIFY_STATIC( sizeof(hub_status_response_t) == 4, "size is not correct"); + +// data in response of HUB_REQUEST_GET_STATUS, wIndex = Port num +typedef struct { + union { + struct TU_ATTR_PACKED { + uint16_t connect_status : 1; + uint16_t port_enable : 1; + uint16_t suspend : 1; + uint16_t over_current : 1; + uint16_t reset : 1; + + uint16_t : 3; + uint16_t port_power : 1; + uint16_t low_speed_device_attached : 1; + uint16_t high_speed_device_attached : 1; + uint16_t port_test_mode : 1; + uint16_t port_indicator_control : 1; + uint16_t : 0; + }; + + uint16_t value; + } status_current, status_change; +} hub_port_status_response_t; + +TU_VERIFY_STATIC( sizeof(hub_port_status_response_t) == 4, "size is not correct"); + +bool hub_port_reset_subtask(uint8_t hub_addr, uint8_t hub_port); +bool hub_port_clear_feature_subtask(uint8_t hub_addr, uint8_t hub_port, uint8_t feature); +tusb_speed_t hub_port_get_speed(void); +bool hub_status_pipe_queue(uint8_t dev_addr); + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +void hub_init(void); +bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length); +void hub_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); +void hub_close(uint8_t dev_addr); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_HUB_H_ */ + +/** @} */ diff --git a/src/host/ohci/ohci.c b/src/host/ohci/ohci.c index 9fa861ac9..547622edf 100644 --- a/src/host/ohci/ohci.c +++ b/src/host/ohci/ohci.c @@ -1,651 +1,651 @@ -/* - * 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 - -#if TUSB_OPT_HOST_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC40XX) -//--------------------------------------------------------------------+ -// INCLUDE -//--------------------------------------------------------------------+ -#include "osal/osal.h" - -#include "../hcd.h" -#include "../usbh_hcd.h" -#include "ohci.h" - -// TODO remove -#include "chip.h" - -//--------------------------------------------------------------------+ -// MACRO CONSTANT TYPEDEF -//--------------------------------------------------------------------+ -#define OHCI_REG ((ohci_registers_t *) LPC_USB_BASE) - -enum { - OHCI_CONTROL_FUNCSTATE_RESET = 0, - OHCI_CONTROL_FUNCSTATE_RESUME, - OHCI_CONTROL_FUNCSTATE_OPERATIONAL, - OHCI_CONTROL_FUNCSTATE_SUSPEND -}; - -enum { - OHCI_CONTROL_CONTROL_BULK_RATIO = 3, ///< This specifies the service ratio between Control and Bulk EDs. 0 = 1:1, 3 = 4:1 - OHCI_CONTROL_LIST_PERIODIC_ENABLE_MASK = TU_BIT(2), - OHCI_CONTROL_LIST_ISOCHRONOUS_ENABLE_MASK = TU_BIT(3), - OHCI_CONTROL_LIST_CONTROL_ENABLE_MASK = TU_BIT(4), - OHCI_CONTROL_LIST_BULK_ENABLE_MASK = TU_BIT(5), -}; - -enum { - OHCI_FMINTERVAL_FI = 0x2EDF, // 7.3.1 nominal (reset) value - OHCI_FMINTERVAL_FSMPS = (6*(OHCI_FMINTERVAL_FI-210)) / 7, // 5.4 calculated based on maximum overhead + bit stuffing -}; - -enum { - OHCI_PERIODIC_START = 0x3E67 -}; - -enum { - OHCI_INT_SCHEDULING_OVERUN_MASK = TU_BIT(0), - OHCI_INT_WRITEBACK_DONEHEAD_MASK = TU_BIT(1), - OHCI_INT_SOF_MASK = TU_BIT(2), - OHCI_INT_RESUME_DETECTED_MASK = TU_BIT(3), - OHCI_INT_UNRECOVERABLE_ERROR_MASK = TU_BIT(4), - OHCI_INT_FRAME_OVERFLOW_MASK = TU_BIT(5), - OHCI_INT_RHPORT_STATUS_CHANGE_MASK = TU_BIT(6), - - OHCI_INT_OWNERSHIP_CHANGE_MASK = TU_BIT(30), - OHCI_INT_MASTER_ENABLE_MASK = TU_BIT(31), -}; - -enum { - OHCI_RHPORT_CURRENT_CONNECT_STATUS_MASK = TU_BIT(0), - OHCI_RHPORT_PORT_ENABLE_STATUS_MASK = TU_BIT(1), - OHCI_RHPORT_PORT_SUSPEND_STATUS_MASK = TU_BIT(2), - OHCI_RHPORT_PORT_OVER_CURRENT_INDICATOR_MASK = TU_BIT(3), - OHCI_RHPORT_PORT_RESET_STATUS_MASK = TU_BIT(4), ///< write '1' to reset port - - OHCI_RHPORT_PORT_POWER_STATUS_MASK = TU_BIT(8), - OHCI_RHPORT_LOW_SPEED_DEVICE_ATTACHED_MASK = TU_BIT(9), - - OHCI_RHPORT_CONNECT_STATUS_CHANGE_MASK = TU_BIT(16), - OHCI_RHPORT_PORT_ENABLE_CHANGE_MASK = TU_BIT(17), - OHCI_RHPORT_PORT_SUSPEND_CHANGE_MASK = TU_BIT(18), - OHCI_RHPORT_OVER_CURRENT_CHANGE_MASK = TU_BIT(19), - OHCI_RHPORT_PORT_RESET_CHANGE_MASK = TU_BIT(20), - - OHCI_RHPORT_ALL_CHANGE_MASK = OHCI_RHPORT_CONNECT_STATUS_CHANGE_MASK | OHCI_RHPORT_PORT_ENABLE_CHANGE_MASK | - OHCI_RHPORT_PORT_SUSPEND_CHANGE_MASK | OHCI_RHPORT_OVER_CURRENT_CHANGE_MASK | OHCI_RHPORT_PORT_RESET_CHANGE_MASK -}; - -enum { - OHCI_CCODE_NO_ERROR = 0, - OHCI_CCODE_CRC = 1, - OHCI_CCODE_BIT_STUFFING = 2, - OHCI_CCODE_DATA_TOGGLE_MISMATCH = 3, - OHCI_CCODE_STALL = 4, - OHCI_CCODE_DEVICE_NOT_RESPONDING = 5, - OHCI_CCODE_PID_CHECK_FAILURE = 6, - OHCI_CCODE_UNEXPECTED_PID = 7, - OHCI_CCODE_DATA_OVERRUN = 8, - OHCI_CCODE_DATA_UNDERRUN = 9, - OHCI_CCODE_BUFFER_OVERRUN = 12, - OHCI_CCODE_BUFFER_UNDERRUN = 13, - OHCI_CCODE_NOT_ACCESSED = 14, -}; - -enum { - OHCI_INT_ON_COMPLETE_YES = 0, - OHCI_INT_ON_COMPLETE_NO = TU_BIN8(111) -}; -//--------------------------------------------------------------------+ -// INTERNAL OBJECT & FUNCTION DECLARATION -//--------------------------------------------------------------------+ -CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(256) static ohci_data_t ohci_data; - -static ohci_ed_t * const p_ed_head[] = -{ - [TUSB_XFER_CONTROL] = &ohci_data.control[0].ed, - [TUSB_XFER_BULK ] = &ohci_data.bulk_head_ed, - [TUSB_XFER_INTERRUPT] = &ohci_data.period_head_ed, - [TUSB_XFER_ISOCHRONOUS] = NULL // TODO Isochronous -}; - -static void ed_list_insert(ohci_ed_t * p_pre, ohci_ed_t * p_ed); -static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr); - -//--------------------------------------------------------------------+ -// USBH-HCD API -//--------------------------------------------------------------------+ -// Initialization according to 5.1.1.4 -bool hcd_init(void) -{ - //------------- Data Structure init -------------// - tu_memclr(&ohci_data, sizeof(ohci_data_t)); - for(uint8_t i=0; i<32; i++) - { // assign all interrupt pointes to period head ed - ohci_data.hcca.interrupt_table[i] = (uint32_t) &ohci_data.period_head_ed; - } - - ohci_data.control[0].ed.skip = 1; - ohci_data.bulk_head_ed.skip = 1; - ohci_data.period_head_ed.skip = 1; - - // reset controller - OHCI_REG->command_status_bit.controller_reset = 1; - while( OHCI_REG->command_status_bit.controller_reset ) {} // should not take longer than 10 us - - //------------- init ohci registers -------------// - OHCI_REG->control_head_ed = (uint32_t) &ohci_data.control[0].ed; - OHCI_REG->bulk_head_ed = (uint32_t) &ohci_data.bulk_head_ed; - OHCI_REG->hcca = (uint32_t) &ohci_data.hcca; - - OHCI_REG->interrupt_disable = OHCI_REG->interrupt_enable; // disable all interrupts - OHCI_REG->interrupt_status = OHCI_REG->interrupt_status; // clear current set bits - OHCI_REG->interrupt_enable = OHCI_INT_WRITEBACK_DONEHEAD_MASK | OHCI_INT_RESUME_DETECTED_MASK | - OHCI_INT_UNRECOVERABLE_ERROR_MASK | /*OHCI_INT_FRAME_OVERFLOW_MASK |*/ OHCI_INT_RHPORT_STATUS_CHANGE_MASK | - OHCI_INT_MASTER_ENABLE_MASK; - - OHCI_REG->control |= OHCI_CONTROL_CONTROL_BULK_RATIO | OHCI_CONTROL_LIST_CONTROL_ENABLE_MASK | - OHCI_CONTROL_LIST_BULK_ENABLE_MASK | OHCI_CONTROL_LIST_PERIODIC_ENABLE_MASK; // TODO Isochronous - - OHCI_REG->frame_interval = (OHCI_FMINTERVAL_FSMPS << 16) | OHCI_FMINTERVAL_FI; - OHCI_REG->periodic_start = (OHCI_FMINTERVAL_FI * 9) / 10; // Periodic start is 90% of frame interval - - OHCI_REG->control_bit.hc_functional_state = OHCI_CONTROL_FUNCSTATE_OPERATIONAL; // make HC's state to operational state TODO use this to suspend (save power) - OHCI_REG->rh_status_bit.local_power_status_change = 1; // set global power for ports - - return true; -} - -//--------------------------------------------------------------------+ -// PORT API -//--------------------------------------------------------------------+ -void hcd_port_reset(uint8_t hostid) -{ - (void) hostid; - OHCI_REG->rhport_status[0] = OHCI_RHPORT_PORT_RESET_STATUS_MASK; -} - -bool hcd_port_connect_status(uint8_t hostid) -{ - (void) hostid; - return OHCI_REG->rhport_status_bit[0].current_connect_status; -} - -tusb_speed_t hcd_port_speed_get(uint8_t hostid) -{ - (void) hostid; - return OHCI_REG->rhport_status_bit[0].low_speed_device_attached ? TUSB_SPEED_LOW : TUSB_SPEED_FULL; -} - -// endpoints are tied to an address, which only reclaim after a long delay when enumerating -// thus there is no need to make sure ED is not in HC's cahed as it will not for sure -void hcd_device_close(uint8_t rhport, uint8_t dev_addr) -{ - // TODO OHCI - (void) rhport; - - // addr0 serves as static head --> only set skip bit - if ( dev_addr == 0 ) - { - ohci_data.control[0].ed.skip = 1; - }else - { - // remove control - ed_list_remove_by_addr( p_ed_head[TUSB_XFER_CONTROL], dev_addr); - - // remove bulk - ed_list_remove_by_addr(p_ed_head[TUSB_XFER_BULK], dev_addr); - - // remove interrupt - ed_list_remove_by_addr(p_ed_head[TUSB_XFER_INTERRUPT], dev_addr); - - // TODO remove ISO - } -} - -//--------------------------------------------------------------------+ -// Controller API -//--------------------------------------------------------------------+ - -//--------------------------------------------------------------------+ -// CONTROL PIPE API -//--------------------------------------------------------------------+ -static inline tusb_xfer_type_t ed_get_xfer_type(ohci_ed_t const * const p_ed) -{ - return (p_ed->ep_number == 0 ) ? TUSB_XFER_CONTROL : - (p_ed->is_iso ) ? TUSB_XFER_ISOCHRONOUS : - (p_ed->is_interrupt_xfer ) ? TUSB_XFER_INTERRUPT : TUSB_XFER_BULK; -} - -static void ed_init(ohci_ed_t *p_ed, uint8_t dev_addr, uint16_t max_packet_size, uint8_t endpoint_addr, uint8_t xfer_type, uint8_t interval) -{ - (void) interval; - - // address 0 is used as async head, which always on the list --> cannot be cleared - if (dev_addr != 0) - { - tu_memclr(p_ed, sizeof(ohci_ed_t)); - } - - p_ed->dev_addr = dev_addr; - p_ed->ep_number = endpoint_addr & 0x0F; - p_ed->pid = (xfer_type == TUSB_XFER_CONTROL) ? OHCI_PID_SETUP : ( (endpoint_addr & TUSB_DIR_IN_MASK) ? OHCI_PID_IN : OHCI_PID_OUT ); - p_ed->speed = _usbh_devices[dev_addr].speed; - p_ed->is_iso = (xfer_type == TUSB_XFER_ISOCHRONOUS) ? 1 : 0; - p_ed->max_packet_size = max_packet_size; - - p_ed->used = 1; - p_ed->is_interrupt_xfer = (xfer_type == TUSB_XFER_INTERRUPT ? 1 : 0); -} - -static void gtd_init(ohci_gtd_t* p_td, void* data_ptr, uint16_t total_bytes) -{ - tu_memclr(p_td, sizeof(ohci_gtd_t)); - - p_td->used = 1; - p_td->expected_bytes = total_bytes; - - p_td->buffer_rounding = 1; // less than queued length is not a error - p_td->delay_interrupt = OHCI_INT_ON_COMPLETE_NO; - p_td->condition_code = OHCI_CCODE_NOT_ACCESSED; - - p_td->current_buffer_pointer = data_ptr; - p_td->buffer_end = total_bytes ? (((uint8_t*) data_ptr) + total_bytes-1) : NULL; -} - -bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) -{ - (void) rhport; - - ohci_ed_t* p_ed = &ohci_data.control[dev_addr].ed; - ohci_gtd_t *p_setup = &ohci_data.control[dev_addr].gtd; - - gtd_init(p_setup, (void*) setup_packet, 8); - p_setup->index = dev_addr; - p_setup->pid = OHCI_PID_SETUP; - p_setup->data_toggle = TU_BIN8(10); // DATA0 - p_setup->delay_interrupt = OHCI_INT_ON_COMPLETE_YES; - - //------------- Attach TDs list to Control Endpoint -------------// - p_ed->td_head.address = (uint32_t) p_setup; - - OHCI_REG->command_status_bit.control_list_filled = 1; - - return true; -} - -bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) -{ - (void) rhport; - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - // FIXME control only for now - if ( epnum == 0 ) - { - ohci_ed_t* const p_ed = &ohci_data.control[dev_addr].ed; - ohci_gtd_t *p_data = &ohci_data.control[dev_addr].gtd; - - gtd_init(p_data, buffer, buflen); - - p_data->index = dev_addr; - p_data->pid = dir ? OHCI_PID_IN : OHCI_PID_OUT; - p_data->data_toggle = TU_BIN8(11); // DATA1 - p_data->delay_interrupt = OHCI_INT_ON_COMPLETE_YES; - - p_ed->td_head.address = (uint32_t) p_data; - - OHCI_REG->command_status_bit.control_list_filled = 1; - } - - return true; -} - -//--------------------------------------------------------------------+ -// BULK/INT/ISO PIPE API -//--------------------------------------------------------------------+ -static inline ohci_ed_t * ed_from_addr(uint8_t dev_addr, uint8_t ep_addr) -{ - if ( tu_edpt_number(ep_addr) == 0 ) return &ohci_data.control[dev_addr].ed; - - ohci_ed_t* ed_pool = ohci_data.ed_pool; - - for(uint32_t i=0; inext = p_pre->next; - p_pre->next = (uint32_t) p_ed; -} - -static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr) -{ - ohci_ed_t* p_prev = p_head; - - while( p_prev->next ) - { - ohci_ed_t* ed = (ohci_ed_t*) p_prev->next; - - if (ed->dev_addr == dev_addr) - { - // unlink ed - p_prev->next = ed->next; - - // point the removed ED's next pointer to list head to make sure HC can always safely move away from this ED - ed->next = (uint32_t) p_head; - ed->used = 0; - } - - // check next valid since we could remove it - if (p_prev->next) p_prev = (ohci_ed_t*) p_prev->next; - } -} - -bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc) -{ - (void) rhport; - - // TODO iso support - TU_ASSERT(ep_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS); - - //------------- Prepare Queue Head -------------// - ohci_ed_t * p_ed; - - if ( ep_desc->bEndpointAddress == 0 ) - { - p_ed = &ohci_data.control[dev_addr].ed; - }else - { - p_ed = ed_find_free(); - } - TU_ASSERT(p_ed); - - ed_init( p_ed, dev_addr, ep_desc->wMaxPacketSize.size, ep_desc->bEndpointAddress, - ep_desc->bmAttributes.xfer, ep_desc->bInterval ); - - // control of dev0 is used as static async head - if ( dev_addr == 0 ) - { - p_ed->skip = 0; // only need to clear skip bit - return true; - } - - ed_list_insert( p_ed_head[ep_desc->bmAttributes.xfer], p_ed ); - - return true; -} - -static ohci_gtd_t * gtd_find_free(void) -{ - for(uint8_t i=0; i < HCD_MAX_XFER; i++) - { - if ( !ohci_data.gtd_pool[i].used ) return &ohci_data.gtd_pool[i]; - } - - return NULL; -} - -static void td_insert_to_ed(ohci_ed_t* p_ed, ohci_gtd_t * p_gtd) -{ - // tail is always NULL - if ( tu_align16(p_ed->td_head.address) == 0 ) - { // TD queue is empty --> head = TD - p_ed->td_head.address |= (uint32_t) p_gtd; - } - else - { // TODO currently only support queue up to 2 TD each endpoint at a time - ((ohci_gtd_t*) tu_align16(p_ed->td_head.address))->next = (uint32_t) p_gtd; - } -} - -static bool pipe_queue_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete) -{ - ohci_ed_t* const p_ed = ed_from_addr(dev_addr, ep_addr); - - // not support ISO yet - TU_VERIFY ( !p_ed->is_iso ); - - ohci_gtd_t * const p_gtd = gtd_find_free(); - TU_ASSERT(p_gtd); // not enough gtd - - gtd_init(p_gtd, buffer, total_bytes); - p_gtd->index = p_ed-ohci_data.ed_pool; - - if ( int_on_complete ) p_gtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES; - - td_insert_to_ed(p_ed, p_gtd); - - return true; -} - -bool hcd_pipe_queue_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes) -{ - return pipe_queue_xfer(dev_addr, ep_addr, buffer, total_bytes, false); -} - -bool hcd_pipe_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete) -{ - (void) int_on_complete; - TU_ASSERT( pipe_queue_xfer(dev_addr, ep_addr, buffer, total_bytes, true) ); - - tusb_xfer_type_t xfer_type = ed_get_xfer_type( ed_from_addr(dev_addr, ep_addr) ); - - if (TUSB_XFER_BULK == xfer_type) OHCI_REG->command_status_bit.bulk_list_filled = 1; - - return true; -} - -bool hcd_edpt_busy(uint8_t dev_addr, uint8_t ep_addr) -{ - ohci_ed_t const * const p_ed = ed_from_addr(dev_addr, ep_addr); - return tu_align16(p_ed->td_head.address) != tu_align16(p_ed->td_tail); -} - -bool hcd_edpt_stalled(uint8_t dev_addr, uint8_t ep_addr) -{ - ohci_ed_t const * const p_ed = ed_from_addr(dev_addr, ep_addr); - return p_ed->td_head.halted && p_ed->is_stalled; -} - -bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr) -{ - ohci_ed_t * const p_ed = ed_from_addr(dev_addr, ep_addr); - - p_ed->is_stalled = 0; - p_ed->td_tail &= 0x0Ful; // set tail pointer back to NULL - - p_ed->td_head.toggle = 0; // reset data toggle - p_ed->td_head.halted = 0; - - if ( TUSB_XFER_BULK == ed_get_xfer_type(p_ed) ) OHCI_REG->command_status_bit.bulk_list_filled = 1; - - return true; -} - - -//--------------------------------------------------------------------+ -// OHCI Interrupt Handler -//--------------------------------------------------------------------+ -static ohci_td_item_t* list_reverse(ohci_td_item_t* td_head) -{ - ohci_td_item_t* td_reverse_head = NULL; - - while(td_head != NULL) - { - uint32_t next = td_head->next; - - // make current's item become reverse's first item - td_head->next = (uint32_t) td_reverse_head; - td_reverse_head = td_head; - - td_head = (ohci_td_item_t*) next; // advance to next item - } - - return td_reverse_head; -} - -static inline bool gtd_is_control(ohci_gtd_t const * const p_qtd) -{ - return ((uint32_t) p_qtd) < ((uint32_t) ohci_data.gtd_pool); // check ohci_data_t for memory layout -} - -static inline ohci_ed_t* gtd_get_ed(ohci_gtd_t const * const p_qtd) -{ - if ( gtd_is_control(p_qtd) ) - { - return &ohci_data.control[p_qtd->index].ed; - }else - { - return &ohci_data.ed_pool[p_qtd->index]; - } -} - -static inline uint32_t gtd_xfer_byte_left(uint32_t buffer_end, uint32_t current_buffer) -{ // 5.2.9 OHCI sample code - return (tu_align4k(buffer_end ^ current_buffer) ? 0x1000 : 0) + - tu_offset4k(buffer_end) - tu_offset4k(current_buffer) + 1; -} - -static void done_queue_isr(uint8_t hostid) -{ - (void) hostid; - - // done head is written in reversed order of completion --> need to reverse the done queue first - ohci_td_item_t* td_head = list_reverse ( (ohci_td_item_t*) tu_align16(ohci_data.hcca.done_head) ); - - while( td_head != NULL ) - { - // TODO check if td_head is iso td - //------------- Non ISO transfer -------------// - ohci_gtd_t * const p_qtd = (ohci_gtd_t *) td_head; - xfer_result_t const event = (p_qtd->condition_code == OHCI_CCODE_NO_ERROR) ? XFER_RESULT_SUCCESS : - (p_qtd->condition_code == OHCI_CCODE_STALL) ? XFER_RESULT_STALLED : XFER_RESULT_FAILED; - - p_qtd->used = 0; // free TD - if ( (p_qtd->delay_interrupt == OHCI_INT_ON_COMPLETE_YES) || (event != XFER_RESULT_SUCCESS) ) - { - ohci_ed_t * const p_ed = gtd_get_ed(p_qtd); - - uint32_t const xferred_bytes = p_qtd->expected_bytes - gtd_xfer_byte_left((uint32_t) p_qtd->buffer_end, (uint32_t) p_qtd->current_buffer_pointer); - - // NOTE Assuming the current list is BULK and there is no other EDs in the list has queued TDs. - // When there is a error resulting this ED is halted, and this EP still has other queued TD - // --> the Bulk list only has this halted EP queueing TDs (remaining) - // --> Bulk list will be considered as not empty by HC !!! while there is no attempt transaction on this list - // --> HC will not process Control list (due to service ratio when Bulk list not empty) - // To walk-around this, the halted ED will have TailP = HeadP (empty list condition), when clearing halt - // the TailP must be set back to NULL for processing remaining TDs - if ((event != XFER_RESULT_SUCCESS)) - { - p_ed->td_tail &= 0x0Ful; - p_ed->td_tail |= tu_align16(p_ed->td_head.address); // mark halted EP as empty queue - if ( event == XFER_RESULT_STALLED ) p_ed->is_stalled = 1; - } - - hcd_event_xfer_complete(p_ed->dev_addr, - tu_edpt_addr(p_ed->ep_number, p_ed->pid == OHCI_PID_IN), - event, xferred_bytes); - } - - td_head = (ohci_td_item_t*) td_head->next; - } -} - -void hcd_isr(uint8_t hostid) -{ - uint32_t const int_en = OHCI_REG->interrupt_enable; - uint32_t const int_status = OHCI_REG->interrupt_status & int_en; - - if (int_status == 0) return; - - //------------- RootHub status -------------// - if ( int_status & OHCI_INT_RHPORT_STATUS_CHANGE_MASK ) - { - uint32_t const rhport_status = OHCI_REG->rhport_status[0] & OHCI_RHPORT_ALL_CHANGE_MASK; - - // TODO dual port is not yet supported - if ( rhport_status & OHCI_RHPORT_CONNECT_STATUS_CHANGE_MASK ) - { - // TODO check if remote wake-up - if ( OHCI_REG->rhport_status_bit[0].current_connect_status ) - { - // TODO reset port immediately, without this controller will got 2-3 (debouncing connection status change) - OHCI_REG->rhport_status[0] = OHCI_RHPORT_PORT_RESET_STATUS_MASK; - hcd_event_device_attach(0); - }else - { - hcd_event_device_remove(0); - } - } - - if ( rhport_status & OHCI_RHPORT_PORT_SUSPEND_CHANGE_MASK) - { - - } - - OHCI_REG->rhport_status[0] = rhport_status; // acknowledge all interrupt - } - - //------------- Transfer Complete -------------// - if ( int_status & OHCI_INT_WRITEBACK_DONEHEAD_MASK) - { - done_queue_isr(hostid); - } - - OHCI_REG->interrupt_status = int_status; // Acknowledge handled interrupt -} -//--------------------------------------------------------------------+ -// HELPER -//--------------------------------------------------------------------+ - - -#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 + +#if TUSB_OPT_HOST_ENABLED && (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC40XX) +//--------------------------------------------------------------------+ +// INCLUDE +//--------------------------------------------------------------------+ +#include "osal/osal.h" + +#include "../hcd.h" +#include "../usbh_hcd.h" +#include "ohci.h" + +// TODO remove +#include "chip.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +#define OHCI_REG ((ohci_registers_t *) LPC_USB_BASE) + +enum { + OHCI_CONTROL_FUNCSTATE_RESET = 0, + OHCI_CONTROL_FUNCSTATE_RESUME, + OHCI_CONTROL_FUNCSTATE_OPERATIONAL, + OHCI_CONTROL_FUNCSTATE_SUSPEND +}; + +enum { + OHCI_CONTROL_CONTROL_BULK_RATIO = 3, ///< This specifies the service ratio between Control and Bulk EDs. 0 = 1:1, 3 = 4:1 + OHCI_CONTROL_LIST_PERIODIC_ENABLE_MASK = TU_BIT(2), + OHCI_CONTROL_LIST_ISOCHRONOUS_ENABLE_MASK = TU_BIT(3), + OHCI_CONTROL_LIST_CONTROL_ENABLE_MASK = TU_BIT(4), + OHCI_CONTROL_LIST_BULK_ENABLE_MASK = TU_BIT(5), +}; + +enum { + OHCI_FMINTERVAL_FI = 0x2EDF, // 7.3.1 nominal (reset) value + OHCI_FMINTERVAL_FSMPS = (6*(OHCI_FMINTERVAL_FI-210)) / 7, // 5.4 calculated based on maximum overhead + bit stuffing +}; + +enum { + OHCI_PERIODIC_START = 0x3E67 +}; + +enum { + OHCI_INT_SCHEDULING_OVERUN_MASK = TU_BIT(0), + OHCI_INT_WRITEBACK_DONEHEAD_MASK = TU_BIT(1), + OHCI_INT_SOF_MASK = TU_BIT(2), + OHCI_INT_RESUME_DETECTED_MASK = TU_BIT(3), + OHCI_INT_UNRECOVERABLE_ERROR_MASK = TU_BIT(4), + OHCI_INT_FRAME_OVERFLOW_MASK = TU_BIT(5), + OHCI_INT_RHPORT_STATUS_CHANGE_MASK = TU_BIT(6), + + OHCI_INT_OWNERSHIP_CHANGE_MASK = TU_BIT(30), + OHCI_INT_MASTER_ENABLE_MASK = TU_BIT(31), +}; + +enum { + OHCI_RHPORT_CURRENT_CONNECT_STATUS_MASK = TU_BIT(0), + OHCI_RHPORT_PORT_ENABLE_STATUS_MASK = TU_BIT(1), + OHCI_RHPORT_PORT_SUSPEND_STATUS_MASK = TU_BIT(2), + OHCI_RHPORT_PORT_OVER_CURRENT_INDICATOR_MASK = TU_BIT(3), + OHCI_RHPORT_PORT_RESET_STATUS_MASK = TU_BIT(4), ///< write '1' to reset port + + OHCI_RHPORT_PORT_POWER_STATUS_MASK = TU_BIT(8), + OHCI_RHPORT_LOW_SPEED_DEVICE_ATTACHED_MASK = TU_BIT(9), + + OHCI_RHPORT_CONNECT_STATUS_CHANGE_MASK = TU_BIT(16), + OHCI_RHPORT_PORT_ENABLE_CHANGE_MASK = TU_BIT(17), + OHCI_RHPORT_PORT_SUSPEND_CHANGE_MASK = TU_BIT(18), + OHCI_RHPORT_OVER_CURRENT_CHANGE_MASK = TU_BIT(19), + OHCI_RHPORT_PORT_RESET_CHANGE_MASK = TU_BIT(20), + + OHCI_RHPORT_ALL_CHANGE_MASK = OHCI_RHPORT_CONNECT_STATUS_CHANGE_MASK | OHCI_RHPORT_PORT_ENABLE_CHANGE_MASK | + OHCI_RHPORT_PORT_SUSPEND_CHANGE_MASK | OHCI_RHPORT_OVER_CURRENT_CHANGE_MASK | OHCI_RHPORT_PORT_RESET_CHANGE_MASK +}; + +enum { + OHCI_CCODE_NO_ERROR = 0, + OHCI_CCODE_CRC = 1, + OHCI_CCODE_BIT_STUFFING = 2, + OHCI_CCODE_DATA_TOGGLE_MISMATCH = 3, + OHCI_CCODE_STALL = 4, + OHCI_CCODE_DEVICE_NOT_RESPONDING = 5, + OHCI_CCODE_PID_CHECK_FAILURE = 6, + OHCI_CCODE_UNEXPECTED_PID = 7, + OHCI_CCODE_DATA_OVERRUN = 8, + OHCI_CCODE_DATA_UNDERRUN = 9, + OHCI_CCODE_BUFFER_OVERRUN = 12, + OHCI_CCODE_BUFFER_UNDERRUN = 13, + OHCI_CCODE_NOT_ACCESSED = 14, +}; + +enum { + OHCI_INT_ON_COMPLETE_YES = 0, + OHCI_INT_ON_COMPLETE_NO = TU_BIN8(111) +}; +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ +CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(256) static ohci_data_t ohci_data; + +static ohci_ed_t * const p_ed_head[] = +{ + [TUSB_XFER_CONTROL] = &ohci_data.control[0].ed, + [TUSB_XFER_BULK ] = &ohci_data.bulk_head_ed, + [TUSB_XFER_INTERRUPT] = &ohci_data.period_head_ed, + [TUSB_XFER_ISOCHRONOUS] = NULL // TODO Isochronous +}; + +static void ed_list_insert(ohci_ed_t * p_pre, ohci_ed_t * p_ed); +static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr); + +//--------------------------------------------------------------------+ +// USBH-HCD API +//--------------------------------------------------------------------+ +// Initialization according to 5.1.1.4 +bool hcd_init(void) +{ + //------------- Data Structure init -------------// + tu_memclr(&ohci_data, sizeof(ohci_data_t)); + for(uint8_t i=0; i<32; i++) + { // assign all interrupt pointes to period head ed + ohci_data.hcca.interrupt_table[i] = (uint32_t) &ohci_data.period_head_ed; + } + + ohci_data.control[0].ed.skip = 1; + ohci_data.bulk_head_ed.skip = 1; + ohci_data.period_head_ed.skip = 1; + + // reset controller + OHCI_REG->command_status_bit.controller_reset = 1; + while( OHCI_REG->command_status_bit.controller_reset ) {} // should not take longer than 10 us + + //------------- init ohci registers -------------// + OHCI_REG->control_head_ed = (uint32_t) &ohci_data.control[0].ed; + OHCI_REG->bulk_head_ed = (uint32_t) &ohci_data.bulk_head_ed; + OHCI_REG->hcca = (uint32_t) &ohci_data.hcca; + + OHCI_REG->interrupt_disable = OHCI_REG->interrupt_enable; // disable all interrupts + OHCI_REG->interrupt_status = OHCI_REG->interrupt_status; // clear current set bits + OHCI_REG->interrupt_enable = OHCI_INT_WRITEBACK_DONEHEAD_MASK | OHCI_INT_RESUME_DETECTED_MASK | + OHCI_INT_UNRECOVERABLE_ERROR_MASK | /*OHCI_INT_FRAME_OVERFLOW_MASK |*/ OHCI_INT_RHPORT_STATUS_CHANGE_MASK | + OHCI_INT_MASTER_ENABLE_MASK; + + OHCI_REG->control |= OHCI_CONTROL_CONTROL_BULK_RATIO | OHCI_CONTROL_LIST_CONTROL_ENABLE_MASK | + OHCI_CONTROL_LIST_BULK_ENABLE_MASK | OHCI_CONTROL_LIST_PERIODIC_ENABLE_MASK; // TODO Isochronous + + OHCI_REG->frame_interval = (OHCI_FMINTERVAL_FSMPS << 16) | OHCI_FMINTERVAL_FI; + OHCI_REG->periodic_start = (OHCI_FMINTERVAL_FI * 9) / 10; // Periodic start is 90% of frame interval + + OHCI_REG->control_bit.hc_functional_state = OHCI_CONTROL_FUNCSTATE_OPERATIONAL; // make HC's state to operational state TODO use this to suspend (save power) + OHCI_REG->rh_status_bit.local_power_status_change = 1; // set global power for ports + + return true; +} + +//--------------------------------------------------------------------+ +// PORT API +//--------------------------------------------------------------------+ +void hcd_port_reset(uint8_t hostid) +{ + (void) hostid; + OHCI_REG->rhport_status[0] = OHCI_RHPORT_PORT_RESET_STATUS_MASK; +} + +bool hcd_port_connect_status(uint8_t hostid) +{ + (void) hostid; + return OHCI_REG->rhport_status_bit[0].current_connect_status; +} + +tusb_speed_t hcd_port_speed_get(uint8_t hostid) +{ + (void) hostid; + return OHCI_REG->rhport_status_bit[0].low_speed_device_attached ? TUSB_SPEED_LOW : TUSB_SPEED_FULL; +} + +// endpoints are tied to an address, which only reclaim after a long delay when enumerating +// thus there is no need to make sure ED is not in HC's cahed as it will not for sure +void hcd_device_close(uint8_t rhport, uint8_t dev_addr) +{ + // TODO OHCI + (void) rhport; + + // addr0 serves as static head --> only set skip bit + if ( dev_addr == 0 ) + { + ohci_data.control[0].ed.skip = 1; + }else + { + // remove control + ed_list_remove_by_addr( p_ed_head[TUSB_XFER_CONTROL], dev_addr); + + // remove bulk + ed_list_remove_by_addr(p_ed_head[TUSB_XFER_BULK], dev_addr); + + // remove interrupt + ed_list_remove_by_addr(p_ed_head[TUSB_XFER_INTERRUPT], dev_addr); + + // TODO remove ISO + } +} + +//--------------------------------------------------------------------+ +// Controller API +//--------------------------------------------------------------------+ + +//--------------------------------------------------------------------+ +// CONTROL PIPE API +//--------------------------------------------------------------------+ +static inline tusb_xfer_type_t ed_get_xfer_type(ohci_ed_t const * const p_ed) +{ + return (p_ed->ep_number == 0 ) ? TUSB_XFER_CONTROL : + (p_ed->is_iso ) ? TUSB_XFER_ISOCHRONOUS : + (p_ed->is_interrupt_xfer ) ? TUSB_XFER_INTERRUPT : TUSB_XFER_BULK; +} + +static void ed_init(ohci_ed_t *p_ed, uint8_t dev_addr, uint16_t max_packet_size, uint8_t endpoint_addr, uint8_t xfer_type, uint8_t interval) +{ + (void) interval; + + // address 0 is used as async head, which always on the list --> cannot be cleared + if (dev_addr != 0) + { + tu_memclr(p_ed, sizeof(ohci_ed_t)); + } + + p_ed->dev_addr = dev_addr; + p_ed->ep_number = endpoint_addr & 0x0F; + p_ed->pid = (xfer_type == TUSB_XFER_CONTROL) ? OHCI_PID_SETUP : ( (endpoint_addr & TUSB_DIR_IN_MASK) ? OHCI_PID_IN : OHCI_PID_OUT ); + p_ed->speed = _usbh_devices[dev_addr].speed; + p_ed->is_iso = (xfer_type == TUSB_XFER_ISOCHRONOUS) ? 1 : 0; + p_ed->max_packet_size = max_packet_size; + + p_ed->used = 1; + p_ed->is_interrupt_xfer = (xfer_type == TUSB_XFER_INTERRUPT ? 1 : 0); +} + +static void gtd_init(ohci_gtd_t* p_td, void* data_ptr, uint16_t total_bytes) +{ + tu_memclr(p_td, sizeof(ohci_gtd_t)); + + p_td->used = 1; + p_td->expected_bytes = total_bytes; + + p_td->buffer_rounding = 1; // less than queued length is not a error + p_td->delay_interrupt = OHCI_INT_ON_COMPLETE_NO; + p_td->condition_code = OHCI_CCODE_NOT_ACCESSED; + + p_td->current_buffer_pointer = data_ptr; + p_td->buffer_end = total_bytes ? (((uint8_t*) data_ptr) + total_bytes-1) : NULL; +} + +bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) +{ + (void) rhport; + + ohci_ed_t* p_ed = &ohci_data.control[dev_addr].ed; + ohci_gtd_t *p_setup = &ohci_data.control[dev_addr].gtd; + + gtd_init(p_setup, (void*) setup_packet, 8); + p_setup->index = dev_addr; + p_setup->pid = OHCI_PID_SETUP; + p_setup->data_toggle = TU_BIN8(10); // DATA0 + p_setup->delay_interrupt = OHCI_INT_ON_COMPLETE_YES; + + //------------- Attach TDs list to Control Endpoint -------------// + p_ed->td_head.address = (uint32_t) p_setup; + + OHCI_REG->command_status_bit.control_list_filled = 1; + + return true; +} + +bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) +{ + (void) rhport; + + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + // FIXME control only for now + if ( epnum == 0 ) + { + ohci_ed_t* const p_ed = &ohci_data.control[dev_addr].ed; + ohci_gtd_t *p_data = &ohci_data.control[dev_addr].gtd; + + gtd_init(p_data, buffer, buflen); + + p_data->index = dev_addr; + p_data->pid = dir ? OHCI_PID_IN : OHCI_PID_OUT; + p_data->data_toggle = TU_BIN8(11); // DATA1 + p_data->delay_interrupt = OHCI_INT_ON_COMPLETE_YES; + + p_ed->td_head.address = (uint32_t) p_data; + + OHCI_REG->command_status_bit.control_list_filled = 1; + } + + return true; +} + +//--------------------------------------------------------------------+ +// BULK/INT/ISO PIPE API +//--------------------------------------------------------------------+ +static inline ohci_ed_t * ed_from_addr(uint8_t dev_addr, uint8_t ep_addr) +{ + if ( tu_edpt_number(ep_addr) == 0 ) return &ohci_data.control[dev_addr].ed; + + ohci_ed_t* ed_pool = ohci_data.ed_pool; + + for(uint32_t i=0; inext = p_pre->next; + p_pre->next = (uint32_t) p_ed; +} + +static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr) +{ + ohci_ed_t* p_prev = p_head; + + while( p_prev->next ) + { + ohci_ed_t* ed = (ohci_ed_t*) p_prev->next; + + if (ed->dev_addr == dev_addr) + { + // unlink ed + p_prev->next = ed->next; + + // point the removed ED's next pointer to list head to make sure HC can always safely move away from this ED + ed->next = (uint32_t) p_head; + ed->used = 0; + } + + // check next valid since we could remove it + if (p_prev->next) p_prev = (ohci_ed_t*) p_prev->next; + } +} + +bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc) +{ + (void) rhport; + + // TODO iso support + TU_ASSERT(ep_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS); + + //------------- Prepare Queue Head -------------// + ohci_ed_t * p_ed; + + if ( ep_desc->bEndpointAddress == 0 ) + { + p_ed = &ohci_data.control[dev_addr].ed; + }else + { + p_ed = ed_find_free(); + } + TU_ASSERT(p_ed); + + ed_init( p_ed, dev_addr, ep_desc->wMaxPacketSize.size, ep_desc->bEndpointAddress, + ep_desc->bmAttributes.xfer, ep_desc->bInterval ); + + // control of dev0 is used as static async head + if ( dev_addr == 0 ) + { + p_ed->skip = 0; // only need to clear skip bit + return true; + } + + ed_list_insert( p_ed_head[ep_desc->bmAttributes.xfer], p_ed ); + + return true; +} + +static ohci_gtd_t * gtd_find_free(void) +{ + for(uint8_t i=0; i < HCD_MAX_XFER; i++) + { + if ( !ohci_data.gtd_pool[i].used ) return &ohci_data.gtd_pool[i]; + } + + return NULL; +} + +static void td_insert_to_ed(ohci_ed_t* p_ed, ohci_gtd_t * p_gtd) +{ + // tail is always NULL + if ( tu_align16(p_ed->td_head.address) == 0 ) + { // TD queue is empty --> head = TD + p_ed->td_head.address |= (uint32_t) p_gtd; + } + else + { // TODO currently only support queue up to 2 TD each endpoint at a time + ((ohci_gtd_t*) tu_align16(p_ed->td_head.address))->next = (uint32_t) p_gtd; + } +} + +static bool pipe_queue_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete) +{ + ohci_ed_t* const p_ed = ed_from_addr(dev_addr, ep_addr); + + // not support ISO yet + TU_VERIFY ( !p_ed->is_iso ); + + ohci_gtd_t * const p_gtd = gtd_find_free(); + TU_ASSERT(p_gtd); // not enough gtd + + gtd_init(p_gtd, buffer, total_bytes); + p_gtd->index = p_ed-ohci_data.ed_pool; + + if ( int_on_complete ) p_gtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES; + + td_insert_to_ed(p_ed, p_gtd); + + return true; +} + +bool hcd_pipe_queue_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes) +{ + return pipe_queue_xfer(dev_addr, ep_addr, buffer, total_bytes, false); +} + +bool hcd_pipe_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete) +{ + (void) int_on_complete; + TU_ASSERT( pipe_queue_xfer(dev_addr, ep_addr, buffer, total_bytes, true) ); + + tusb_xfer_type_t xfer_type = ed_get_xfer_type( ed_from_addr(dev_addr, ep_addr) ); + + if (TUSB_XFER_BULK == xfer_type) OHCI_REG->command_status_bit.bulk_list_filled = 1; + + return true; +} + +bool hcd_edpt_busy(uint8_t dev_addr, uint8_t ep_addr) +{ + ohci_ed_t const * const p_ed = ed_from_addr(dev_addr, ep_addr); + return tu_align16(p_ed->td_head.address) != tu_align16(p_ed->td_tail); +} + +bool hcd_edpt_stalled(uint8_t dev_addr, uint8_t ep_addr) +{ + ohci_ed_t const * const p_ed = ed_from_addr(dev_addr, ep_addr); + return p_ed->td_head.halted && p_ed->is_stalled; +} + +bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr) +{ + ohci_ed_t * const p_ed = ed_from_addr(dev_addr, ep_addr); + + p_ed->is_stalled = 0; + p_ed->td_tail &= 0x0Ful; // set tail pointer back to NULL + + p_ed->td_head.toggle = 0; // reset data toggle + p_ed->td_head.halted = 0; + + if ( TUSB_XFER_BULK == ed_get_xfer_type(p_ed) ) OHCI_REG->command_status_bit.bulk_list_filled = 1; + + return true; +} + + +//--------------------------------------------------------------------+ +// OHCI Interrupt Handler +//--------------------------------------------------------------------+ +static ohci_td_item_t* list_reverse(ohci_td_item_t* td_head) +{ + ohci_td_item_t* td_reverse_head = NULL; + + while(td_head != NULL) + { + uint32_t next = td_head->next; + + // make current's item become reverse's first item + td_head->next = (uint32_t) td_reverse_head; + td_reverse_head = td_head; + + td_head = (ohci_td_item_t*) next; // advance to next item + } + + return td_reverse_head; +} + +static inline bool gtd_is_control(ohci_gtd_t const * const p_qtd) +{ + return ((uint32_t) p_qtd) < ((uint32_t) ohci_data.gtd_pool); // check ohci_data_t for memory layout +} + +static inline ohci_ed_t* gtd_get_ed(ohci_gtd_t const * const p_qtd) +{ + if ( gtd_is_control(p_qtd) ) + { + return &ohci_data.control[p_qtd->index].ed; + }else + { + return &ohci_data.ed_pool[p_qtd->index]; + } +} + +static inline uint32_t gtd_xfer_byte_left(uint32_t buffer_end, uint32_t current_buffer) +{ // 5.2.9 OHCI sample code + return (tu_align4k(buffer_end ^ current_buffer) ? 0x1000 : 0) + + tu_offset4k(buffer_end) - tu_offset4k(current_buffer) + 1; +} + +static void done_queue_isr(uint8_t hostid) +{ + (void) hostid; + + // done head is written in reversed order of completion --> need to reverse the done queue first + ohci_td_item_t* td_head = list_reverse ( (ohci_td_item_t*) tu_align16(ohci_data.hcca.done_head) ); + + while( td_head != NULL ) + { + // TODO check if td_head is iso td + //------------- Non ISO transfer -------------// + ohci_gtd_t * const p_qtd = (ohci_gtd_t *) td_head; + xfer_result_t const event = (p_qtd->condition_code == OHCI_CCODE_NO_ERROR) ? XFER_RESULT_SUCCESS : + (p_qtd->condition_code == OHCI_CCODE_STALL) ? XFER_RESULT_STALLED : XFER_RESULT_FAILED; + + p_qtd->used = 0; // free TD + if ( (p_qtd->delay_interrupt == OHCI_INT_ON_COMPLETE_YES) || (event != XFER_RESULT_SUCCESS) ) + { + ohci_ed_t * const p_ed = gtd_get_ed(p_qtd); + + uint32_t const xferred_bytes = p_qtd->expected_bytes - gtd_xfer_byte_left((uint32_t) p_qtd->buffer_end, (uint32_t) p_qtd->current_buffer_pointer); + + // NOTE Assuming the current list is BULK and there is no other EDs in the list has queued TDs. + // When there is a error resulting this ED is halted, and this EP still has other queued TD + // --> the Bulk list only has this halted EP queueing TDs (remaining) + // --> Bulk list will be considered as not empty by HC !!! while there is no attempt transaction on this list + // --> HC will not process Control list (due to service ratio when Bulk list not empty) + // To walk-around this, the halted ED will have TailP = HeadP (empty list condition), when clearing halt + // the TailP must be set back to NULL for processing remaining TDs + if ((event != XFER_RESULT_SUCCESS)) + { + p_ed->td_tail &= 0x0Ful; + p_ed->td_tail |= tu_align16(p_ed->td_head.address); // mark halted EP as empty queue + if ( event == XFER_RESULT_STALLED ) p_ed->is_stalled = 1; + } + + hcd_event_xfer_complete(p_ed->dev_addr, + tu_edpt_addr(p_ed->ep_number, p_ed->pid == OHCI_PID_IN), + event, xferred_bytes); + } + + td_head = (ohci_td_item_t*) td_head->next; + } +} + +void hcd_isr(uint8_t hostid) +{ + uint32_t const int_en = OHCI_REG->interrupt_enable; + uint32_t const int_status = OHCI_REG->interrupt_status & int_en; + + if (int_status == 0) return; + + //------------- RootHub status -------------// + if ( int_status & OHCI_INT_RHPORT_STATUS_CHANGE_MASK ) + { + uint32_t const rhport_status = OHCI_REG->rhport_status[0] & OHCI_RHPORT_ALL_CHANGE_MASK; + + // TODO dual port is not yet supported + if ( rhport_status & OHCI_RHPORT_CONNECT_STATUS_CHANGE_MASK ) + { + // TODO check if remote wake-up + if ( OHCI_REG->rhport_status_bit[0].current_connect_status ) + { + // TODO reset port immediately, without this controller will got 2-3 (debouncing connection status change) + OHCI_REG->rhport_status[0] = OHCI_RHPORT_PORT_RESET_STATUS_MASK; + hcd_event_device_attach(0); + }else + { + hcd_event_device_remove(0); + } + } + + if ( rhport_status & OHCI_RHPORT_PORT_SUSPEND_CHANGE_MASK) + { + + } + + OHCI_REG->rhport_status[0] = rhport_status; // acknowledge all interrupt + } + + //------------- Transfer Complete -------------// + if ( int_status & OHCI_INT_WRITEBACK_DONEHEAD_MASK) + { + done_queue_isr(hostid); + } + + OHCI_REG->interrupt_status = int_status; // Acknowledge handled interrupt +} +//--------------------------------------------------------------------+ +// HELPER +//--------------------------------------------------------------------+ + + +#endif + diff --git a/src/host/ohci/ohci.h b/src/host/ohci/ohci.h index a8aae251b..6f6ef9676 100644 --- a/src/host/ohci/ohci.h +++ b/src/host/ohci/ohci.h @@ -1,291 +1,291 @@ -/* - * 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_HCD - * @{ - * \defgroup OHCI - * \brief OHCI driver. All documents sources mentioned here (eg section 3.5) is referring to OHCI Specs unless state otherwise - * @{ */ - -#ifndef _TUSB_OHCI_H_ -#define _TUSB_OHCI_H_ - -#ifdef __cplusplus - extern "C" { -#endif - -#include "common/tusb_common.h" - -//--------------------------------------------------------------------+ -// OHCI CONFIGURATION & CONSTANTS -//--------------------------------------------------------------------+ -#define HOST_HCD_XFER_INTERRUPT // TODO interrupt is used widely, should always be enalbed -#define OHCI_PERIODIC_LIST (defined HOST_HCD_XFER_INTERRUPT || defined HOST_HCD_XFER_ISOCHRONOUS) - -// TODO merge OHCI with EHCI -enum { - OHCI_MAX_ITD = 4 -}; - -enum { - OHCI_PID_SETUP = 0, - OHCI_PID_OUT, - OHCI_PID_IN, -}; - -//--------------------------------------------------------------------+ -// OHCI Data Structure -//--------------------------------------------------------------------+ -typedef struct { - uint32_t interrupt_table[32]; - volatile uint16_t frame_number; - volatile uint16_t frame_pad; - volatile uint32_t done_head; - uint8_t reserved[116+4]; // TODO try to make use of this area if possible, extra 4 byte to make the whole struct size = 256 -}ohci_hcca_t; // TU_ATTR_ALIGNED(256) - -TU_VERIFY_STATIC( sizeof(ohci_hcca_t) == 256, "size is not correct" ); - -typedef struct { - uint32_t reserved[2]; - volatile uint32_t next; - uint32_t reserved2; -}ohci_td_item_t; - - -typedef struct TU_ATTR_ALIGNED(16) -{ - // Word 0 - uint32_t used : 1; - uint32_t index : 4; // endpoint index the td belongs to, or device address in case of control xfer - uint32_t expected_bytes : 13; // TODO available for hcd - - uint32_t buffer_rounding : 1; - uint32_t pid : 2; - uint32_t delay_interrupt : 3; - volatile uint32_t data_toggle : 2; - volatile uint32_t error_count : 2; - volatile uint32_t condition_code : 4; - - // Word 1 - volatile uint8_t* current_buffer_pointer; - - // Word 2 : next TD - volatile uint32_t next; - - // Word 3 - uint8_t* buffer_end; -} ohci_gtd_t; - -TU_VERIFY_STATIC( sizeof(ohci_gtd_t) == 16, "size is not correct" ); - -typedef struct TU_ATTR_ALIGNED(16) -{ - // Word 0 - uint32_t dev_addr : 7; - uint32_t ep_number : 4; - uint32_t pid : 2; // 00b from TD, 01b Out, 10b In - uint32_t speed : 1; - uint32_t skip : 1; - uint32_t is_iso : 1; - uint32_t max_packet_size : 11; - // HCD: make use of 5 reserved bits - uint32_t used : 1; - uint32_t is_interrupt_xfer : 1; - uint32_t is_stalled : 1; - uint32_t : 2; - - // Word 1 - uint32_t td_tail; - - // Word 2 - volatile union { - uint32_t address; - struct { - uint32_t halted : 1; - uint32_t toggle : 1; - uint32_t : 30; - }; - }td_head; - - // Word 3: next ED - uint32_t next; -} ohci_ed_t; - -TU_VERIFY_STATIC( sizeof(ohci_ed_t) == 16, "size is not correct" ); - -typedef struct TU_ATTR_ALIGNED(32) -{ - /*---------- Word 1 ----------*/ - uint32_t starting_frame : 16; - uint32_t : 5; // can be used - uint32_t delay_interrupt : 3; - uint32_t frame_count : 3; - uint32_t : 1; // can be used - volatile uint32_t condition_code : 4; - - /*---------- Word 2 ----------*/ - uint32_t buffer_page0; // 12 lsb bits can be used - - /*---------- Word 3 ----------*/ - volatile uint32_t next; - - /*---------- Word 4 ----------*/ - uint32_t buffer_end; - - /*---------- Word 5-8 ----------*/ - volatile uint16_t offset_packetstatus[8]; -} ochi_itd_t; - -TU_VERIFY_STATIC( sizeof(ochi_itd_t) == 32, "size is not correct" ); - -// structure with member alignment required from large to small -typedef struct TU_ATTR_ALIGNED(256) -{ - ohci_hcca_t hcca; - - ohci_ed_t bulk_head_ed; // static bulk head (dummy) - ohci_ed_t period_head_ed; // static periodic list head (dummy) - - // control endpoints has reserved resources - struct { - ohci_ed_t ed; - ohci_gtd_t gtd; - }control[CFG_TUSB_HOST_DEVICE_MAX+1]; - - // ochi_itd_t itd[OHCI_MAX_ITD]; // itd requires alignment of 32 - ohci_ed_t ed_pool[HCD_MAX_ENDPOINT]; - ohci_gtd_t gtd_pool[HCD_MAX_XFER]; - -} ohci_data_t; - -//--------------------------------------------------------------------+ -// OHCI Operational Register -//--------------------------------------------------------------------+ - - -//--------------------------------------------------------------------+ -// OHCI Data Organization -//--------------------------------------------------------------------+ -typedef volatile struct -{ - uint32_t revision; - - union { - uint32_t control; - struct { - uint32_t control_bulk_service_ratio : 2; - uint32_t periodic_list_enable : 1; - uint32_t isochronous_enable : 1; - uint32_t control_list_enable : 1; - uint32_t bulk_list_enable : 1; - uint32_t hc_functional_state : 2; - uint32_t interrupt_routing : 1; - uint32_t remote_wakeup_connected : 1; - uint32_t remote_wakeup_enale : 1; - uint32_t : 0; - }control_bit; - }; - - union { - uint32_t command_status; - struct { - uint32_t controller_reset : 1; - uint32_t control_list_filled : 1; - uint32_t bulk_list_filled : 1; - uint32_t ownership_change_request : 1; - uint32_t : 12; - uint32_t scheduling_overrun_count : 2; - }command_status_bit; - }; - - uint32_t interrupt_status; - uint32_t interrupt_enable; - uint32_t interrupt_disable; - - uint32_t hcca; - uint32_t period_current_ed; - uint32_t control_head_ed; - uint32_t control_current_ed; - uint32_t bulk_head_ed; - uint32_t bulk_current_ed; - uint32_t done_head; - - uint32_t frame_interval; - uint32_t frame_remaining; - uint32_t frame_number; - uint32_t periodic_start; - uint32_t lowspeed_threshold; - - uint32_t rh_descriptorA; - uint32_t rh_descriptorB; - - union { - uint32_t rh_status; - struct { - uint32_t local_power_status : 1; // read Local Power Status; write: Clear Global Power - uint32_t over_current_indicator : 1; - uint32_t : 13; - uint32_t device_remote_wakeup_enable : 1; - uint32_t local_power_status_change : 1; - uint32_t over_current_indicator_change : 1; - uint32_t : 13; - uint32_t clear_remote_wakeup_enable : 1; - }rh_status_bit; - }; - - union { - uint32_t rhport_status[2]; // TODO NXP OHCI controller only has 2 ports - struct { - uint32_t current_connect_status : 1; - uint32_t port_enable_status : 1; - uint32_t port_suspend_status : 1; - uint32_t port_over_current_indicator : 1; - uint32_t port_reset_status : 1; - uint32_t : 3; - uint32_t port_power_status : 1; - uint32_t low_speed_device_attached : 1; - uint32_t : 6; - uint32_t connect_status_change : 1; - uint32_t port_enable_status_change : 1; - uint32_t port_suspend_status_change : 1; - uint32_t port_over_current_indicator_change : 1; - uint32_t port_reset_status_change : 1; - uint32_t : 0; - }rhport_status_bit[2]; - }; -}ohci_registers_t; - -TU_VERIFY_STATIC( sizeof(ohci_registers_t) == 0x5c, "size is not correct"); - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_OHCI_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_HCD + * @{ + * \defgroup OHCI + * \brief OHCI driver. All documents sources mentioned here (eg section 3.5) is referring to OHCI Specs unless state otherwise + * @{ */ + +#ifndef _TUSB_OHCI_H_ +#define _TUSB_OHCI_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +#include "common/tusb_common.h" + +//--------------------------------------------------------------------+ +// OHCI CONFIGURATION & CONSTANTS +//--------------------------------------------------------------------+ +#define HOST_HCD_XFER_INTERRUPT // TODO interrupt is used widely, should always be enalbed +#define OHCI_PERIODIC_LIST (defined HOST_HCD_XFER_INTERRUPT || defined HOST_HCD_XFER_ISOCHRONOUS) + +// TODO merge OHCI with EHCI +enum { + OHCI_MAX_ITD = 4 +}; + +enum { + OHCI_PID_SETUP = 0, + OHCI_PID_OUT, + OHCI_PID_IN, +}; + +//--------------------------------------------------------------------+ +// OHCI Data Structure +//--------------------------------------------------------------------+ +typedef struct { + uint32_t interrupt_table[32]; + volatile uint16_t frame_number; + volatile uint16_t frame_pad; + volatile uint32_t done_head; + uint8_t reserved[116+4]; // TODO try to make use of this area if possible, extra 4 byte to make the whole struct size = 256 +}ohci_hcca_t; // TU_ATTR_ALIGNED(256) + +TU_VERIFY_STATIC( sizeof(ohci_hcca_t) == 256, "size is not correct" ); + +typedef struct { + uint32_t reserved[2]; + volatile uint32_t next; + uint32_t reserved2; +}ohci_td_item_t; + + +typedef struct TU_ATTR_ALIGNED(16) +{ + // Word 0 + uint32_t used : 1; + uint32_t index : 4; // endpoint index the td belongs to, or device address in case of control xfer + uint32_t expected_bytes : 13; // TODO available for hcd + + uint32_t buffer_rounding : 1; + uint32_t pid : 2; + uint32_t delay_interrupt : 3; + volatile uint32_t data_toggle : 2; + volatile uint32_t error_count : 2; + volatile uint32_t condition_code : 4; + + // Word 1 + volatile uint8_t* current_buffer_pointer; + + // Word 2 : next TD + volatile uint32_t next; + + // Word 3 + uint8_t* buffer_end; +} ohci_gtd_t; + +TU_VERIFY_STATIC( sizeof(ohci_gtd_t) == 16, "size is not correct" ); + +typedef struct TU_ATTR_ALIGNED(16) +{ + // Word 0 + uint32_t dev_addr : 7; + uint32_t ep_number : 4; + uint32_t pid : 2; // 00b from TD, 01b Out, 10b In + uint32_t speed : 1; + uint32_t skip : 1; + uint32_t is_iso : 1; + uint32_t max_packet_size : 11; + // HCD: make use of 5 reserved bits + uint32_t used : 1; + uint32_t is_interrupt_xfer : 1; + uint32_t is_stalled : 1; + uint32_t : 2; + + // Word 1 + uint32_t td_tail; + + // Word 2 + volatile union { + uint32_t address; + struct { + uint32_t halted : 1; + uint32_t toggle : 1; + uint32_t : 30; + }; + }td_head; + + // Word 3: next ED + uint32_t next; +} ohci_ed_t; + +TU_VERIFY_STATIC( sizeof(ohci_ed_t) == 16, "size is not correct" ); + +typedef struct TU_ATTR_ALIGNED(32) +{ + /*---------- Word 1 ----------*/ + uint32_t starting_frame : 16; + uint32_t : 5; // can be used + uint32_t delay_interrupt : 3; + uint32_t frame_count : 3; + uint32_t : 1; // can be used + volatile uint32_t condition_code : 4; + + /*---------- Word 2 ----------*/ + uint32_t buffer_page0; // 12 lsb bits can be used + + /*---------- Word 3 ----------*/ + volatile uint32_t next; + + /*---------- Word 4 ----------*/ + uint32_t buffer_end; + + /*---------- Word 5-8 ----------*/ + volatile uint16_t offset_packetstatus[8]; +} ochi_itd_t; + +TU_VERIFY_STATIC( sizeof(ochi_itd_t) == 32, "size is not correct" ); + +// structure with member alignment required from large to small +typedef struct TU_ATTR_ALIGNED(256) +{ + ohci_hcca_t hcca; + + ohci_ed_t bulk_head_ed; // static bulk head (dummy) + ohci_ed_t period_head_ed; // static periodic list head (dummy) + + // control endpoints has reserved resources + struct { + ohci_ed_t ed; + ohci_gtd_t gtd; + }control[CFG_TUSB_HOST_DEVICE_MAX+1]; + + // ochi_itd_t itd[OHCI_MAX_ITD]; // itd requires alignment of 32 + ohci_ed_t ed_pool[HCD_MAX_ENDPOINT]; + ohci_gtd_t gtd_pool[HCD_MAX_XFER]; + +} ohci_data_t; + +//--------------------------------------------------------------------+ +// OHCI Operational Register +//--------------------------------------------------------------------+ + + +//--------------------------------------------------------------------+ +// OHCI Data Organization +//--------------------------------------------------------------------+ +typedef volatile struct +{ + uint32_t revision; + + union { + uint32_t control; + struct { + uint32_t control_bulk_service_ratio : 2; + uint32_t periodic_list_enable : 1; + uint32_t isochronous_enable : 1; + uint32_t control_list_enable : 1; + uint32_t bulk_list_enable : 1; + uint32_t hc_functional_state : 2; + uint32_t interrupt_routing : 1; + uint32_t remote_wakeup_connected : 1; + uint32_t remote_wakeup_enale : 1; + uint32_t : 0; + }control_bit; + }; + + union { + uint32_t command_status; + struct { + uint32_t controller_reset : 1; + uint32_t control_list_filled : 1; + uint32_t bulk_list_filled : 1; + uint32_t ownership_change_request : 1; + uint32_t : 12; + uint32_t scheduling_overrun_count : 2; + }command_status_bit; + }; + + uint32_t interrupt_status; + uint32_t interrupt_enable; + uint32_t interrupt_disable; + + uint32_t hcca; + uint32_t period_current_ed; + uint32_t control_head_ed; + uint32_t control_current_ed; + uint32_t bulk_head_ed; + uint32_t bulk_current_ed; + uint32_t done_head; + + uint32_t frame_interval; + uint32_t frame_remaining; + uint32_t frame_number; + uint32_t periodic_start; + uint32_t lowspeed_threshold; + + uint32_t rh_descriptorA; + uint32_t rh_descriptorB; + + union { + uint32_t rh_status; + struct { + uint32_t local_power_status : 1; // read Local Power Status; write: Clear Global Power + uint32_t over_current_indicator : 1; + uint32_t : 13; + uint32_t device_remote_wakeup_enable : 1; + uint32_t local_power_status_change : 1; + uint32_t over_current_indicator_change : 1; + uint32_t : 13; + uint32_t clear_remote_wakeup_enable : 1; + }rh_status_bit; + }; + + union { + uint32_t rhport_status[2]; // TODO NXP OHCI controller only has 2 ports + struct { + uint32_t current_connect_status : 1; + uint32_t port_enable_status : 1; + uint32_t port_suspend_status : 1; + uint32_t port_over_current_indicator : 1; + uint32_t port_reset_status : 1; + uint32_t : 3; + uint32_t port_power_status : 1; + uint32_t low_speed_device_attached : 1; + uint32_t : 6; + uint32_t connect_status_change : 1; + uint32_t port_enable_status_change : 1; + uint32_t port_suspend_status_change : 1; + uint32_t port_over_current_indicator_change : 1; + uint32_t port_reset_status_change : 1; + uint32_t : 0; + }rhport_status_bit[2]; + }; +}ohci_registers_t; + +TU_VERIFY_STATIC( sizeof(ohci_registers_t) == 0x5c, "size is not correct"); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_OHCI_H_ */ + +/** @} */ +/** @} */ diff --git a/src/host/usbh.c b/src/host/usbh.c index 9e1861f87..f1b88259c 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -1,676 +1,676 @@ -/* - * 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 "common/tusb_common.h" - -#if TUSB_OPT_HOST_ENABLED - -#ifndef CFG_TUH_TASK_QUEUE_SZ -#define CFG_TUH_TASK_QUEUE_SZ 16 -#endif - -//--------------------------------------------------------------------+ -// INCLUDE -//--------------------------------------------------------------------+ -#include "tusb.h" -#include "hub.h" -#include "usbh_hcd.h" - -//--------------------------------------------------------------------+ -// MACRO CONSTANT TYPEDEF -//--------------------------------------------------------------------+ -static host_class_driver_t const usbh_class_drivers[] = -{ - #if CFG_TUH_CDC - { - .class_code = TUSB_CLASS_CDC, - .init = cdch_init, - .open = cdch_open, - .isr = cdch_isr, - .close = cdch_close - }, - #endif - - #if CFG_TUH_MSC - { - .class_code = TUSB_CLASS_MSC, - .init = msch_init, - .open = msch_open, - .isr = msch_isr, - .close = msch_close - }, - #endif - - #if HOST_CLASS_HID - { - .class_code = TUSB_CLASS_HID, - .init = hidh_init, - .open = hidh_open_subtask, - .isr = hidh_isr, - .close = hidh_close - }, - #endif - - #if CFG_TUH_HUB - { - .class_code = TUSB_CLASS_HUB, - .init = hub_init, - .open = hub_open, - .isr = hub_isr, - .close = hub_close - }, - #endif - - #if CFG_TUH_VENDOR - { - .class_code = TUSB_CLASS_VENDOR_SPECIFIC, - .init = cush_init, - .open = cush_open_subtask, - .isr = cush_isr, - .close = cush_close - } - #endif -}; - -enum { USBH_CLASS_DRIVER_COUNT = TU_ARRAY_SIZE(usbh_class_drivers) }; - -//--------------------------------------------------------------------+ -// INTERNAL OBJECT & FUNCTION DECLARATION -//--------------------------------------------------------------------+ - -// including zero-address -CFG_TUSB_MEM_SECTION usbh_device_t _usbh_devices[CFG_TUSB_HOST_DEVICE_MAX+1]; - -// Event queue -// role device/host is used by OS NONE for mutex (disable usb isr) only -OSAL_QUEUE_DEF(OPT_MODE_HOST, _usbh_qdef, CFG_TUH_TASK_QUEUE_SZ, hcd_event_t); -static osal_queue_t _usbh_q; - -CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t _usbh_ctrl_buf[CFG_TUSB_HOST_ENUM_BUFFER_SIZE]; - -//------------- Reporter Task Data -------------// - -//------------- Helper Function Prototypes -------------// -static inline uint8_t get_new_address(void); -static inline uint8_t get_configure_number_for_device(tusb_desc_device_t* dev_desc); -static void mark_interface_endpoint(uint8_t ep2drv[8][2], uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id); - -//--------------------------------------------------------------------+ -// PUBLIC API (Parameter Verification is required) -//--------------------------------------------------------------------+ -tusb_device_state_t tuh_device_get_state (uint8_t const dev_addr) -{ - TU_ASSERT( dev_addr <= CFG_TUSB_HOST_DEVICE_MAX, TUSB_DEVICE_STATE_UNPLUG); - return (tusb_device_state_t) _usbh_devices[dev_addr].state; -} - -//--------------------------------------------------------------------+ -// CLASS-USBD API (don't require to verify parameters) -//--------------------------------------------------------------------+ -bool usbh_init(void) -{ - tu_memclr(_usbh_devices, sizeof(usbh_device_t)*(CFG_TUSB_HOST_DEVICE_MAX+1)); - - //------------- Enumeration & Reporter Task init -------------// - _usbh_q = osal_queue_create( &_usbh_qdef ); - TU_ASSERT(_usbh_q != NULL); - - //------------- Semaphore, Mutex for Control Pipe -------------// - for(uint8_t i=0; icontrol.sem_hdl = osal_semaphore_create(&dev->control.sem_def); - TU_ASSERT(dev->control.sem_hdl != NULL); - - dev->control.mutex_hdl = osal_mutex_create(&dev->control.mutex_def); - TU_ASSERT(dev->control.mutex_hdl != NULL); - - memset(dev->itf2drv, 0xff, sizeof(dev->itf2drv)); // invalid mapping - memset(dev->ep2drv , 0xff, sizeof(dev->ep2drv )); // invalid mapping - } - - // Class drivers init - for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++) usbh_class_drivers[drv_id].init(); - - TU_ASSERT(hcd_init()); - hcd_int_enable(TUH_OPT_RHPORT); - - return true; -} - -//------------- USBH control transfer -------------// -bool usbh_control_xfer (uint8_t dev_addr, tusb_control_request_t* request, uint8_t* data) -{ - usbh_device_t* dev = &_usbh_devices[dev_addr]; - const uint8_t rhport = dev->rhport; - - TU_ASSERT(osal_mutex_lock(dev->control.mutex_hdl, OSAL_TIMEOUT_NORMAL)); - - dev->control.request = *request; - dev->control.pipe_status = 0; - - // Setup Stage - hcd_setup_send(rhport, dev_addr, (uint8_t*) &dev->control.request); - TU_VERIFY(osal_semaphore_wait(dev->control.sem_hdl, OSAL_TIMEOUT_NORMAL)); - - // Data stage : first data toggle is always 1 - if ( request->wLength ) - { - hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, request->bmRequestType_bit.direction), data, request->wLength); - TU_VERIFY(osal_semaphore_wait(dev->control.sem_hdl, OSAL_TIMEOUT_NORMAL)); - } - - // Status : data toggle is always 1 - hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, 1-request->bmRequestType_bit.direction), NULL, 0); - TU_VERIFY(osal_semaphore_wait(dev->control.sem_hdl, OSAL_TIMEOUT_NORMAL)); - - osal_mutex_unlock(dev->control.mutex_hdl); - - if ( XFER_RESULT_STALLED == dev->control.pipe_status ) return false; - if ( XFER_RESULT_FAILED == dev->control.pipe_status ) return false; - - return true; -} - -tusb_error_t usbh_pipe_control_open(uint8_t dev_addr, uint8_t max_packet_size) -{ - osal_semaphore_reset( _usbh_devices[dev_addr].control.sem_hdl ); - //osal_mutex_reset( usbh_devices[dev_addr].control.mutex_hdl ); - - tusb_desc_endpoint_t ep0_desc = - { - .bLength = sizeof(tusb_desc_endpoint_t), - .bDescriptorType = TUSB_DESC_ENDPOINT, - .bEndpointAddress = 0, - .bmAttributes = { .xfer = TUSB_XFER_CONTROL }, - .wMaxPacketSize = { .size = max_packet_size }, - .bInterval = 0 - }; - - hcd_edpt_open(_usbh_devices[dev_addr].rhport, dev_addr, &ep0_desc); - - return TUSB_ERROR_NONE; -} - -//--------------------------------------------------------------------+ -// USBH-HCD ISR/Callback API -//--------------------------------------------------------------------+ -// interrupt caused by a TD (with IOC=1) in pipe of class class_code -void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) -{ - usbh_device_t* dev = &_usbh_devices[ dev_addr ]; - - if (0 == tu_edpt_number(ep_addr)) - { - dev->control.pipe_status = event; -// usbh_devices[ pipe_hdl.dev_addr ].control.xferred_bytes = xferred_bytes; not yet neccessary - osal_semaphore_post( dev->control.sem_hdl, true ); - } - else - { - uint8_t drv_id = dev->ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)]; - TU_ASSERT(drv_id < USBH_CLASS_DRIVER_COUNT, ); - - if (usbh_class_drivers[drv_id].isr) - { - usbh_class_drivers[drv_id].isr(dev_addr, ep_addr, event, xferred_bytes); - } - else - { - TU_BREAKPOINT(); // something wrong, no one claims the isr's source - } - } -} - -void hcd_event_device_attach(uint8_t rhport) -{ - hcd_event_t event = - { - .rhport = rhport, - .event_id = HCD_EVENT_DEVICE_ATTACH - }; - - event.attach.hub_addr = 0; - event.attach.hub_port = 0; - - hcd_event_handler(&event, true); -} - -void hcd_event_handler(hcd_event_t const* event, bool in_isr) -{ - switch (event->event_id) - { - default: - osal_queue_send(_usbh_q, event, in_isr); - break; - } -} - -void hcd_event_device_remove(uint8_t hostid) -{ - hcd_event_t event = - { - .rhport = hostid, - .event_id = HCD_EVENT_DEVICE_REMOVE - }; - - event.attach.hub_addr = 0; - event.attach.hub_port = 0; - - hcd_event_handler(&event, true); -} - - -// a device unplugged on hostid, hub_addr, hub_port -// return true if found and unmounted device, false if cannot find -static void usbh_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port) -{ - //------------- find the all devices (star-network) under port that is unplugged -------------// - for (uint8_t dev_addr = 0; dev_addr <= CFG_TUSB_HOST_DEVICE_MAX; dev_addr ++) - { - usbh_device_t* dev = &_usbh_devices[dev_addr]; - - // TODO Hub multiple level - if (dev->rhport == rhport && - (hub_addr == 0 || dev->hub_addr == hub_addr) && // hub_addr == 0 & hub_port == 0 means roothub - (hub_port == 0 || dev->hub_port == hub_port) && - dev->state != TUSB_DEVICE_STATE_UNPLUG) - { - // Invoke callback before close driver - if (tuh_umount_cb) tuh_umount_cb(dev_addr); - - // Close class driver - for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++) usbh_class_drivers[drv_id].close(dev_addr); - - memset(dev->itf2drv, 0xff, sizeof(dev->itf2drv)); // invalid mapping - memset(dev->ep2drv , 0xff, sizeof(dev->ep2drv )); // invalid mapping - - hcd_device_close(rhport, dev_addr); - - dev->state = TUSB_DEVICE_STATE_UNPLUG; - } - } -} - -//--------------------------------------------------------------------+ -// ENUMERATION TASK -//--------------------------------------------------------------------+ - -bool enum_task(hcd_event_t* event) -{ - enum { -#if 1 - // FIXME ohci LPC1769 xpresso + debugging to have 1st control xfer to work, some kind of timing or ohci driver issue !!! - POWER_STABLE_DELAY = 100, - RESET_DELAY = 500 -#else - POWER_STABLE_DELAY = 500, - RESET_DELAY = 200, // USB specs say only 50ms but many devices require much longer -#endif - }; - - // for OSAL_NONE local variable won't retain value after blocking service sem_wait/queue_recv - static uint8_t configure_selected = 1; // TODO move - - usbh_device_t* dev0 = &_usbh_devices[0]; - tusb_control_request_t request; - - dev0->rhport = event->rhport; // TODO refractor integrate to device_pool - dev0->hub_addr = event->attach.hub_addr; - dev0->hub_port = event->attach.hub_port; - dev0->state = TUSB_DEVICE_STATE_UNPLUG; - - //------------- connected/disconnected directly with roothub -------------// - if ( dev0->hub_addr == 0) - { - if( hcd_port_connect_status(dev0->rhport) ) - { - // connection event - osal_task_delay(POWER_STABLE_DELAY); // wait until device is stable. Increase this if the first 8 bytes is failed to get - - // exit if device unplugged while delaying - if ( !hcd_port_connect_status(dev0->rhport) ) return true; - - hcd_port_reset( dev0->rhport ); // port must be reset to have correct speed operation - osal_task_delay(RESET_DELAY); - - dev0->speed = hcd_port_speed_get( dev0->rhport ); - } - else - { - // disconnection event - usbh_device_unplugged(dev0->rhport, 0, 0); - return true; // restart task - } - } - #if CFG_TUH_HUB - //------------- connected/disconnected via hub -------------// - else - { - //------------- Get Port Status -------------// - request = (tusb_control_request_t ) { - .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_OTHER, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_IN }, - .bRequest = HUB_REQUEST_GET_STATUS, - .wValue = 0, - .wIndex = dev0->hub_port, - .wLength = 4 - }; - // TODO hub refractor - TU_VERIFY_HDLR( usbh_control_xfer( dev0->hub_addr, &request, _usbh_ctrl_buf ), hub_status_pipe_queue( dev0->hub_addr) ); - - // Acknowledge Port Connection Change - hub_port_clear_feature_subtask(dev0->hub_addr, dev0->hub_port, HUB_FEATURE_PORT_CONNECTION_CHANGE); - - hub_port_status_response_t * p_port_status; - p_port_status = ((hub_port_status_response_t *) _usbh_ctrl_buf); - - if ( ! p_port_status->status_change.connect_status ) return true; // only handle connection change - - if ( ! p_port_status->status_current.connect_status ) - { - // Disconnection event - usbh_device_unplugged(dev0->rhport, dev0->hub_addr, dev0->hub_port); - - (void) hub_status_pipe_queue( dev0->hub_addr ); // done with hub, waiting for next data on status pipe - return true; // restart task - } - else - { - // Connection Event - TU_VERIFY_HDLR(hub_port_reset_subtask(dev0->hub_addr, dev0->hub_port), - hub_status_pipe_queue( dev0->hub_addr) ); // TODO hub refractor - - dev0->speed = hub_port_get_speed(); - - // Acknowledge Port Reset Change - hub_port_clear_feature_subtask(dev0->hub_addr, dev0->hub_port, HUB_FEATURE_PORT_RESET_CHANGE); - } - } - #endif - - TU_ASSERT_ERR( usbh_pipe_control_open(0, 8) ); - - //------------- Get first 8 bytes of device descriptor to get Control Endpoint Size -------------// - request = (tusb_control_request_t ) { - .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_DEVICE, .type = TUSB_REQ_TYPE_STANDARD, .direction = TUSB_DIR_IN }, - .bRequest = TUSB_REQ_GET_DESCRIPTOR, - .wValue = TUSB_DESC_DEVICE << 8, - .wIndex = 0, - .wLength = 8 - }; - bool is_ok = usbh_control_xfer(0, &request, _usbh_ctrl_buf); - - //------------- Reset device again before Set Address -------------// - if (dev0->hub_addr == 0) - { - // connected directly to roothub - TU_ASSERT(is_ok); // TODO some slow device is observed to fail the very fist controller xfer, can try more times - hcd_port_reset( dev0->rhport ); // reset port after 8 byte descriptor - osal_task_delay(RESET_DELAY); - } - #if CFG_TUH_HUB - else - { - // connected via a hub - TU_VERIFY_HDLR(is_ok, hub_status_pipe_queue( dev0->hub_addr) ); // TODO hub refractor - - if ( hub_port_reset_subtask(dev0->hub_addr, dev0->hub_port) ) - { - // Acknowledge Port Reset Change if Reset Successful - hub_port_clear_feature_subtask(dev0->hub_addr, dev0->hub_port, HUB_FEATURE_PORT_RESET_CHANGE); - } - - (void) hub_status_pipe_queue( dev0->hub_addr ); // done with hub, waiting for next data on status pipe - } - #endif - - //------------- Set new address -------------// - uint8_t const new_addr = get_new_address(); - TU_ASSERT(new_addr <= CFG_TUSB_HOST_DEVICE_MAX); // TODO notify application we reach max devices - - request = (tusb_control_request_t ) { - .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_DEVICE, .type = TUSB_REQ_TYPE_STANDARD, .direction = TUSB_DIR_OUT }, - .bRequest = TUSB_REQ_SET_ADDRESS, - .wValue = new_addr, - .wIndex = 0, - .wLength = 0 - }; - TU_ASSERT(usbh_control_xfer(0, &request, NULL)); - - //------------- update port info & close control pipe of addr0 -------------// - usbh_device_t* new_dev = &_usbh_devices[new_addr]; - new_dev->rhport = dev0->rhport; - new_dev->hub_addr = dev0->hub_addr; - new_dev->hub_port = dev0->hub_port; - new_dev->speed = dev0->speed; - - hcd_device_close(dev0->rhport, 0); // close device 0 - dev0->state = TUSB_DEVICE_STATE_UNPLUG; - - // open control pipe for new address - TU_ASSERT_ERR ( usbh_pipe_control_open(new_addr, ((tusb_desc_device_t*) _usbh_ctrl_buf)->bMaxPacketSize0 ) ); - - //------------- Get full device descriptor -------------// - request = (tusb_control_request_t ) { - .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_DEVICE, .type = TUSB_REQ_TYPE_STANDARD, .direction = TUSB_DIR_IN }, - .bRequest = TUSB_REQ_GET_DESCRIPTOR, - .wValue = TUSB_DESC_DEVICE << 8, - .wIndex = 0, - .wLength = 18 - }; - TU_ASSERT(usbh_control_xfer(new_addr, &request, _usbh_ctrl_buf)); - - // update device info TODO alignment issue - new_dev->vendor_id = ((tusb_desc_device_t*) _usbh_ctrl_buf)->idVendor; - new_dev->product_id = ((tusb_desc_device_t*) _usbh_ctrl_buf)->idProduct; - new_dev->configure_count = ((tusb_desc_device_t*) _usbh_ctrl_buf)->bNumConfigurations; - - configure_selected = get_configure_number_for_device((tusb_desc_device_t*) _usbh_ctrl_buf); - TU_ASSERT(configure_selected <= new_dev->configure_count); // TODO notify application when invalid configuration - - //------------- Get 9 bytes of configuration descriptor -------------// - request = (tusb_control_request_t ) { - .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_DEVICE, .type = TUSB_REQ_TYPE_STANDARD, .direction = TUSB_DIR_IN }, - .bRequest = TUSB_REQ_GET_DESCRIPTOR, - .wValue = (TUSB_DESC_CONFIGURATION << 8) | (configure_selected - 1), - .wIndex = 0, - .wLength = 9 - }; - TU_ASSERT( usbh_control_xfer(new_addr, &request, _usbh_ctrl_buf)); - - // TODO not enough buffer to hold configuration descriptor - TU_ASSERT( CFG_TUSB_HOST_ENUM_BUFFER_SIZE >= ((tusb_desc_configuration_t*)_usbh_ctrl_buf)->wTotalLength ); - - //------------- Get full configuration descriptor -------------// - request.wLength = ((tusb_desc_configuration_t*)_usbh_ctrl_buf)->wTotalLength; // full length - TU_ASSERT( usbh_control_xfer( new_addr, &request, _usbh_ctrl_buf ) ); - - // update configuration info - new_dev->interface_count = ((tusb_desc_configuration_t*) _usbh_ctrl_buf)->bNumInterfaces; - - //------------- Set Configure -------------// - request = (tusb_control_request_t ) { - .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_DEVICE, .type = TUSB_REQ_TYPE_STANDARD, .direction = TUSB_DIR_OUT }, - .bRequest = TUSB_REQ_SET_CONFIGURATION, - .wValue = configure_selected, - .wIndex = 0, - .wLength = 0 - }; - TU_ASSERT(usbh_control_xfer( new_addr, &request, NULL )); - - new_dev->state = TUSB_DEVICE_STATE_CONFIGURED; - - //------------- TODO Get String Descriptors -------------// - - //------------- parse configuration & install drivers -------------// - uint8_t const* p_desc = _usbh_ctrl_buf + sizeof(tusb_desc_configuration_t); - - // parse each interfaces - while( p_desc < _usbh_ctrl_buf + ((tusb_desc_configuration_t*)_usbh_ctrl_buf)->wTotalLength ) - { - // skip until we see interface descriptor - if ( TUSB_DESC_INTERFACE != tu_desc_type(p_desc) ) - { - p_desc = tu_desc_next(p_desc); // skip the descriptor, increase by the descriptor's length - }else - { - 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 < USBH_CLASS_DRIVER_COUNT; drv_id++) - { - if ( usbh_class_drivers[drv_id].class_code == desc_itf->bInterfaceClass ) break; - } - - if( drv_id >= USBH_CLASS_DRIVER_COUNT ) - { - // skip unsupported class - p_desc = tu_desc_next(p_desc); - } - else - { - // Interface number must not be used already TODO alternate interface - TU_ASSERT( new_dev->itf2drv[desc_itf->bInterfaceNumber] == 0xff ); - new_dev->itf2drv[desc_itf->bInterfaceNumber] = drv_id; - - if (desc_itf->bInterfaceClass == TUSB_CLASS_HUB && new_dev->hub_addr != 0) - { - // TODO Attach hub to Hub is not currently supported - // skip this interface - p_desc = tu_desc_next(p_desc); - } - else - { - uint16_t itf_len = 0; - - if ( usbh_class_drivers[drv_id].open(new_dev->rhport, new_addr, desc_itf, &itf_len) ) - { - mark_interface_endpoint(new_dev->ep2drv, p_desc, itf_len, drv_id); - } - - TU_ASSERT( itf_len >= sizeof(tusb_desc_interface_t) ); - p_desc += itf_len; - } - } - } - } - - if (tuh_mount_cb) tuh_mount_cb(new_addr); - - return true; -} - -/* USB Host Driver task - * This top level thread manages all host 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(); - - tuh_task(); // tinyusb host task - } - } - @endcode - */ -void tuh_task(void) -{ - // Skip if stack is not initialized - if ( !tusb_inited() ) return; - - // Loop until there is no more events in the queue - while (1) - { - hcd_event_t event; - if ( !osal_queue_receive(_usbh_q, &event) ) return; - - switch (event.event_id) - { - case HCD_EVENT_DEVICE_ATTACH: - case HCD_EVENT_DEVICE_REMOVE: - enum_task(&event); - break; - - default: break; - } - } -} - -//--------------------------------------------------------------------+ -// INTERNAL HELPER -//--------------------------------------------------------------------+ -static inline uint8_t get_new_address(void) -{ - for (uint8_t addr=1; addr <= CFG_TUSB_HOST_DEVICE_MAX; addr++) - { - if (_usbh_devices[addr].state == TUSB_DEVICE_STATE_UNPLUG) return addr; - } - return CFG_TUSB_HOST_DEVICE_MAX+1; -} - -static inline uint8_t get_configure_number_for_device(tusb_desc_device_t* dev_desc) -{ - uint8_t config_num = 1; - - // invoke callback to ask user which configuration to select - if (tuh_device_attached_cb) - { - config_num = tu_min8(1, tuh_device_attached_cb(dev_desc) ); - } - - return config_num; -} - -// Helper marking endpoint of interface belongs to class driver -// TODO merge with usbd -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 += tu_desc_len(p_desc); - p_desc = tu_desc_next(p_desc); - } -} - - -#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 "common/tusb_common.h" + +#if TUSB_OPT_HOST_ENABLED + +#ifndef CFG_TUH_TASK_QUEUE_SZ +#define CFG_TUH_TASK_QUEUE_SZ 16 +#endif + +//--------------------------------------------------------------------+ +// INCLUDE +//--------------------------------------------------------------------+ +#include "tusb.h" +#include "hub.h" +#include "usbh_hcd.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +static host_class_driver_t const usbh_class_drivers[] = +{ + #if CFG_TUH_CDC + { + .class_code = TUSB_CLASS_CDC, + .init = cdch_init, + .open = cdch_open, + .isr = cdch_isr, + .close = cdch_close + }, + #endif + + #if CFG_TUH_MSC + { + .class_code = TUSB_CLASS_MSC, + .init = msch_init, + .open = msch_open, + .isr = msch_isr, + .close = msch_close + }, + #endif + + #if HOST_CLASS_HID + { + .class_code = TUSB_CLASS_HID, + .init = hidh_init, + .open = hidh_open_subtask, + .isr = hidh_isr, + .close = hidh_close + }, + #endif + + #if CFG_TUH_HUB + { + .class_code = TUSB_CLASS_HUB, + .init = hub_init, + .open = hub_open, + .isr = hub_isr, + .close = hub_close + }, + #endif + + #if CFG_TUH_VENDOR + { + .class_code = TUSB_CLASS_VENDOR_SPECIFIC, + .init = cush_init, + .open = cush_open_subtask, + .isr = cush_isr, + .close = cush_close + } + #endif +}; + +enum { USBH_CLASS_DRIVER_COUNT = TU_ARRAY_SIZE(usbh_class_drivers) }; + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ + +// including zero-address +CFG_TUSB_MEM_SECTION usbh_device_t _usbh_devices[CFG_TUSB_HOST_DEVICE_MAX+1]; + +// Event queue +// role device/host is used by OS NONE for mutex (disable usb isr) only +OSAL_QUEUE_DEF(OPT_MODE_HOST, _usbh_qdef, CFG_TUH_TASK_QUEUE_SZ, hcd_event_t); +static osal_queue_t _usbh_q; + +CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t _usbh_ctrl_buf[CFG_TUSB_HOST_ENUM_BUFFER_SIZE]; + +//------------- Reporter Task Data -------------// + +//------------- Helper Function Prototypes -------------// +static inline uint8_t get_new_address(void); +static inline uint8_t get_configure_number_for_device(tusb_desc_device_t* dev_desc); +static void mark_interface_endpoint(uint8_t ep2drv[8][2], uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id); + +//--------------------------------------------------------------------+ +// PUBLIC API (Parameter Verification is required) +//--------------------------------------------------------------------+ +tusb_device_state_t tuh_device_get_state (uint8_t const dev_addr) +{ + TU_ASSERT( dev_addr <= CFG_TUSB_HOST_DEVICE_MAX, TUSB_DEVICE_STATE_UNPLUG); + return (tusb_device_state_t) _usbh_devices[dev_addr].state; +} + +//--------------------------------------------------------------------+ +// CLASS-USBD API (don't require to verify parameters) +//--------------------------------------------------------------------+ +bool usbh_init(void) +{ + tu_memclr(_usbh_devices, sizeof(usbh_device_t)*(CFG_TUSB_HOST_DEVICE_MAX+1)); + + //------------- Enumeration & Reporter Task init -------------// + _usbh_q = osal_queue_create( &_usbh_qdef ); + TU_ASSERT(_usbh_q != NULL); + + //------------- Semaphore, Mutex for Control Pipe -------------// + for(uint8_t i=0; icontrol.sem_hdl = osal_semaphore_create(&dev->control.sem_def); + TU_ASSERT(dev->control.sem_hdl != NULL); + + dev->control.mutex_hdl = osal_mutex_create(&dev->control.mutex_def); + TU_ASSERT(dev->control.mutex_hdl != NULL); + + memset(dev->itf2drv, 0xff, sizeof(dev->itf2drv)); // invalid mapping + memset(dev->ep2drv , 0xff, sizeof(dev->ep2drv )); // invalid mapping + } + + // Class drivers init + for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++) usbh_class_drivers[drv_id].init(); + + TU_ASSERT(hcd_init()); + hcd_int_enable(TUH_OPT_RHPORT); + + return true; +} + +//------------- USBH control transfer -------------// +bool usbh_control_xfer (uint8_t dev_addr, tusb_control_request_t* request, uint8_t* data) +{ + usbh_device_t* dev = &_usbh_devices[dev_addr]; + const uint8_t rhport = dev->rhport; + + TU_ASSERT(osal_mutex_lock(dev->control.mutex_hdl, OSAL_TIMEOUT_NORMAL)); + + dev->control.request = *request; + dev->control.pipe_status = 0; + + // Setup Stage + hcd_setup_send(rhport, dev_addr, (uint8_t*) &dev->control.request); + TU_VERIFY(osal_semaphore_wait(dev->control.sem_hdl, OSAL_TIMEOUT_NORMAL)); + + // Data stage : first data toggle is always 1 + if ( request->wLength ) + { + hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, request->bmRequestType_bit.direction), data, request->wLength); + TU_VERIFY(osal_semaphore_wait(dev->control.sem_hdl, OSAL_TIMEOUT_NORMAL)); + } + + // Status : data toggle is always 1 + hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, 1-request->bmRequestType_bit.direction), NULL, 0); + TU_VERIFY(osal_semaphore_wait(dev->control.sem_hdl, OSAL_TIMEOUT_NORMAL)); + + osal_mutex_unlock(dev->control.mutex_hdl); + + if ( XFER_RESULT_STALLED == dev->control.pipe_status ) return false; + if ( XFER_RESULT_FAILED == dev->control.pipe_status ) return false; + + return true; +} + +tusb_error_t usbh_pipe_control_open(uint8_t dev_addr, uint8_t max_packet_size) +{ + osal_semaphore_reset( _usbh_devices[dev_addr].control.sem_hdl ); + //osal_mutex_reset( usbh_devices[dev_addr].control.mutex_hdl ); + + tusb_desc_endpoint_t ep0_desc = + { + .bLength = sizeof(tusb_desc_endpoint_t), + .bDescriptorType = TUSB_DESC_ENDPOINT, + .bEndpointAddress = 0, + .bmAttributes = { .xfer = TUSB_XFER_CONTROL }, + .wMaxPacketSize = { .size = max_packet_size }, + .bInterval = 0 + }; + + hcd_edpt_open(_usbh_devices[dev_addr].rhport, dev_addr, &ep0_desc); + + return TUSB_ERROR_NONE; +} + +//--------------------------------------------------------------------+ +// USBH-HCD ISR/Callback API +//--------------------------------------------------------------------+ +// interrupt caused by a TD (with IOC=1) in pipe of class class_code +void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) +{ + usbh_device_t* dev = &_usbh_devices[ dev_addr ]; + + if (0 == tu_edpt_number(ep_addr)) + { + dev->control.pipe_status = event; +// usbh_devices[ pipe_hdl.dev_addr ].control.xferred_bytes = xferred_bytes; not yet neccessary + osal_semaphore_post( dev->control.sem_hdl, true ); + } + else + { + uint8_t drv_id = dev->ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)]; + TU_ASSERT(drv_id < USBH_CLASS_DRIVER_COUNT, ); + + if (usbh_class_drivers[drv_id].isr) + { + usbh_class_drivers[drv_id].isr(dev_addr, ep_addr, event, xferred_bytes); + } + else + { + TU_BREAKPOINT(); // something wrong, no one claims the isr's source + } + } +} + +void hcd_event_device_attach(uint8_t rhport) +{ + hcd_event_t event = + { + .rhport = rhport, + .event_id = HCD_EVENT_DEVICE_ATTACH + }; + + event.attach.hub_addr = 0; + event.attach.hub_port = 0; + + hcd_event_handler(&event, true); +} + +void hcd_event_handler(hcd_event_t const* event, bool in_isr) +{ + switch (event->event_id) + { + default: + osal_queue_send(_usbh_q, event, in_isr); + break; + } +} + +void hcd_event_device_remove(uint8_t hostid) +{ + hcd_event_t event = + { + .rhport = hostid, + .event_id = HCD_EVENT_DEVICE_REMOVE + }; + + event.attach.hub_addr = 0; + event.attach.hub_port = 0; + + hcd_event_handler(&event, true); +} + + +// a device unplugged on hostid, hub_addr, hub_port +// return true if found and unmounted device, false if cannot find +static void usbh_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port) +{ + //------------- find the all devices (star-network) under port that is unplugged -------------// + for (uint8_t dev_addr = 0; dev_addr <= CFG_TUSB_HOST_DEVICE_MAX; dev_addr ++) + { + usbh_device_t* dev = &_usbh_devices[dev_addr]; + + // TODO Hub multiple level + if (dev->rhport == rhport && + (hub_addr == 0 || dev->hub_addr == hub_addr) && // hub_addr == 0 & hub_port == 0 means roothub + (hub_port == 0 || dev->hub_port == hub_port) && + dev->state != TUSB_DEVICE_STATE_UNPLUG) + { + // Invoke callback before close driver + if (tuh_umount_cb) tuh_umount_cb(dev_addr); + + // Close class driver + for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++) usbh_class_drivers[drv_id].close(dev_addr); + + memset(dev->itf2drv, 0xff, sizeof(dev->itf2drv)); // invalid mapping + memset(dev->ep2drv , 0xff, sizeof(dev->ep2drv )); // invalid mapping + + hcd_device_close(rhport, dev_addr); + + dev->state = TUSB_DEVICE_STATE_UNPLUG; + } + } +} + +//--------------------------------------------------------------------+ +// ENUMERATION TASK +//--------------------------------------------------------------------+ + +bool enum_task(hcd_event_t* event) +{ + enum { +#if 1 + // FIXME ohci LPC1769 xpresso + debugging to have 1st control xfer to work, some kind of timing or ohci driver issue !!! + POWER_STABLE_DELAY = 100, + RESET_DELAY = 500 +#else + POWER_STABLE_DELAY = 500, + RESET_DELAY = 200, // USB specs say only 50ms but many devices require much longer +#endif + }; + + // for OSAL_NONE local variable won't retain value after blocking service sem_wait/queue_recv + static uint8_t configure_selected = 1; // TODO move + + usbh_device_t* dev0 = &_usbh_devices[0]; + tusb_control_request_t request; + + dev0->rhport = event->rhport; // TODO refractor integrate to device_pool + dev0->hub_addr = event->attach.hub_addr; + dev0->hub_port = event->attach.hub_port; + dev0->state = TUSB_DEVICE_STATE_UNPLUG; + + //------------- connected/disconnected directly with roothub -------------// + if ( dev0->hub_addr == 0) + { + if( hcd_port_connect_status(dev0->rhport) ) + { + // connection event + osal_task_delay(POWER_STABLE_DELAY); // wait until device is stable. Increase this if the first 8 bytes is failed to get + + // exit if device unplugged while delaying + if ( !hcd_port_connect_status(dev0->rhport) ) return true; + + hcd_port_reset( dev0->rhport ); // port must be reset to have correct speed operation + osal_task_delay(RESET_DELAY); + + dev0->speed = hcd_port_speed_get( dev0->rhport ); + } + else + { + // disconnection event + usbh_device_unplugged(dev0->rhport, 0, 0); + return true; // restart task + } + } + #if CFG_TUH_HUB + //------------- connected/disconnected via hub -------------// + else + { + //------------- Get Port Status -------------// + request = (tusb_control_request_t ) { + .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_OTHER, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_IN }, + .bRequest = HUB_REQUEST_GET_STATUS, + .wValue = 0, + .wIndex = dev0->hub_port, + .wLength = 4 + }; + // TODO hub refractor + TU_VERIFY_HDLR( usbh_control_xfer( dev0->hub_addr, &request, _usbh_ctrl_buf ), hub_status_pipe_queue( dev0->hub_addr) ); + + // Acknowledge Port Connection Change + hub_port_clear_feature_subtask(dev0->hub_addr, dev0->hub_port, HUB_FEATURE_PORT_CONNECTION_CHANGE); + + hub_port_status_response_t * p_port_status; + p_port_status = ((hub_port_status_response_t *) _usbh_ctrl_buf); + + if ( ! p_port_status->status_change.connect_status ) return true; // only handle connection change + + if ( ! p_port_status->status_current.connect_status ) + { + // Disconnection event + usbh_device_unplugged(dev0->rhport, dev0->hub_addr, dev0->hub_port); + + (void) hub_status_pipe_queue( dev0->hub_addr ); // done with hub, waiting for next data on status pipe + return true; // restart task + } + else + { + // Connection Event + TU_VERIFY_HDLR(hub_port_reset_subtask(dev0->hub_addr, dev0->hub_port), + hub_status_pipe_queue( dev0->hub_addr) ); // TODO hub refractor + + dev0->speed = hub_port_get_speed(); + + // Acknowledge Port Reset Change + hub_port_clear_feature_subtask(dev0->hub_addr, dev0->hub_port, HUB_FEATURE_PORT_RESET_CHANGE); + } + } + #endif + + TU_ASSERT_ERR( usbh_pipe_control_open(0, 8) ); + + //------------- Get first 8 bytes of device descriptor to get Control Endpoint Size -------------// + request = (tusb_control_request_t ) { + .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_DEVICE, .type = TUSB_REQ_TYPE_STANDARD, .direction = TUSB_DIR_IN }, + .bRequest = TUSB_REQ_GET_DESCRIPTOR, + .wValue = TUSB_DESC_DEVICE << 8, + .wIndex = 0, + .wLength = 8 + }; + bool is_ok = usbh_control_xfer(0, &request, _usbh_ctrl_buf); + + //------------- Reset device again before Set Address -------------// + if (dev0->hub_addr == 0) + { + // connected directly to roothub + TU_ASSERT(is_ok); // TODO some slow device is observed to fail the very fist controller xfer, can try more times + hcd_port_reset( dev0->rhport ); // reset port after 8 byte descriptor + osal_task_delay(RESET_DELAY); + } + #if CFG_TUH_HUB + else + { + // connected via a hub + TU_VERIFY_HDLR(is_ok, hub_status_pipe_queue( dev0->hub_addr) ); // TODO hub refractor + + if ( hub_port_reset_subtask(dev0->hub_addr, dev0->hub_port) ) + { + // Acknowledge Port Reset Change if Reset Successful + hub_port_clear_feature_subtask(dev0->hub_addr, dev0->hub_port, HUB_FEATURE_PORT_RESET_CHANGE); + } + + (void) hub_status_pipe_queue( dev0->hub_addr ); // done with hub, waiting for next data on status pipe + } + #endif + + //------------- Set new address -------------// + uint8_t const new_addr = get_new_address(); + TU_ASSERT(new_addr <= CFG_TUSB_HOST_DEVICE_MAX); // TODO notify application we reach max devices + + request = (tusb_control_request_t ) { + .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_DEVICE, .type = TUSB_REQ_TYPE_STANDARD, .direction = TUSB_DIR_OUT }, + .bRequest = TUSB_REQ_SET_ADDRESS, + .wValue = new_addr, + .wIndex = 0, + .wLength = 0 + }; + TU_ASSERT(usbh_control_xfer(0, &request, NULL)); + + //------------- update port info & close control pipe of addr0 -------------// + usbh_device_t* new_dev = &_usbh_devices[new_addr]; + new_dev->rhport = dev0->rhport; + new_dev->hub_addr = dev0->hub_addr; + new_dev->hub_port = dev0->hub_port; + new_dev->speed = dev0->speed; + + hcd_device_close(dev0->rhport, 0); // close device 0 + dev0->state = TUSB_DEVICE_STATE_UNPLUG; + + // open control pipe for new address + TU_ASSERT_ERR ( usbh_pipe_control_open(new_addr, ((tusb_desc_device_t*) _usbh_ctrl_buf)->bMaxPacketSize0 ) ); + + //------------- Get full device descriptor -------------// + request = (tusb_control_request_t ) { + .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_DEVICE, .type = TUSB_REQ_TYPE_STANDARD, .direction = TUSB_DIR_IN }, + .bRequest = TUSB_REQ_GET_DESCRIPTOR, + .wValue = TUSB_DESC_DEVICE << 8, + .wIndex = 0, + .wLength = 18 + }; + TU_ASSERT(usbh_control_xfer(new_addr, &request, _usbh_ctrl_buf)); + + // update device info TODO alignment issue + new_dev->vendor_id = ((tusb_desc_device_t*) _usbh_ctrl_buf)->idVendor; + new_dev->product_id = ((tusb_desc_device_t*) _usbh_ctrl_buf)->idProduct; + new_dev->configure_count = ((tusb_desc_device_t*) _usbh_ctrl_buf)->bNumConfigurations; + + configure_selected = get_configure_number_for_device((tusb_desc_device_t*) _usbh_ctrl_buf); + TU_ASSERT(configure_selected <= new_dev->configure_count); // TODO notify application when invalid configuration + + //------------- Get 9 bytes of configuration descriptor -------------// + request = (tusb_control_request_t ) { + .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_DEVICE, .type = TUSB_REQ_TYPE_STANDARD, .direction = TUSB_DIR_IN }, + .bRequest = TUSB_REQ_GET_DESCRIPTOR, + .wValue = (TUSB_DESC_CONFIGURATION << 8) | (configure_selected - 1), + .wIndex = 0, + .wLength = 9 + }; + TU_ASSERT( usbh_control_xfer(new_addr, &request, _usbh_ctrl_buf)); + + // TODO not enough buffer to hold configuration descriptor + TU_ASSERT( CFG_TUSB_HOST_ENUM_BUFFER_SIZE >= ((tusb_desc_configuration_t*)_usbh_ctrl_buf)->wTotalLength ); + + //------------- Get full configuration descriptor -------------// + request.wLength = ((tusb_desc_configuration_t*)_usbh_ctrl_buf)->wTotalLength; // full length + TU_ASSERT( usbh_control_xfer( new_addr, &request, _usbh_ctrl_buf ) ); + + // update configuration info + new_dev->interface_count = ((tusb_desc_configuration_t*) _usbh_ctrl_buf)->bNumInterfaces; + + //------------- Set Configure -------------// + request = (tusb_control_request_t ) { + .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_DEVICE, .type = TUSB_REQ_TYPE_STANDARD, .direction = TUSB_DIR_OUT }, + .bRequest = TUSB_REQ_SET_CONFIGURATION, + .wValue = configure_selected, + .wIndex = 0, + .wLength = 0 + }; + TU_ASSERT(usbh_control_xfer( new_addr, &request, NULL )); + + new_dev->state = TUSB_DEVICE_STATE_CONFIGURED; + + //------------- TODO Get String Descriptors -------------// + + //------------- parse configuration & install drivers -------------// + uint8_t const* p_desc = _usbh_ctrl_buf + sizeof(tusb_desc_configuration_t); + + // parse each interfaces + while( p_desc < _usbh_ctrl_buf + ((tusb_desc_configuration_t*)_usbh_ctrl_buf)->wTotalLength ) + { + // skip until we see interface descriptor + if ( TUSB_DESC_INTERFACE != tu_desc_type(p_desc) ) + { + p_desc = tu_desc_next(p_desc); // skip the descriptor, increase by the descriptor's length + }else + { + 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 < USBH_CLASS_DRIVER_COUNT; drv_id++) + { + if ( usbh_class_drivers[drv_id].class_code == desc_itf->bInterfaceClass ) break; + } + + if( drv_id >= USBH_CLASS_DRIVER_COUNT ) + { + // skip unsupported class + p_desc = tu_desc_next(p_desc); + } + else + { + // Interface number must not be used already TODO alternate interface + TU_ASSERT( new_dev->itf2drv[desc_itf->bInterfaceNumber] == 0xff ); + new_dev->itf2drv[desc_itf->bInterfaceNumber] = drv_id; + + if (desc_itf->bInterfaceClass == TUSB_CLASS_HUB && new_dev->hub_addr != 0) + { + // TODO Attach hub to Hub is not currently supported + // skip this interface + p_desc = tu_desc_next(p_desc); + } + else + { + uint16_t itf_len = 0; + + if ( usbh_class_drivers[drv_id].open(new_dev->rhport, new_addr, desc_itf, &itf_len) ) + { + mark_interface_endpoint(new_dev->ep2drv, p_desc, itf_len, drv_id); + } + + TU_ASSERT( itf_len >= sizeof(tusb_desc_interface_t) ); + p_desc += itf_len; + } + } + } + } + + if (tuh_mount_cb) tuh_mount_cb(new_addr); + + return true; +} + +/* USB Host Driver task + * This top level thread manages all host 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(); + + tuh_task(); // tinyusb host task + } + } + @endcode + */ +void tuh_task(void) +{ + // Skip if stack is not initialized + if ( !tusb_inited() ) return; + + // Loop until there is no more events in the queue + while (1) + { + hcd_event_t event; + if ( !osal_queue_receive(_usbh_q, &event) ) return; + + switch (event.event_id) + { + case HCD_EVENT_DEVICE_ATTACH: + case HCD_EVENT_DEVICE_REMOVE: + enum_task(&event); + break; + + default: break; + } + } +} + +//--------------------------------------------------------------------+ +// INTERNAL HELPER +//--------------------------------------------------------------------+ +static inline uint8_t get_new_address(void) +{ + for (uint8_t addr=1; addr <= CFG_TUSB_HOST_DEVICE_MAX; addr++) + { + if (_usbh_devices[addr].state == TUSB_DEVICE_STATE_UNPLUG) return addr; + } + return CFG_TUSB_HOST_DEVICE_MAX+1; +} + +static inline uint8_t get_configure_number_for_device(tusb_desc_device_t* dev_desc) +{ + uint8_t config_num = 1; + + // invoke callback to ask user which configuration to select + if (tuh_device_attached_cb) + { + config_num = tu_min8(1, tuh_device_attached_cb(dev_desc) ); + } + + return config_num; +} + +// Helper marking endpoint of interface belongs to class driver +// TODO merge with usbd +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 += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } +} + + +#endif diff --git a/src/osal/osal.h b/src/osal/osal.h index 6b1d668a4..8b7527a7e 100644 --- a/src/osal/osal.h +++ b/src/osal/osal.h @@ -1,98 +1,98 @@ -/* - * 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. - */ - -#ifndef _TUSB_OSAL_H_ -#define _TUSB_OSAL_H_ - -#ifdef __cplusplus - extern "C" { -#endif - -/** \addtogroup group_osal - * @{ */ - -#include "common/tusb_common.h" - -enum -{ - OSAL_TIMEOUT_NOTIMEOUT = 0, // return immediately - OSAL_TIMEOUT_NORMAL = 10, // default timeout - OSAL_TIMEOUT_WAIT_FOREVER = 0xFFFFFFFFUL -}; - -#define OSAL_TIMEOUT_CONTROL_XFER OSAL_TIMEOUT_WAIT_FOREVER - -typedef void (*osal_task_func_t)( void * ); - -#if CFG_TUSB_OS == OPT_OS_NONE - #include "osal_none.h" -#elif CFG_TUSB_OS == OPT_OS_FREERTOS - #include "osal_freertos.h" -#elif CFG_TUSB_OS == OPT_OS_MYNEWT - #include "osal_mynewt.h" -#else - #error OS is not supported yet -#endif - -//--------------------------------------------------------------------+ -// OSAL Porting API -//--------------------------------------------------------------------+ -static inline void osal_task_delay(uint32_t msec); - -//------------- Semaphore -------------// -static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef); -static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr); -static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec); - -static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl); // TODO removed - -//------------- Mutex -------------// -static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef); -static inline bool osal_mutex_lock (osal_mutex_t sem_hdl, uint32_t msec); -static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl); - -//------------- Queue -------------// -static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef); -static inline bool osal_queue_receive(osal_queue_t const qhdl, void* data); -static inline bool osal_queue_send(osal_queue_t const qhdl, void const * data, bool in_isr); - -#if 0 // TODO remove subtask related macros later -// Sub Task -#define OSAL_SUBTASK_BEGIN -#define OSAL_SUBTASK_END return TUSB_ERROR_NONE; - -#define STASK_RETURN(_error) return _error; -#define STASK_INVOKE(_subtask, _status) (_status) = _subtask -#define STASK_ASSERT(_cond) TU_VERIFY(_cond, TUSB_ERROR_OSAL_TASK_FAILED) -#endif - -#ifdef __cplusplus - } -#endif - -/** @} */ - -#endif /* _TUSB_OSAL_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. + */ + +#ifndef _TUSB_OSAL_H_ +#define _TUSB_OSAL_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +/** \addtogroup group_osal + * @{ */ + +#include "common/tusb_common.h" + +enum +{ + OSAL_TIMEOUT_NOTIMEOUT = 0, // return immediately + OSAL_TIMEOUT_NORMAL = 10, // default timeout + OSAL_TIMEOUT_WAIT_FOREVER = 0xFFFFFFFFUL +}; + +#define OSAL_TIMEOUT_CONTROL_XFER OSAL_TIMEOUT_WAIT_FOREVER + +typedef void (*osal_task_func_t)( void * ); + +#if CFG_TUSB_OS == OPT_OS_NONE + #include "osal_none.h" +#elif CFG_TUSB_OS == OPT_OS_FREERTOS + #include "osal_freertos.h" +#elif CFG_TUSB_OS == OPT_OS_MYNEWT + #include "osal_mynewt.h" +#else + #error OS is not supported yet +#endif + +//--------------------------------------------------------------------+ +// OSAL Porting API +//--------------------------------------------------------------------+ +static inline void osal_task_delay(uint32_t msec); + +//------------- Semaphore -------------// +static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef); +static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr); +static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec); + +static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl); // TODO removed + +//------------- Mutex -------------// +static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef); +static inline bool osal_mutex_lock (osal_mutex_t sem_hdl, uint32_t msec); +static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl); + +//------------- Queue -------------// +static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef); +static inline bool osal_queue_receive(osal_queue_t const qhdl, void* data); +static inline bool osal_queue_send(osal_queue_t const qhdl, void const * data, bool in_isr); + +#if 0 // TODO remove subtask related macros later +// Sub Task +#define OSAL_SUBTASK_BEGIN +#define OSAL_SUBTASK_END return TUSB_ERROR_NONE; + +#define STASK_RETURN(_error) return _error; +#define STASK_INVOKE(_subtask, _status) (_status) = _subtask +#define STASK_ASSERT(_cond) TU_VERIFY(_cond, TUSB_ERROR_OSAL_TASK_FAILED) +#endif + +#ifdef __cplusplus + } +#endif + +/** @} */ + +#endif /* _TUSB_OSAL_H_ */ diff --git a/src/osal/osal_none.h b/src/osal/osal_none.h index 5868dfb36..b27e628a9 100644 --- a/src/osal/osal_none.h +++ b/src/osal/osal_none.h @@ -1,210 +1,210 @@ -/* - * 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. - */ - -#ifndef _TUSB_OSAL_NONE_H_ -#define _TUSB_OSAL_NONE_H_ - -#ifdef __cplusplus - extern "C" { -#endif - -//--------------------------------------------------------------------+ -// TASK API -//--------------------------------------------------------------------+ -static inline void osal_task_delay(uint32_t msec) -{ - (void) msec; - // TODO only used by Host stack, will implement using SOF - -// uint32_t start = tusb_hal_millis(); -// while ( ( tusb_hal_millis() - start ) < msec ) {} -} - -//--------------------------------------------------------------------+ -// Binary Semaphore API -//--------------------------------------------------------------------+ -typedef struct -{ - volatile uint16_t count; -}osal_semaphore_def_t; - -typedef osal_semaphore_def_t* osal_semaphore_t; - -static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef) -{ - semdef->count = 0; - return semdef; -} - -static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) -{ - (void) in_isr; - sem_hdl->count++; - return true; -} - -// TODO blocking for now -static inline bool osal_semaphore_wait (osal_semaphore_t sem_hdl, uint32_t msec) -{ - (void) msec; - - while (sem_hdl->count == 0) { } - sem_hdl->count--; - - return true; -} - -static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl) -{ - sem_hdl->count = 0; -} - -//--------------------------------------------------------------------+ -// MUTEX API -// Within tinyusb, mutex is never used in ISR context -//--------------------------------------------------------------------+ -typedef osal_semaphore_def_t osal_mutex_def_t; -typedef osal_semaphore_t osal_mutex_t; - -static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef) -{ - mdef->count = 1; - return mdef; -} - -static inline bool osal_mutex_lock (osal_mutex_t mutex_hdl, uint32_t msec) -{ - return osal_semaphore_wait(mutex_hdl, msec); -} - -static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl) -{ - return osal_semaphore_post(mutex_hdl, false); -} - -//--------------------------------------------------------------------+ -// QUEUE API -//--------------------------------------------------------------------+ -#include "common/tusb_fifo.h" - -// extern to avoid including dcd.h and hcd.h -#if TUSB_OPT_DEVICE_ENABLED -extern void dcd_int_disable(uint8_t rhport); -extern void dcd_int_enable(uint8_t rhport); -#endif - -#if TUSB_OPT_HOST_ENABLED -extern void hcd_int_disable(uint8_t rhport); -extern void hcd_int_enable(uint8_t rhport); -#endif - -typedef struct -{ - uint8_t role; // device or host - tu_fifo_t ff; -}osal_queue_def_t; - -typedef osal_queue_def_t* osal_queue_t; - -// role device/host is used by OS NONE for mutex (disable usb isr) only -#define OSAL_QUEUE_DEF(_role, _name, _depth, _type) \ - uint8_t _name##_buf[_depth*sizeof(_type)]; \ - osal_queue_def_t _name = { \ - .role = _role, \ - .ff = { \ - .buffer = _name##_buf, \ - .depth = _depth, \ - .item_size = sizeof(_type), \ - .overwritable = false, \ - }\ - } - -// lock queue by disable usb isr -static inline void _osal_q_lock(osal_queue_t qhdl) -{ - (void) qhdl; - -#if TUSB_OPT_DEVICE_ENABLED - if (qhdl->role == OPT_MODE_DEVICE) dcd_int_disable(TUD_OPT_RHPORT); -#endif - -#if TUSB_OPT_HOST_ENABLED - if (qhdl->role == OPT_MODE_HOST) hcd_int_disable(TUH_OPT_RHPORT); -#endif -} - -// unlock queue -static inline void _osal_q_unlock(osal_queue_t qhdl) -{ - (void) qhdl; - -#if TUSB_OPT_DEVICE_ENABLED - if (qhdl->role == OPT_MODE_DEVICE) dcd_int_enable(TUD_OPT_RHPORT); -#endif - -#if TUSB_OPT_HOST_ENABLED - if (qhdl->role == OPT_MODE_HOST) hcd_int_enable(TUH_OPT_RHPORT); -#endif -} - -static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) -{ - tu_fifo_clear(&qdef->ff); - return (osal_queue_t) qdef; -} - -// non blocking -static inline bool osal_queue_receive(osal_queue_t const qhdl, void* data) -{ - _osal_q_lock(qhdl); - bool success = tu_fifo_read(&qhdl->ff, data); - _osal_q_unlock(qhdl); - - return success; -} - -static inline bool osal_queue_send(osal_queue_t const qhdl, void const * data, bool in_isr) -{ - if (!in_isr) { - _osal_q_lock(qhdl); - } - - bool success = tu_fifo_write(&qhdl->ff, data); - - if (!in_isr) { - _osal_q_unlock(qhdl); - } - - TU_ASSERT(success); - - return success; -} - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_OSAL_NONE_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. + */ + +#ifndef _TUSB_OSAL_NONE_H_ +#define _TUSB_OSAL_NONE_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// TASK API +//--------------------------------------------------------------------+ +static inline void osal_task_delay(uint32_t msec) +{ + (void) msec; + // TODO only used by Host stack, will implement using SOF + +// uint32_t start = tusb_hal_millis(); +// while ( ( tusb_hal_millis() - start ) < msec ) {} +} + +//--------------------------------------------------------------------+ +// Binary Semaphore API +//--------------------------------------------------------------------+ +typedef struct +{ + volatile uint16_t count; +}osal_semaphore_def_t; + +typedef osal_semaphore_def_t* osal_semaphore_t; + +static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef) +{ + semdef->count = 0; + return semdef; +} + +static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) +{ + (void) in_isr; + sem_hdl->count++; + return true; +} + +// TODO blocking for now +static inline bool osal_semaphore_wait (osal_semaphore_t sem_hdl, uint32_t msec) +{ + (void) msec; + + while (sem_hdl->count == 0) { } + sem_hdl->count--; + + return true; +} + +static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl) +{ + sem_hdl->count = 0; +} + +//--------------------------------------------------------------------+ +// MUTEX API +// Within tinyusb, mutex is never used in ISR context +//--------------------------------------------------------------------+ +typedef osal_semaphore_def_t osal_mutex_def_t; +typedef osal_semaphore_t osal_mutex_t; + +static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef) +{ + mdef->count = 1; + return mdef; +} + +static inline bool osal_mutex_lock (osal_mutex_t mutex_hdl, uint32_t msec) +{ + return osal_semaphore_wait(mutex_hdl, msec); +} + +static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl) +{ + return osal_semaphore_post(mutex_hdl, false); +} + +//--------------------------------------------------------------------+ +// QUEUE API +//--------------------------------------------------------------------+ +#include "common/tusb_fifo.h" + +// extern to avoid including dcd.h and hcd.h +#if TUSB_OPT_DEVICE_ENABLED +extern void dcd_int_disable(uint8_t rhport); +extern void dcd_int_enable(uint8_t rhport); +#endif + +#if TUSB_OPT_HOST_ENABLED +extern void hcd_int_disable(uint8_t rhport); +extern void hcd_int_enable(uint8_t rhport); +#endif + +typedef struct +{ + uint8_t role; // device or host + tu_fifo_t ff; +}osal_queue_def_t; + +typedef osal_queue_def_t* osal_queue_t; + +// role device/host is used by OS NONE for mutex (disable usb isr) only +#define OSAL_QUEUE_DEF(_role, _name, _depth, _type) \ + uint8_t _name##_buf[_depth*sizeof(_type)]; \ + osal_queue_def_t _name = { \ + .role = _role, \ + .ff = { \ + .buffer = _name##_buf, \ + .depth = _depth, \ + .item_size = sizeof(_type), \ + .overwritable = false, \ + }\ + } + +// lock queue by disable usb isr +static inline void _osal_q_lock(osal_queue_t qhdl) +{ + (void) qhdl; + +#if TUSB_OPT_DEVICE_ENABLED + if (qhdl->role == OPT_MODE_DEVICE) dcd_int_disable(TUD_OPT_RHPORT); +#endif + +#if TUSB_OPT_HOST_ENABLED + if (qhdl->role == OPT_MODE_HOST) hcd_int_disable(TUH_OPT_RHPORT); +#endif +} + +// unlock queue +static inline void _osal_q_unlock(osal_queue_t qhdl) +{ + (void) qhdl; + +#if TUSB_OPT_DEVICE_ENABLED + if (qhdl->role == OPT_MODE_DEVICE) dcd_int_enable(TUD_OPT_RHPORT); +#endif + +#if TUSB_OPT_HOST_ENABLED + if (qhdl->role == OPT_MODE_HOST) hcd_int_enable(TUH_OPT_RHPORT); +#endif +} + +static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) +{ + tu_fifo_clear(&qdef->ff); + return (osal_queue_t) qdef; +} + +// non blocking +static inline bool osal_queue_receive(osal_queue_t const qhdl, void* data) +{ + _osal_q_lock(qhdl); + bool success = tu_fifo_read(&qhdl->ff, data); + _osal_q_unlock(qhdl); + + return success; +} + +static inline bool osal_queue_send(osal_queue_t const qhdl, void const * data, bool in_isr) +{ + if (!in_isr) { + _osal_q_lock(qhdl); + } + + bool success = tu_fifo_write(&qhdl->ff, data); + + if (!in_isr) { + _osal_q_unlock(qhdl); + } + + TU_ASSERT(success); + + return success; +} + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_OSAL_NONE_H_ */ diff --git a/src/portable/nuvoton/nuc121/dcd_nuc121.c b/src/portable/nuvoton/nuc121/dcd_nuc121.c index 237a5fd9a..57beb24bf 100644 --- a/src/portable/nuvoton/nuc121/dcd_nuc121.c +++ b/src/portable/nuvoton/nuc121/dcd_nuc121.c @@ -1,436 +1,436 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2019 Peter Lawrence - * - * 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. - */ - -/* - Theory of operation: - - The NUC121/NUC125/NUC126 USBD peripheral has eight "EP"s, but each is simplex, - so two collectively (peripheral nomenclature of "EP0" and "EP1") are needed to - implement USB EP0. PERIPH_EP0 and PERIPH_EP1 are used by this driver for - EP0_IN and EP0_OUT respectively. This leaves up to six for user usage. -*/ - -#include "tusb_option.h" - -#if TUSB_OPT_DEVICE_ENABLED && ( (CFG_TUSB_MCU == OPT_MCU_NUC121) || (CFG_TUSB_MCU == OPT_MCU_NUC126) ) - -#include "device/dcd.h" -#include "NuMicro.h" - -/* allocation of USBD RAM for Setup, EP0_IN, and and EP_OUT */ -#define PERIPH_SETUP_BUF_BASE 0 -#define PERIPH_SETUP_BUF_LEN 8 -#define PERIPH_EP0_BUF_BASE (PERIPH_SETUP_BUF_BASE + PERIPH_SETUP_BUF_LEN) -#define PERIPH_EP0_BUF_LEN CFG_TUD_ENDPOINT0_SIZE -#define PERIPH_EP1_BUF_BASE (PERIPH_EP0_BUF_BASE + PERIPH_EP0_BUF_LEN) -#define PERIPH_EP1_BUF_LEN CFG_TUD_ENDPOINT0_SIZE -#define PERIPH_EP2_BUF_BASE (PERIPH_EP1_BUF_BASE + PERIPH_EP1_BUF_LEN) - -/* rather important info unfortunately not provided by device include files: how much there is */ -#define USBD_BUF_SIZE ((CFG_TUSB_MCU == OPT_MCU_NUC121) ? 768 : 512) - -enum ep_enum -{ - PERIPH_EP0 = 0, - PERIPH_EP1 = 1, - PERIPH_EP2 = 2, - PERIPH_EP3 = 3, - PERIPH_EP4 = 4, - PERIPH_EP5 = 5, - PERIPH_EP6 = 6, - PERIPH_EP7 = 7, - PERIPH_MAX_EP, -}; - -/* set by dcd_set_address() */ -static volatile uint8_t assigned_address; - -/* reset by dcd_init(), this is used by dcd_edpt_open() to assign USBD peripheral buffer addresses */ -static uint32_t bufseg_addr; - -/* used by dcd_edpt_xfer() and the ISR to reset the data sync (DATA0/DATA1) in an EP0_IN transfer */ -static bool active_ep0_xfer; - -/* RAM table needed to track ongoing transfers performed by dcd_edpt_xfer(), dcd_in_xfer(), and the ISR */ -static struct xfer_ctl_t -{ - uint8_t *data_ptr; /* collectively, data_ptr and remaining_bytes track progress of endpoint transfers */ - uint16_t remaining_bytes; - uint16_t max_packet_size; /* needed since device driver only finds out this at runtime */ - uint16_t total_bytes; /* quantity needed to pass as argument to dcd_event_xfer_complete() (for IN endpoints) */ -} xfer_table[PERIPH_MAX_EP]; - -/* - local helper functions -*/ - -static void usb_attach(void) -{ - USBD->SE0 &= ~USBD_SE0_SE0_Msk; -} - -static void usb_detach(void) -{ - USBD->SE0 |= USBD_SE0_SE0_Msk; -} - -static void usb_control_send_zlp(void) -{ - USBD->EP[PERIPH_EP0].CFG |= USBD_CFG_DSQSYNC_Msk; - USBD->EP[PERIPH_EP0].MXPLD = 0; -} - -/* reconstruct ep_addr from particular USB Configuration Register */ -static uint8_t decode_ep_addr(USBD_EP_T *ep) -{ - uint8_t ep_addr = ep->CFG & USBD_CFG_EPNUM_Msk; - if ( USBD_CFG_EPMODE_IN == (ep->CFG & USBD_CFG_STATE_Msk) ) - ep_addr |= TUSB_DIR_IN_MASK; - return ep_addr; -} - -/* map 8-bit ep_addr into peripheral endpoint index (PERIPH_EP0...) */ -static USBD_EP_T *ep_entry(uint8_t ep_addr, bool add) -{ - USBD_EP_T *ep; - enum ep_enum ep_index; - - for (ep_index = PERIPH_EP0, ep = USBD->EP; ep_index < PERIPH_MAX_EP; ep_index++, ep++) - { - if (add) - { - /* take first peripheral endpoint that is unused */ - if (0 == (ep->CFG & USBD_CFG_STATE_Msk)) return ep; - } - else - { - /* find a peripheral endpoint that matches ep_addr */ - uint8_t candidate_ep_addr = decode_ep_addr(ep); - if (candidate_ep_addr == ep_addr) return ep; - } - } - - return NULL; -} - -/* perform an IN endpoint transfer; this is called by dcd_edpt_xfer() and the ISR */ -static void dcd_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep) -{ - uint16_t bytes_now = tu_min16(xfer->remaining_bytes, xfer->max_packet_size); - - memcpy((uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), xfer->data_ptr, bytes_now); - ep->MXPLD = bytes_now; -} - -/* centralized location for USBD interrupt enable bit mask */ -static const uint32_t enabled_irqs = USBD_INTSTS_VBDETIF_Msk | USBD_INTSTS_BUSIF_Msk | USBD_INTSTS_SETUP_Msk | USBD_INTSTS_USBIF_Msk | USBD_INTSTS_SOFIF_Msk; - -/* - NUC121/NUC125/NUC126 TinyUSB API driver implementation -*/ - -void dcd_init(uint8_t rhport) -{ - (void) rhport; - -#ifdef SUPPORT_LPM - USBD->ATTR = 0x7D0 | USBD_LPMACK; -#else - USBD->ATTR = 0x7D0; -#endif - - usb_detach(); - - USBD->STBUFSEG = PERIPH_SETUP_BUF_BASE; - - for (enum ep_enum ep_index = PERIPH_EP0; ep_index < PERIPH_MAX_EP; ep_index++) - { - USBD->EP[ep_index].CFGP &= ~USBD_CFG_STATE_Msk; - } - - /* allocate the default EP0 endpoints */ - - USBD->EP[PERIPH_EP0].CFG = USBD_CFG_CSTALL_Msk | USBD_CFG_EPMODE_IN; - USBD->EP[PERIPH_EP0].BUFSEG = PERIPH_EP0_BUF_BASE; - xfer_table[PERIPH_EP0].max_packet_size = PERIPH_EP0_BUF_LEN; - - USBD->EP[PERIPH_EP1].CFG = USBD_CFG_CSTALL_Msk | USBD_CFG_EPMODE_OUT; - USBD->EP[PERIPH_EP1].BUFSEG = PERIPH_EP1_BUF_BASE; - xfer_table[PERIPH_EP1].max_packet_size = PERIPH_EP1_BUF_LEN; - - /* USB RAM beyond what we've allocated above is available to the user */ - bufseg_addr = PERIPH_EP2_BUF_BASE; - - usb_attach(); - - USBD->INTSTS = enabled_irqs; - USBD->INTEN = enabled_irqs; -} - -void dcd_int_enable(uint8_t rhport) -{ - (void) rhport; - NVIC_EnableIRQ(USBD_IRQn); -} - -void dcd_int_disable(uint8_t rhport) -{ - (void) rhport; - NVIC_DisableIRQ(USBD_IRQn); -} - -void dcd_set_address(uint8_t rhport, uint8_t dev_addr) -{ - (void) rhport; - usb_control_send_zlp(); /* SET_ADDRESS is the one exception where TinyUSB doesn't use dcd_edpt_xfer() to generate a ZLP */ - assigned_address = dev_addr; -} - -void dcd_set_config(uint8_t rhport, uint8_t config_num) -{ - (void) rhport; - (void) config_num; -} - -void dcd_remote_wakeup(uint8_t rhport) -{ - (void) rhport; - USBD->ATTR = USBD_ATTR_RWAKEUP_Msk; -} - -bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) -{ - (void) rhport; - - USBD_EP_T *ep = ep_entry(p_endpoint_desc->bEndpointAddress, true); - TU_ASSERT(ep); - - /* mine the data for the information we need */ - int const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); - int const size = p_endpoint_desc->wMaxPacketSize.size; - tusb_xfer_type_t const type = p_endpoint_desc->bmAttributes.xfer; - struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP]; - - /* allocate buffer from USB RAM */ - ep->BUFSEG = bufseg_addr; - bufseg_addr += size; - TU_ASSERT(bufseg_addr <= USBD_BUF_SIZE); - - /* construct USB Configuration Register value and then write it */ - uint32_t cfg = tu_edpt_number(p_endpoint_desc->bEndpointAddress); - cfg |= (TUSB_DIR_IN == dir) ? USBD_CFG_EPMODE_IN : USBD_CFG_EPMODE_OUT; - if (TUSB_XFER_ISOCHRONOUS == type) - cfg |= USBD_CFG_TYPE_ISO; - ep->CFG = cfg; - - /* make a note of the endpoint size */ - xfer->max_packet_size = size; - - return true; -} - -bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) -{ - (void) rhport; - - /* mine the data for the information we need */ - tusb_dir_t dir = tu_edpt_dir(ep_addr); - USBD_EP_T *ep = ep_entry(ep_addr, false); - struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP]; - - /* store away the information we'll needing now and later */ - xfer->data_ptr = buffer; - xfer->remaining_bytes = total_bytes; - xfer->total_bytes = total_bytes; - - /* for the first of one or more EP0_IN packets in a message, the first must be DATA1 */ - if ( (0x80 == ep_addr) && !active_ep0_xfer ) ep->CFG |= USBD_CFG_DSQSYNC_Msk; - - if (TUSB_DIR_IN == dir) - dcd_in_xfer(xfer, ep); - else - ep->MXPLD = xfer->max_packet_size; - - return true; -} - -void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) -{ - (void) rhport; - USBD_EP_T *ep = ep_entry(ep_addr, false); - ep->CFGP |= USBD_CFGP_SSTALL_Msk; -} - -void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) -{ - (void) rhport; - USBD_EP_T *ep = ep_entry(ep_addr, false); - ep->CFG |= USBD_CFG_CSTALL_Msk; -} - -void USBD_IRQHandler(void) -{ - uint32_t status = USBD->INTSTS; -#ifdef SUPPORT_LPM - uint32_t state = USBD->ATTR & 0x300f; -#else - uint32_t state = USBD->ATTR & 0xf; -#endif - - if(status & USBD_INTSTS_VBDETIF_Msk) - { - if(USBD->VBUSDET & USBD_VBUSDET_VBUSDET_Msk) - { - /* USB connect */ - USBD->ATTR |= USBD_ATTR_USBEN_Msk | USBD_ATTR_PHYEN_Msk; - } - else - { - /* USB disconnect */ - USBD->ATTR &= ~USBD_ATTR_USBEN_Msk; - } - } - - if(status & USBD_INTSTS_BUSIF_Msk) - { - if(state & USBD_ATTR_USBRST_Msk) - { - /* USB bus reset */ - USBD->ATTR |= USBD_ATTR_USBEN_Msk | USBD_ATTR_PHYEN_Msk; - - /* Reset all endpoints to DATA0 */ - for(enum ep_enum ep_index = PERIPH_EP0; ep_index < PERIPH_MAX_EP; ep_index++) - USBD->EP[ep_index].CFG &= ~USBD_CFG_DSQSYNC_Msk; - - /* Reset USB device address */ - USBD->FADDR = 0; - - /* reset EP0_IN flag */ - active_ep0_xfer = false; - - dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true); - } - - if(state & USBD_ATTR_SUSPEND_Msk) - { - /* Enable USB but disable PHY */ - USBD->ATTR &= ~USBD_ATTR_PHYEN_Msk; - dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true); - } - - if(state & USBD_ATTR_RESUME_Msk) - { - /* Enable USB and enable PHY */ - USBD->ATTR |= USBD_ATTR_USBEN_Msk | USBD_ATTR_PHYEN_Msk; - dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); - } - } - - if(status & USBD_INTSTS_SETUP_Msk) - { - /* clear the data ready flag of control endpoints */ - USBD->EP[PERIPH_EP0].CFGP |= USBD_CFGP_CLRRDY_Msk; - USBD->EP[PERIPH_EP1].CFGP |= USBD_CFGP_CLRRDY_Msk; - - /* get SETUP packet from USB buffer */ - dcd_event_setup_received(0, (uint8_t *)USBD_BUF_BASE, true); - } - - if(status & USBD_INTSTS_USBIF_Msk) - { - if (status & USBD_INTSTS_EPEVT0_Msk) /* PERIPH_EP0 (EP0_IN) event: this is treated separately from the rest */ - { - /* given ACK from host has happened, we can now set the address (if not already done) */ - if((USBD->FADDR != assigned_address) && (USBD->FADDR == 0)) USBD->FADDR = assigned_address; - - uint16_t const available_bytes = USBD->EP[PERIPH_EP0].MXPLD; - - active_ep0_xfer = (available_bytes == xfer_table[PERIPH_EP0].max_packet_size); - - dcd_event_xfer_complete(0, 0x80, available_bytes, XFER_RESULT_SUCCESS, true); - } - - /* service PERIPH_EP1 through PERIPH_EP7 */ - enum ep_enum ep_index; - uint32_t mask; - struct xfer_ctl_t *xfer; - USBD_EP_T *ep; - for (ep_index = PERIPH_EP1, mask = USBD_INTSTS_EPEVT1_Msk, xfer = &xfer_table[PERIPH_EP1], ep = &USBD->EP[PERIPH_EP1]; ep_index <= PERIPH_EP7; ep_index++, mask <<= 1, xfer++, ep++) - { - if(status & mask) - { - USBD->INTSTS = mask; - - uint16_t const available_bytes = ep->MXPLD; - uint8_t const ep_addr = decode_ep_addr(ep); - bool const out_ep = !(ep_addr & TUSB_DIR_IN_MASK); - - if (out_ep) - { - /* copy the data from the PC to the previously provided buffer */ - memcpy(xfer->data_ptr, (uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), available_bytes); - xfer->remaining_bytes -= available_bytes; - xfer->data_ptr += available_bytes; - - /* when the transfer is finished, alert TinyUSB; otherwise, accept more data */ - if ( (0 == xfer->remaining_bytes) || (available_bytes < xfer->max_packet_size) ) - dcd_event_xfer_complete(0, ep_addr, xfer->total_bytes, XFER_RESULT_SUCCESS, true); - else if (xfer->remaining_bytes) - ep->MXPLD = xfer->max_packet_size; - } - else - { - /* update the bookkeeping to reflect the data that has now been sent to the PC */ - xfer->remaining_bytes -= available_bytes; - xfer->data_ptr += available_bytes; - - /* if more data to send, send it; otherwise, alert TinyUSB that we've finished */ - if (xfer->remaining_bytes) - dcd_in_xfer(xfer, ep); - else - dcd_event_xfer_complete(0, ep_addr, xfer->total_bytes, XFER_RESULT_SUCCESS, true); - } - } - } - } - - if(status & USBD_INTSTS_SOFIF_Msk) - { - /* Start-Of-Frame event */ - dcd_event_bus_signal(0, DCD_EVENT_SOF, true); - } - - /* acknowledge all interrupts */ - USBD->INTSTS = status & enabled_irqs; -} - -void dcd_isr(uint8_t rhport) -{ - (void) rhport; - USBD_IRQHandler(); -} - -#endif +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Peter Lawrence + * + * 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. + */ + +/* + Theory of operation: + + The NUC121/NUC125/NUC126 USBD peripheral has eight "EP"s, but each is simplex, + so two collectively (peripheral nomenclature of "EP0" and "EP1") are needed to + implement USB EP0. PERIPH_EP0 and PERIPH_EP1 are used by this driver for + EP0_IN and EP0_OUT respectively. This leaves up to six for user usage. +*/ + +#include "tusb_option.h" + +#if TUSB_OPT_DEVICE_ENABLED && ( (CFG_TUSB_MCU == OPT_MCU_NUC121) || (CFG_TUSB_MCU == OPT_MCU_NUC126) ) + +#include "device/dcd.h" +#include "NuMicro.h" + +/* allocation of USBD RAM for Setup, EP0_IN, and and EP_OUT */ +#define PERIPH_SETUP_BUF_BASE 0 +#define PERIPH_SETUP_BUF_LEN 8 +#define PERIPH_EP0_BUF_BASE (PERIPH_SETUP_BUF_BASE + PERIPH_SETUP_BUF_LEN) +#define PERIPH_EP0_BUF_LEN CFG_TUD_ENDPOINT0_SIZE +#define PERIPH_EP1_BUF_BASE (PERIPH_EP0_BUF_BASE + PERIPH_EP0_BUF_LEN) +#define PERIPH_EP1_BUF_LEN CFG_TUD_ENDPOINT0_SIZE +#define PERIPH_EP2_BUF_BASE (PERIPH_EP1_BUF_BASE + PERIPH_EP1_BUF_LEN) + +/* rather important info unfortunately not provided by device include files: how much there is */ +#define USBD_BUF_SIZE ((CFG_TUSB_MCU == OPT_MCU_NUC121) ? 768 : 512) + +enum ep_enum +{ + PERIPH_EP0 = 0, + PERIPH_EP1 = 1, + PERIPH_EP2 = 2, + PERIPH_EP3 = 3, + PERIPH_EP4 = 4, + PERIPH_EP5 = 5, + PERIPH_EP6 = 6, + PERIPH_EP7 = 7, + PERIPH_MAX_EP, +}; + +/* set by dcd_set_address() */ +static volatile uint8_t assigned_address; + +/* reset by dcd_init(), this is used by dcd_edpt_open() to assign USBD peripheral buffer addresses */ +static uint32_t bufseg_addr; + +/* used by dcd_edpt_xfer() and the ISR to reset the data sync (DATA0/DATA1) in an EP0_IN transfer */ +static bool active_ep0_xfer; + +/* RAM table needed to track ongoing transfers performed by dcd_edpt_xfer(), dcd_in_xfer(), and the ISR */ +static struct xfer_ctl_t +{ + uint8_t *data_ptr; /* collectively, data_ptr and remaining_bytes track progress of endpoint transfers */ + uint16_t remaining_bytes; + uint16_t max_packet_size; /* needed since device driver only finds out this at runtime */ + uint16_t total_bytes; /* quantity needed to pass as argument to dcd_event_xfer_complete() (for IN endpoints) */ +} xfer_table[PERIPH_MAX_EP]; + +/* + local helper functions +*/ + +static void usb_attach(void) +{ + USBD->SE0 &= ~USBD_SE0_SE0_Msk; +} + +static void usb_detach(void) +{ + USBD->SE0 |= USBD_SE0_SE0_Msk; +} + +static void usb_control_send_zlp(void) +{ + USBD->EP[PERIPH_EP0].CFG |= USBD_CFG_DSQSYNC_Msk; + USBD->EP[PERIPH_EP0].MXPLD = 0; +} + +/* reconstruct ep_addr from particular USB Configuration Register */ +static uint8_t decode_ep_addr(USBD_EP_T *ep) +{ + uint8_t ep_addr = ep->CFG & USBD_CFG_EPNUM_Msk; + if ( USBD_CFG_EPMODE_IN == (ep->CFG & USBD_CFG_STATE_Msk) ) + ep_addr |= TUSB_DIR_IN_MASK; + return ep_addr; +} + +/* map 8-bit ep_addr into peripheral endpoint index (PERIPH_EP0...) */ +static USBD_EP_T *ep_entry(uint8_t ep_addr, bool add) +{ + USBD_EP_T *ep; + enum ep_enum ep_index; + + for (ep_index = PERIPH_EP0, ep = USBD->EP; ep_index < PERIPH_MAX_EP; ep_index++, ep++) + { + if (add) + { + /* take first peripheral endpoint that is unused */ + if (0 == (ep->CFG & USBD_CFG_STATE_Msk)) return ep; + } + else + { + /* find a peripheral endpoint that matches ep_addr */ + uint8_t candidate_ep_addr = decode_ep_addr(ep); + if (candidate_ep_addr == ep_addr) return ep; + } + } + + return NULL; +} + +/* perform an IN endpoint transfer; this is called by dcd_edpt_xfer() and the ISR */ +static void dcd_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep) +{ + uint16_t bytes_now = tu_min16(xfer->remaining_bytes, xfer->max_packet_size); + + memcpy((uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), xfer->data_ptr, bytes_now); + ep->MXPLD = bytes_now; +} + +/* centralized location for USBD interrupt enable bit mask */ +static const uint32_t enabled_irqs = USBD_INTSTS_VBDETIF_Msk | USBD_INTSTS_BUSIF_Msk | USBD_INTSTS_SETUP_Msk | USBD_INTSTS_USBIF_Msk | USBD_INTSTS_SOFIF_Msk; + +/* + NUC121/NUC125/NUC126 TinyUSB API driver implementation +*/ + +void dcd_init(uint8_t rhport) +{ + (void) rhport; + +#ifdef SUPPORT_LPM + USBD->ATTR = 0x7D0 | USBD_LPMACK; +#else + USBD->ATTR = 0x7D0; +#endif + + usb_detach(); + + USBD->STBUFSEG = PERIPH_SETUP_BUF_BASE; + + for (enum ep_enum ep_index = PERIPH_EP0; ep_index < PERIPH_MAX_EP; ep_index++) + { + USBD->EP[ep_index].CFGP &= ~USBD_CFG_STATE_Msk; + } + + /* allocate the default EP0 endpoints */ + + USBD->EP[PERIPH_EP0].CFG = USBD_CFG_CSTALL_Msk | USBD_CFG_EPMODE_IN; + USBD->EP[PERIPH_EP0].BUFSEG = PERIPH_EP0_BUF_BASE; + xfer_table[PERIPH_EP0].max_packet_size = PERIPH_EP0_BUF_LEN; + + USBD->EP[PERIPH_EP1].CFG = USBD_CFG_CSTALL_Msk | USBD_CFG_EPMODE_OUT; + USBD->EP[PERIPH_EP1].BUFSEG = PERIPH_EP1_BUF_BASE; + xfer_table[PERIPH_EP1].max_packet_size = PERIPH_EP1_BUF_LEN; + + /* USB RAM beyond what we've allocated above is available to the user */ + bufseg_addr = PERIPH_EP2_BUF_BASE; + + usb_attach(); + + USBD->INTSTS = enabled_irqs; + USBD->INTEN = enabled_irqs; +} + +void dcd_int_enable(uint8_t rhport) +{ + (void) rhport; + NVIC_EnableIRQ(USBD_IRQn); +} + +void dcd_int_disable(uint8_t rhport) +{ + (void) rhport; + NVIC_DisableIRQ(USBD_IRQn); +} + +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) +{ + (void) rhport; + usb_control_send_zlp(); /* SET_ADDRESS is the one exception where TinyUSB doesn't use dcd_edpt_xfer() to generate a ZLP */ + assigned_address = dev_addr; +} + +void dcd_set_config(uint8_t rhport, uint8_t config_num) +{ + (void) rhport; + (void) config_num; +} + +void dcd_remote_wakeup(uint8_t rhport) +{ + (void) rhport; + USBD->ATTR = USBD_ATTR_RWAKEUP_Msk; +} + +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) +{ + (void) rhport; + + USBD_EP_T *ep = ep_entry(p_endpoint_desc->bEndpointAddress, true); + TU_ASSERT(ep); + + /* mine the data for the information we need */ + int const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); + int const size = p_endpoint_desc->wMaxPacketSize.size; + tusb_xfer_type_t const type = p_endpoint_desc->bmAttributes.xfer; + struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP]; + + /* allocate buffer from USB RAM */ + ep->BUFSEG = bufseg_addr; + bufseg_addr += size; + TU_ASSERT(bufseg_addr <= USBD_BUF_SIZE); + + /* construct USB Configuration Register value and then write it */ + uint32_t cfg = tu_edpt_number(p_endpoint_desc->bEndpointAddress); + cfg |= (TUSB_DIR_IN == dir) ? USBD_CFG_EPMODE_IN : USBD_CFG_EPMODE_OUT; + if (TUSB_XFER_ISOCHRONOUS == type) + cfg |= USBD_CFG_TYPE_ISO; + ep->CFG = cfg; + + /* make a note of the endpoint size */ + xfer->max_packet_size = size; + + return true; +} + +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) +{ + (void) rhport; + + /* mine the data for the information we need */ + tusb_dir_t dir = tu_edpt_dir(ep_addr); + USBD_EP_T *ep = ep_entry(ep_addr, false); + struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP]; + + /* store away the information we'll needing now and later */ + xfer->data_ptr = buffer; + xfer->remaining_bytes = total_bytes; + xfer->total_bytes = total_bytes; + + /* for the first of one or more EP0_IN packets in a message, the first must be DATA1 */ + if ( (0x80 == ep_addr) && !active_ep0_xfer ) ep->CFG |= USBD_CFG_DSQSYNC_Msk; + + if (TUSB_DIR_IN == dir) + dcd_in_xfer(xfer, ep); + else + ep->MXPLD = xfer->max_packet_size; + + return true; +} + +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + USBD_EP_T *ep = ep_entry(ep_addr, false); + ep->CFGP |= USBD_CFGP_SSTALL_Msk; +} + +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + USBD_EP_T *ep = ep_entry(ep_addr, false); + ep->CFG |= USBD_CFG_CSTALL_Msk; +} + +void USBD_IRQHandler(void) +{ + uint32_t status = USBD->INTSTS; +#ifdef SUPPORT_LPM + uint32_t state = USBD->ATTR & 0x300f; +#else + uint32_t state = USBD->ATTR & 0xf; +#endif + + if(status & USBD_INTSTS_VBDETIF_Msk) + { + if(USBD->VBUSDET & USBD_VBUSDET_VBUSDET_Msk) + { + /* USB connect */ + USBD->ATTR |= USBD_ATTR_USBEN_Msk | USBD_ATTR_PHYEN_Msk; + } + else + { + /* USB disconnect */ + USBD->ATTR &= ~USBD_ATTR_USBEN_Msk; + } + } + + if(status & USBD_INTSTS_BUSIF_Msk) + { + if(state & USBD_ATTR_USBRST_Msk) + { + /* USB bus reset */ + USBD->ATTR |= USBD_ATTR_USBEN_Msk | USBD_ATTR_PHYEN_Msk; + + /* Reset all endpoints to DATA0 */ + for(enum ep_enum ep_index = PERIPH_EP0; ep_index < PERIPH_MAX_EP; ep_index++) + USBD->EP[ep_index].CFG &= ~USBD_CFG_DSQSYNC_Msk; + + /* Reset USB device address */ + USBD->FADDR = 0; + + /* reset EP0_IN flag */ + active_ep0_xfer = false; + + dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true); + } + + if(state & USBD_ATTR_SUSPEND_Msk) + { + /* Enable USB but disable PHY */ + USBD->ATTR &= ~USBD_ATTR_PHYEN_Msk; + dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true); + } + + if(state & USBD_ATTR_RESUME_Msk) + { + /* Enable USB and enable PHY */ + USBD->ATTR |= USBD_ATTR_USBEN_Msk | USBD_ATTR_PHYEN_Msk; + dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); + } + } + + if(status & USBD_INTSTS_SETUP_Msk) + { + /* clear the data ready flag of control endpoints */ + USBD->EP[PERIPH_EP0].CFGP |= USBD_CFGP_CLRRDY_Msk; + USBD->EP[PERIPH_EP1].CFGP |= USBD_CFGP_CLRRDY_Msk; + + /* get SETUP packet from USB buffer */ + dcd_event_setup_received(0, (uint8_t *)USBD_BUF_BASE, true); + } + + if(status & USBD_INTSTS_USBIF_Msk) + { + if (status & USBD_INTSTS_EPEVT0_Msk) /* PERIPH_EP0 (EP0_IN) event: this is treated separately from the rest */ + { + /* given ACK from host has happened, we can now set the address (if not already done) */ + if((USBD->FADDR != assigned_address) && (USBD->FADDR == 0)) USBD->FADDR = assigned_address; + + uint16_t const available_bytes = USBD->EP[PERIPH_EP0].MXPLD; + + active_ep0_xfer = (available_bytes == xfer_table[PERIPH_EP0].max_packet_size); + + dcd_event_xfer_complete(0, 0x80, available_bytes, XFER_RESULT_SUCCESS, true); + } + + /* service PERIPH_EP1 through PERIPH_EP7 */ + enum ep_enum ep_index; + uint32_t mask; + struct xfer_ctl_t *xfer; + USBD_EP_T *ep; + for (ep_index = PERIPH_EP1, mask = USBD_INTSTS_EPEVT1_Msk, xfer = &xfer_table[PERIPH_EP1], ep = &USBD->EP[PERIPH_EP1]; ep_index <= PERIPH_EP7; ep_index++, mask <<= 1, xfer++, ep++) + { + if(status & mask) + { + USBD->INTSTS = mask; + + uint16_t const available_bytes = ep->MXPLD; + uint8_t const ep_addr = decode_ep_addr(ep); + bool const out_ep = !(ep_addr & TUSB_DIR_IN_MASK); + + if (out_ep) + { + /* copy the data from the PC to the previously provided buffer */ + memcpy(xfer->data_ptr, (uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), available_bytes); + xfer->remaining_bytes -= available_bytes; + xfer->data_ptr += available_bytes; + + /* when the transfer is finished, alert TinyUSB; otherwise, accept more data */ + if ( (0 == xfer->remaining_bytes) || (available_bytes < xfer->max_packet_size) ) + dcd_event_xfer_complete(0, ep_addr, xfer->total_bytes, XFER_RESULT_SUCCESS, true); + else if (xfer->remaining_bytes) + ep->MXPLD = xfer->max_packet_size; + } + else + { + /* update the bookkeeping to reflect the data that has now been sent to the PC */ + xfer->remaining_bytes -= available_bytes; + xfer->data_ptr += available_bytes; + + /* if more data to send, send it; otherwise, alert TinyUSB that we've finished */ + if (xfer->remaining_bytes) + dcd_in_xfer(xfer, ep); + else + dcd_event_xfer_complete(0, ep_addr, xfer->total_bytes, XFER_RESULT_SUCCESS, true); + } + } + } + } + + if(status & USBD_INTSTS_SOFIF_Msk) + { + /* Start-Of-Frame event */ + dcd_event_bus_signal(0, DCD_EVENT_SOF, true); + } + + /* acknowledge all interrupts */ + USBD->INTSTS = status & enabled_irqs; +} + +void dcd_isr(uint8_t rhport) +{ + (void) rhport; + USBD_IRQHandler(); +} + +#endif diff --git a/src/tusb.c b/src/tusb.c index f68387450..5dc6825d7 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -1,148 +1,148 @@ -/* - * 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_HOST_ENABLED || TUSB_OPT_DEVICE_ENABLED - -#include "tusb.h" - -static bool _initialized = false; - -// TODO clean up -#if TUSB_OPT_DEVICE_ENABLED -#include "device/usbd_pvt.h" -#endif - -bool tusb_init(void) -{ - // skip if already initialized - if (_initialized) return true; - -#if TUSB_OPT_HOST_ENABLED - TU_ASSERT( usbh_init() ); // init host stack -#endif - -#if TUSB_OPT_DEVICE_ENABLED - TU_ASSERT ( tud_init() ); // init device stack -#endif - - _initialized = true; - - return TUSB_ERROR_NONE; -} - -bool tusb_inited(void) -{ - return _initialized; -} - -/*------------------------------------------------------------------*/ -/* Debug - *------------------------------------------------------------------*/ -#if CFG_TUSB_DEBUG -#include - -char const* const tusb_strerr[TUSB_ERROR_COUNT] = { ERROR_TABLE(ERROR_STRING) }; - -static void dump_str_line(uint8_t const* buf, uint16_t count) -{ - // each line is 16 bytes - for(int i=0; i + +char const* const tusb_strerr[TUSB_ERROR_COUNT] = { ERROR_TABLE(ERROR_STRING) }; + +static void dump_str_line(uint8_t const* buf, uint16_t count) +{ + // each line is 16 bytes + for(int i=0; i= 0 ) - -// Which roothub port is configured as device -#define TUD_OPT_RHPORT ( (CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE) ? 0 : ((CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE) ? 1 : -1) ) - -#if TUD_OPT_RHPORT == 0 -#define TUD_OPT_HIGH_SPEED ( CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED ) -#else -#define TUD_OPT_HIGH_SPEED ( CFG_TUSB_RHPORT1_MODE & OPT_MODE_HIGH_SPEED ) -#endif - -#define TUSB_OPT_DEVICE_ENABLED ( TUD_OPT_RHPORT >= 0 ) - - -//--------------------------------------------------------------------+ -// COMMON OPTIONS -//--------------------------------------------------------------------+ - -// Debug enable to print out error message -#ifndef CFG_TUSB_DEBUG - #define CFG_TUSB_DEBUG 0 -#endif - -// place data in accessible RAM for usb controller -#ifndef CFG_TUSB_MEM_SECTION -#define CFG_TUSB_MEM_SECTION -#endif - -#ifndef CFG_TUSB_MEM_ALIGN -#define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4) -#endif - -#ifndef CFG_TUSB_OS -#define CFG_TUSB_OS OPT_OS_NONE -#endif - -//-------------------------------------------------------------------- -// DEVICE OPTIONS -//-------------------------------------------------------------------- - -#ifndef CFG_TUD_ENDPOINT0_SIZE - #define CFG_TUD_ENDPOINT0_SIZE 64 -#endif - -#ifndef CFG_TUD_CDC - #define CFG_TUD_CDC 0 -#endif - -#ifndef CFG_TUD_MSC - #define CFG_TUD_MSC 0 -#endif - -#ifndef CFG_TUD_HID - #define CFG_TUD_HID 0 -#endif - -#ifndef CFG_TUD_MIDI - #define CFG_TUD_MIDI 0 -#endif - -#ifndef CFG_TUD_VENDOR - #define CFG_TUD_VENDOR 0 -#endif - -#ifndef CFG_TUD_USBTMC - #define CFG_TUD_USBTMC 0 -#endif - -#ifndef CFG_TUD_DFU_RT - #define CFG_TUD_DFU_RT 0 -#endif - - -//-------------------------------------------------------------------- -// HOST OPTIONS -//-------------------------------------------------------------------- -#if TUSB_OPT_HOST_ENABLED - #ifndef CFG_TUSB_HOST_DEVICE_MAX - #define CFG_TUSB_HOST_DEVICE_MAX 1 - #warning CFG_TUSB_HOST_DEVICE_MAX is not defined, default value is 1 - #endif - - //------------- HUB CLASS -------------// - #if CFG_TUH_HUB && (CFG_TUSB_HOST_DEVICE_MAX == 1) - #error there is no benefit enable hub with max device is 1. Please disable hub or increase CFG_TUSB_HOST_DEVICE_MAX - #endif - - //------------- HID CLASS -------------// - #define HOST_CLASS_HID ( CFG_TUH_HID_KEYBOARD + CFG_TUH_HID_MOUSE + CFG_TUSB_HOST_HID_GENERIC ) - - #ifndef CFG_TUSB_HOST_ENUM_BUFFER_SIZE - #define CFG_TUSB_HOST_ENUM_BUFFER_SIZE 256 - #endif - - //------------- CLASS -------------// -#endif // TUSB_OPT_HOST_ENABLED - - -//------------------------------------------------------------------ -// Configuration Validation -//------------------------------------------------------------------ -#if CFG_TUD_ENDPOINT0_SIZE > 64 - #error Control Endpoint Max Packet Size cannot be larger than 64 -#endif - -#endif /* _TUSB_OPTION_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. + */ + +#ifndef _TUSB_OPTION_H_ +#define _TUSB_OPTION_H_ + +#define TUSB_VERSION_MAJOR 0 +#define TUSB_VERSION_MINOR 5 +#define TUSB_VERSION_REVISION 0 +#define TUSB_VERSION_STRING TU_STRING(TUSB_VERSION_MAJOR) "." TU_STRING(TUSB_VERSION_MINOR) "." TU_STRING(TUSB_VERSION_REVISION) + +/** \defgroup group_mcu Supported MCU + * \ref CFG_TUSB_MCU must be defined to one of these + * @{ */ + +#define OPT_MCU_NONE 0 + +// LPC +#define OPT_MCU_LPC11UXX 1 ///< NXP LPC11Uxx +#define OPT_MCU_LPC13XX 2 ///< NXP LPC13xx +#define OPT_MCU_LPC15XX 3 ///< NXP LPC15xx +#define OPT_MCU_LPC175X_6X 4 ///< NXP LPC175x, LPC176x +#define OPT_MCU_LPC177X_8X 5 ///< NXP LPC177x, LPC178x +#define OPT_MCU_LPC18XX 6 ///< NXP LPC18xx +#define OPT_MCU_LPC40XX 7 ///< NXP LPC40xx +#define OPT_MCU_LPC43XX 8 ///< NXP LPC43xx +#define OPT_MCU_LPC51UXX 9 ///< NXP LPC51U6x +#define OPT_MCU_LPC54XXX 10 ///< NXP LPC54xxx +#define OPT_MCU_LPC55XX 11 ///< NXP LPC55xx + +// NRF +#define OPT_MCU_NRF5X 100 ///< Nordic nRF5x series + +// SAM +#define OPT_MCU_SAMD21 200 ///< MicroChip SAMD21 +#define OPT_MCU_SAMD51 201 ///< MicroChip SAMD51 +#define OPT_MCU_SAMG 202 ///< MicroChip SAMDG series + +// STM32 +#define OPT_MCU_STM32F0 300 ///< ST STM32F0 +#define OPT_MCU_STM32F1 301 ///< ST STM32F1 +#define OPT_MCU_STM32F2 302 ///< ST STM32F2 +#define OPT_MCU_STM32F3 303 ///< ST STM32F3 +#define OPT_MCU_STM32F4 304 ///< ST STM32F4 +#define OPT_MCU_STM32F7 305 ///< ST STM32F7 +#define OPT_MCU_STM32H7 306 ///< ST STM32H7 +#define OPT_MCU_STM32L0 307 ///< ST STM32L0 +#define OPT_MCU_STM32L1 308 ///< ST STM32L1 +#define OPT_MCU_STM32L4 309 ///< ST STM32L4 + +#define OPT_MCU_CXD56 400 ///< SONY CXD56 + +#define OPT_MCU_VALENTYUSB_EPTRI 600 ///< Fomu eptri config + +#define OPT_MCU_MIMXRT10XX 700 ///< NXP iMX RT10xx + +#define OPT_MCU_NUC121 800 +#define OPT_MCU_NUC126 801 + +/** @} */ + +/** \defgroup group_supported_os Supported RTOS + * \ref CFG_TUSB_OS must be defined to one of these + * @{ */ +#define OPT_OS_NONE 1 ///< No RTOS +#define OPT_OS_FREERTOS 2 ///< FreeRTOS +#define OPT_OS_MYNEWT 3 ///< Mynewt OS +/** @} */ + + +// Allow to use command line to change the config name/location +#ifndef CFG_TUSB_CONFIG_FILE + #define CFG_TUSB_CONFIG_FILE "tusb_config.h" +#endif + +#include CFG_TUSB_CONFIG_FILE + +/** \addtogroup group_configuration + * @{ */ + +//-------------------------------------------------------------------- +// CONTROLLER +// Only 1 roothub port can be configured to be device and/or host. +// tinyusb does not support dual devices or dual host configuration +//-------------------------------------------------------------------- +/** \defgroup group_mode Controller Mode Selection + * \brief CFG_TUSB_CONTROLLER_N_MODE must be defined with these + * @{ */ +#define OPT_MODE_NONE 0x00 ///< Disabled +#define OPT_MODE_DEVICE 0x01 ///< Device Mode +#define OPT_MODE_HOST 0x02 ///< Host Mode +#define OPT_MODE_HIGH_SPEED 0x10 ///< High speed +/** @} */ + +#ifndef CFG_TUSB_RHPORT0_MODE + #define CFG_TUSB_RHPORT0_MODE OPT_MODE_NONE +#endif + +#ifndef CFG_TUSB_RHPORT1_MODE + #define CFG_TUSB_RHPORT1_MODE OPT_MODE_NONE +#endif + +#if ((CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST) && (CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST)) || \ + ((CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE) && (CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE)) + #error "tinyusb does not support same modes on more than 1 roothub port" +#endif + +// Which roothub port is configured as host +#define TUH_OPT_RHPORT ( (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST) ? 0 : ((CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST) ? 1 : -1) ) +#define TUSB_OPT_HOST_ENABLED ( TUH_OPT_RHPORT >= 0 ) + +// Which roothub port is configured as device +#define TUD_OPT_RHPORT ( (CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE) ? 0 : ((CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE) ? 1 : -1) ) + +#if TUD_OPT_RHPORT == 0 +#define TUD_OPT_HIGH_SPEED ( CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED ) +#else +#define TUD_OPT_HIGH_SPEED ( CFG_TUSB_RHPORT1_MODE & OPT_MODE_HIGH_SPEED ) +#endif + +#define TUSB_OPT_DEVICE_ENABLED ( TUD_OPT_RHPORT >= 0 ) + + +//--------------------------------------------------------------------+ +// COMMON OPTIONS +//--------------------------------------------------------------------+ + +// Debug enable to print out error message +#ifndef CFG_TUSB_DEBUG + #define CFG_TUSB_DEBUG 0 +#endif + +// place data in accessible RAM for usb controller +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4) +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +//-------------------------------------------------------------------- +// DEVICE OPTIONS +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE + #define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +#ifndef CFG_TUD_CDC + #define CFG_TUD_CDC 0 +#endif + +#ifndef CFG_TUD_MSC + #define CFG_TUD_MSC 0 +#endif + +#ifndef CFG_TUD_HID + #define CFG_TUD_HID 0 +#endif + +#ifndef CFG_TUD_MIDI + #define CFG_TUD_MIDI 0 +#endif + +#ifndef CFG_TUD_VENDOR + #define CFG_TUD_VENDOR 0 +#endif + +#ifndef CFG_TUD_USBTMC + #define CFG_TUD_USBTMC 0 +#endif + +#ifndef CFG_TUD_DFU_RT + #define CFG_TUD_DFU_RT 0 +#endif + + +//-------------------------------------------------------------------- +// HOST OPTIONS +//-------------------------------------------------------------------- +#if TUSB_OPT_HOST_ENABLED + #ifndef CFG_TUSB_HOST_DEVICE_MAX + #define CFG_TUSB_HOST_DEVICE_MAX 1 + #warning CFG_TUSB_HOST_DEVICE_MAX is not defined, default value is 1 + #endif + + //------------- HUB CLASS -------------// + #if CFG_TUH_HUB && (CFG_TUSB_HOST_DEVICE_MAX == 1) + #error there is no benefit enable hub with max device is 1. Please disable hub or increase CFG_TUSB_HOST_DEVICE_MAX + #endif + + //------------- HID CLASS -------------// + #define HOST_CLASS_HID ( CFG_TUH_HID_KEYBOARD + CFG_TUH_HID_MOUSE + CFG_TUSB_HOST_HID_GENERIC ) + + #ifndef CFG_TUSB_HOST_ENUM_BUFFER_SIZE + #define CFG_TUSB_HOST_ENUM_BUFFER_SIZE 256 + #endif + + //------------- CLASS -------------// +#endif // TUSB_OPT_HOST_ENABLED + + +//------------------------------------------------------------------ +// Configuration Validation +//------------------------------------------------------------------ +#if CFG_TUD_ENDPOINT0_SIZE > 64 + #error Control Endpoint Max Packet Size cannot be larger than 64 +#endif + +#endif /* _TUSB_OPTION_H_ */ + +/** @} */ -- cgit v1.3.1 From c04bc07c2926ccc2d3404cd8ebb8c7aee69c04ee Mon Sep 17 00:00:00 2001 From: Peter Lawrence <12226419+majbthrd@users.noreply.github.com> Date: Wed, 15 Jan 2020 20:09:50 -0600 Subject: nuc121: enhanced driver and hopefully added readability --- src/portable/nuvoton/nuc121/dcd_nuc121.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) (limited to 'src/portable') diff --git a/src/portable/nuvoton/nuc121/dcd_nuc121.c b/src/portable/nuvoton/nuc121/dcd_nuc121.c index 237a5fd9a..71211af0c 100644 --- a/src/portable/nuvoton/nuc121/dcd_nuc121.c +++ b/src/portable/nuvoton/nuc121/dcd_nuc121.c @@ -77,8 +77,11 @@ static bool active_ep0_xfer; /* RAM table needed to track ongoing transfers performed by dcd_edpt_xfer(), dcd_in_xfer(), and the ISR */ static struct xfer_ctl_t { - uint8_t *data_ptr; /* collectively, data_ptr and remaining_bytes track progress of endpoint transfers */ - uint16_t remaining_bytes; + uint8_t *data_ptr; /* data_ptr tracks where to next copy data to (for OUT) or from (for IN) */ + union { + uint16_t in_remaining_bytes; /* for IN endpoints, we track how many bytes are left to transfer */ + uint16_t out_bytes_so_far; /* but for OUT endpoints, we track how many bytes we've transferred so far */ + }; uint16_t max_packet_size; /* needed since device driver only finds out this at runtime */ uint16_t total_bytes; /* quantity needed to pass as argument to dcd_event_xfer_complete() (for IN endpoints) */ } xfer_table[PERIPH_MAX_EP]; @@ -139,7 +142,7 @@ static USBD_EP_T *ep_entry(uint8_t ep_addr, bool add) /* perform an IN endpoint transfer; this is called by dcd_edpt_xfer() and the ISR */ static void dcd_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep) { - uint16_t bytes_now = tu_min16(xfer->remaining_bytes, xfer->max_packet_size); + uint16_t bytes_now = tu_min16(xfer->in_remaining_bytes, xfer->max_packet_size); memcpy((uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), xfer->data_ptr, bytes_now); ep->MXPLD = bytes_now; @@ -263,16 +266,21 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to /* store away the information we'll needing now and later */ xfer->data_ptr = buffer; - xfer->remaining_bytes = total_bytes; + xfer->in_remaining_bytes = total_bytes; xfer->total_bytes = total_bytes; /* for the first of one or more EP0_IN packets in a message, the first must be DATA1 */ if ( (0x80 == ep_addr) && !active_ep0_xfer ) ep->CFG |= USBD_CFG_DSQSYNC_Msk; if (TUSB_DIR_IN == dir) + { dcd_in_xfer(xfer, ep); + } else + { + xfer->out_bytes_so_far = 0; ep->MXPLD = xfer->max_packet_size; + } return true; } @@ -392,23 +400,23 @@ void USBD_IRQHandler(void) { /* copy the data from the PC to the previously provided buffer */ memcpy(xfer->data_ptr, (uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), available_bytes); - xfer->remaining_bytes -= available_bytes; + xfer->out_bytes_so_far += available_bytes; xfer->data_ptr += available_bytes; /* when the transfer is finished, alert TinyUSB; otherwise, accept more data */ - if ( (0 == xfer->remaining_bytes) || (available_bytes < xfer->max_packet_size) ) - dcd_event_xfer_complete(0, ep_addr, xfer->total_bytes, XFER_RESULT_SUCCESS, true); - else if (xfer->remaining_bytes) + if ( (xfer->total_bytes == xfer->out_bytes_so_far) || (available_bytes < xfer->max_packet_size) ) + dcd_event_xfer_complete(0, ep_addr, xfer->out_bytes_so_far, XFER_RESULT_SUCCESS, true); + else ep->MXPLD = xfer->max_packet_size; } else { /* update the bookkeeping to reflect the data that has now been sent to the PC */ - xfer->remaining_bytes -= available_bytes; + xfer->in_remaining_bytes -= available_bytes; xfer->data_ptr += available_bytes; /* if more data to send, send it; otherwise, alert TinyUSB that we've finished */ - if (xfer->remaining_bytes) + if (xfer->in_remaining_bytes) dcd_in_xfer(xfer, ep); else dcd_event_xfer_complete(0, ep_addr, xfer->total_bytes, XFER_RESULT_SUCCESS, true); -- cgit v1.3.1 From 2ed395bde313ad7d2591dc23c686469d16884e79 Mon Sep 17 00:00:00 2001 From: Peter Lawrence <12226419+majbthrd@users.noreply.github.com> Date: Wed, 15 Jan 2020 21:40:24 -0600 Subject: nuc121: survive USB bus reset --- src/portable/nuvoton/nuc121/dcd_nuc121.c | 61 +++++++++++++++++--------------- 1 file changed, 33 insertions(+), 28 deletions(-) (limited to 'src/portable') diff --git a/src/portable/nuvoton/nuc121/dcd_nuc121.c b/src/portable/nuvoton/nuc121/dcd_nuc121.c index 71211af0c..9c0cefd74 100644 --- a/src/portable/nuvoton/nuc121/dcd_nuc121.c +++ b/src/portable/nuvoton/nuc121/dcd_nuc121.c @@ -148,30 +148,15 @@ static void dcd_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep) ep->MXPLD = bytes_now; } -/* centralized location for USBD interrupt enable bit mask */ -static const uint32_t enabled_irqs = USBD_INTSTS_VBDETIF_Msk | USBD_INTSTS_BUSIF_Msk | USBD_INTSTS_SETUP_Msk | USBD_INTSTS_USBIF_Msk | USBD_INTSTS_SOFIF_Msk; - -/* - NUC121/NUC125/NUC126 TinyUSB API driver implementation -*/ - -void dcd_init(uint8_t rhport) +/* called by dcd_init() as well as by the ISR during a USB bus reset */ +static void bus_reset(void) { - (void) rhport; - -#ifdef SUPPORT_LPM - USBD->ATTR = 0x7D0 | USBD_LPMACK; -#else - USBD->ATTR = 0x7D0; -#endif - - usb_detach(); - USBD->STBUFSEG = PERIPH_SETUP_BUF_BASE; for (enum ep_enum ep_index = PERIPH_EP0; ep_index < PERIPH_MAX_EP; ep_index++) { - USBD->EP[ep_index].CFGP &= ~USBD_CFG_STATE_Msk; + USBD->EP[ep_index].CFG = 0; + USBD->EP[ep_index].CFGP = 0; } /* allocate the default EP0 endpoints */ @@ -187,6 +172,34 @@ void dcd_init(uint8_t rhport) /* USB RAM beyond what we've allocated above is available to the user */ bufseg_addr = PERIPH_EP2_BUF_BASE; + /* Reset USB device address */ + USBD->FADDR = 0; + + /* reset EP0_IN flag */ + active_ep0_xfer = false; +} + +/* centralized location for USBD interrupt enable bit mask */ +static const uint32_t enabled_irqs = USBD_INTSTS_VBDETIF_Msk | USBD_INTSTS_BUSIF_Msk | USBD_INTSTS_SETUP_Msk | USBD_INTSTS_USBIF_Msk | USBD_INTSTS_SOFIF_Msk; + +/* + NUC121/NUC125/NUC126 TinyUSB API driver implementation +*/ + +void dcd_init(uint8_t rhport) +{ + (void) rhport; + +#ifdef SUPPORT_LPM + USBD->ATTR = 0x7D0 | USBD_LPMACK; +#else + USBD->ATTR = 0x7D0; +#endif + + usb_detach(); + + bus_reset(); + usb_attach(); USBD->INTSTS = enabled_irqs; @@ -329,15 +342,7 @@ void USBD_IRQHandler(void) /* USB bus reset */ USBD->ATTR |= USBD_ATTR_USBEN_Msk | USBD_ATTR_PHYEN_Msk; - /* Reset all endpoints to DATA0 */ - for(enum ep_enum ep_index = PERIPH_EP0; ep_index < PERIPH_MAX_EP; ep_index++) - USBD->EP[ep_index].CFG &= ~USBD_CFG_DSQSYNC_Msk; - - /* Reset USB device address */ - USBD->FADDR = 0; - - /* reset EP0_IN flag */ - active_ep0_xfer = false; + bus_reset(); dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true); } -- cgit v1.3.1 From ae68668df9c2e50ab2283485c7aca851adeedfc1 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 15 Jan 2020 21:59:15 -0800 Subject: flush and invalidate the dcache --- src/portable/nxp/transdimension/dcd_transdimension.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'src/portable') diff --git a/src/portable/nxp/transdimension/dcd_transdimension.c b/src/portable/nxp/transdimension/dcd_transdimension.c index 2ec7ae51c..b5fca9ef3 100644 --- a/src/portable/nxp/transdimension/dcd_transdimension.c +++ b/src/portable/nxp/transdimension/dcd_transdimension.c @@ -1,4 +1,4 @@ -/* +/* * The MIT License (MIT) * * Copyright (c) 2019 Ha Thach (tinyusb.org) @@ -313,9 +313,12 @@ void dcd_init(uint8_t rhport) // TODO Force fullspeed on non-highspeed port // dcd_reg->PORTSC1 = PORTSC1_FORCE_FULL_SPEED; + + SCB_CleanInvalidateDCache_by_Addr(&_dcd_data, sizeof(dcd_data_t)); + dcd_reg->ENDPTLISTADDR = (uint32_t) _dcd_data.qhd; // Endpoint List Address has to be 2K alignment dcd_reg->USBSTS = dcd_reg->USBSTS; - dcd_reg->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_RESET | INTR_SUSPEND | INTR_SOF; + dcd_reg->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_RESET | INTR_SUSPEND; // | INTR_SOF; dcd_reg->USBCMD &= ~0x00FF0000; // Interrupt Threshold Interval = 0 dcd_reg->USBCMD |= TU_BIT(0); // connect @@ -418,6 +421,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) p_qhd->zero_length_termination = 1; p_qhd->max_package_size = p_endpoint_desc->wMaxPacketSize.size; p_qhd->qtd_overlay.next = QTD_NEXT_INVALID; + SCB_CleanInvalidateDCache_by_Addr(&_dcd_data, sizeof(dcd_data_t)); // Enable EP Control DCD_REGS[rhport]->ENDPTCTRL[epnum] |= ((p_endpoint_desc->bmAttributes.xfer << 2) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET) << (dir ? 16 : 0); @@ -441,10 +445,14 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t dcd_qhd_t * p_qhd = &_dcd_data.qhd[ep_idx]; dcd_qtd_t * p_qtd = &_dcd_data.qtd[ep_idx]; + // Force the CPU to flush the buffer. + SCB_CleanInvalidateDCache_by_Addr(buffer, total_bytes); + //------------- Prepare qtd -------------// qtd_init(p_qtd, buffer, total_bytes); p_qtd->int_on_complete = true; p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // link qtd to qhd + SCB_CleanInvalidateDCache_by_Addr(&_dcd_data, sizeof(dcd_data_t)); // start transfer DCD_REGS[rhport]->ENDPTPRIME = TU_BIT( ep_idx2bit(ep_idx) ) ; @@ -484,6 +492,9 @@ void dcd_isr(uint8_t rhport) } } + // Make sure we read the latest version of _dcd_data. + SCB_CleanInvalidateDCache_by_Addr(&_dcd_data, sizeof(dcd_data_t)); + // TODO disconnection does not generate interrupt !!!!!! // if (int_status & INTR_PORT_CHANGE) // { -- cgit v1.3.1 From cd6454a330e4251c9ff01403078a537fc180b47a Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 16 Jan 2020 17:17:49 -0800 Subject: Add if guards for dcache flushes. --- .../nxp/transdimension/dcd_transdimension.c | 27 +++++++++++++++------- 1 file changed, 19 insertions(+), 8 deletions(-) (limited to 'src/portable') diff --git a/src/portable/nxp/transdimension/dcd_transdimension.c b/src/portable/nxp/transdimension/dcd_transdimension.c index b5fca9ef3..77e355edf 100644 --- a/src/portable/nxp/transdimension/dcd_transdimension.c +++ b/src/portable/nxp/transdimension/dcd_transdimension.c @@ -313,12 +313,13 @@ void dcd_init(uint8_t rhport) // TODO Force fullspeed on non-highspeed port // dcd_reg->PORTSC1 = PORTSC1_FORCE_FULL_SPEED; - - SCB_CleanInvalidateDCache_by_Addr(&_dcd_data, sizeof(dcd_data_t)); + #if defined(__CORTEX_M) && __CORTEX_M == 7 && __DCACHE_PRESENT == 1 + SCB_CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t)); + #endif dcd_reg->ENDPTLISTADDR = (uint32_t) _dcd_data.qhd; // Endpoint List Address has to be 2K alignment dcd_reg->USBSTS = dcd_reg->USBSTS; - dcd_reg->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_RESET | INTR_SUSPEND; // | INTR_SOF; + dcd_reg->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_RESET | INTR_SUSPEND | INTR_SOF; dcd_reg->USBCMD &= ~0x00FF0000; // Interrupt Threshold Interval = 0 dcd_reg->USBCMD |= TU_BIT(0); // connect @@ -421,7 +422,10 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) p_qhd->zero_length_termination = 1; p_qhd->max_package_size = p_endpoint_desc->wMaxPacketSize.size; p_qhd->qtd_overlay.next = QTD_NEXT_INVALID; - SCB_CleanInvalidateDCache_by_Addr(&_dcd_data, sizeof(dcd_data_t)); + + #if defined(__CORTEX_M) && __CORTEX_M == 7 && __DCACHE_PRESENT == 1 + SCB_CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t)); + #endif // Enable EP Control DCD_REGS[rhport]->ENDPTCTRL[epnum] |= ((p_endpoint_desc->bmAttributes.xfer << 2) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET) << (dir ? 16 : 0); @@ -445,14 +449,19 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t dcd_qhd_t * p_qhd = &_dcd_data.qhd[ep_idx]; dcd_qtd_t * p_qtd = &_dcd_data.qtd[ep_idx]; - // Force the CPU to flush the buffer. - SCB_CleanInvalidateDCache_by_Addr(buffer, total_bytes); + // Force the CPU to flush the buffer. We increase the size by 32 because the call aligns the + // address to 32-byte boundaries. + #if defined(__CORTEX_M) && __CORTEX_M == 7 && __DCACHE_PRESENT == 1 + SCB_CleanInvalidateDCache_by_Addr((uint32_t*) buffer, total_bytes + 31); + #endif //------------- Prepare qtd -------------// qtd_init(p_qtd, buffer, total_bytes); p_qtd->int_on_complete = true; p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // link qtd to qhd - SCB_CleanInvalidateDCache_by_Addr(&_dcd_data, sizeof(dcd_data_t)); + #if defined(__CORTEX_M) && __CORTEX_M == 7 && __DCACHE_PRESENT == 1 + SCB_CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t)); + #endif // start transfer DCD_REGS[rhport]->ENDPTPRIME = TU_BIT( ep_idx2bit(ep_idx) ) ; @@ -493,7 +502,9 @@ void dcd_isr(uint8_t rhport) } // Make sure we read the latest version of _dcd_data. - SCB_CleanInvalidateDCache_by_Addr(&_dcd_data, sizeof(dcd_data_t)); + #if defined(__CORTEX_M) && __CORTEX_M == 7 && __DCACHE_PRESENT == 1 + SCB_CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t)); + #endif // TODO disconnection does not generate interrupt !!!!!! // if (int_status & INTR_PORT_CHANGE) -- cgit v1.3.1 From 3208fe00b6a34032492f0ec4b8f6a4d8ca12e474 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 17 Jan 2020 12:06:59 +0700 Subject: clean up --- examples/rules.mk | 1 + .../nxp/transdimension/dcd_transdimension.c | 56 +++++++++++----------- 2 files changed, 29 insertions(+), 28 deletions(-) (limited to 'src/portable') diff --git a/examples/rules.mk b/examples/rules.mk index 2969a66f1..87c7e9649 100644 --- a/examples/rules.mk +++ b/examples/rules.mk @@ -120,6 +120,7 @@ endif # Flash using jlink flash-jlink: $(BUILD)/$(BOARD)-firmware.hex @echo halt > $(BUILD)/$(BOARD).jlink + @echo r > $(BUILD)/$(BOARD).jlink @echo loadfile $^ >> $(BUILD)/$(BOARD).jlink @echo r >> $(BUILD)/$(BOARD).jlink @echo go >> $(BUILD)/$(BOARD).jlink diff --git a/src/portable/nxp/transdimension/dcd_transdimension.c b/src/portable/nxp/transdimension/dcd_transdimension.c index 77e355edf..89f595bd8 100644 --- a/src/portable/nxp/transdimension/dcd_transdimension.c +++ b/src/portable/nxp/transdimension/dcd_transdimension.c @@ -130,48 +130,48 @@ enum { typedef struct { //------------- ID + HW Parameter Registers-------------// - __I uint32_t TU_RESERVED[64]; ///< For iMX RT10xx, but not used by LPC18XX/LPC43XX + __I uint32_t TU_RESERVED[64]; ///< For iMX RT10xx, but not used by LPC18XX/LPC43XX //------------- Capability Registers-------------// - __I uint8_t CAPLENGTH; ///< Capability Registers Length + __I uint8_t CAPLENGTH; ///< Capability Registers Length __I uint8_t TU_RESERVED[1]; - __I uint16_t HCIVERSION; ///< Host Controller Interface Version + __I uint16_t HCIVERSION; ///< Host Controller Interface Version - __I uint32_t HCSPARAMS; ///< Host Controller Structural Parameters - __I uint32_t HCCPARAMS; ///< Host Controller Capability Parameters + __I uint32_t HCSPARAMS; ///< Host Controller Structural Parameters + __I uint32_t HCCPARAMS; ///< Host Controller Capability Parameters __I uint32_t TU_RESERVED[5]; - __I uint16_t DCIVERSION; ///< Device Controller Interface Version + __I uint16_t DCIVERSION; ///< Device Controller Interface Version __I uint8_t TU_RESERVED[2]; - __I uint32_t DCCPARAMS; ///< Device Controller Capability Parameters + __I uint32_t DCCPARAMS; ///< Device Controller Capability Parameters __I uint32_t TU_RESERVED[6]; //------------- Operational Registers -------------// - __IO uint32_t USBCMD; ///< USB Command Register - __IO uint32_t USBSTS; ///< USB Status Register - __IO uint32_t USBINTR; ///< Interrupt Enable Register - __IO uint32_t FRINDEX; ///< USB Frame Index + __IO uint32_t USBCMD; ///< USB Command Register + __IO uint32_t USBSTS; ///< USB Status Register + __IO uint32_t USBINTR; ///< Interrupt Enable Register + __IO uint32_t FRINDEX; ///< USB Frame Index __I uint32_t TU_RESERVED; - __IO uint32_t DEVICEADDR; ///< Device Address - __IO uint32_t ENDPTLISTADDR; ///< Endpoint List Address + __IO uint32_t DEVICEADDR; ///< Device Address + __IO uint32_t ENDPTLISTADDR; ///< Endpoint List Address __I uint32_t TU_RESERVED; - __IO uint32_t BURSTSIZE; ///< Programmable Burst Size - __IO uint32_t TXFILLTUNING; ///< TX FIFO Fill Tuning + __IO uint32_t BURSTSIZE; ///< Programmable Burst Size + __IO uint32_t TXFILLTUNING; ///< TX FIFO Fill Tuning uint32_t TU_RESERVED[4]; - __IO uint32_t ENDPTNAK; ///< Endpoint NAK - __IO uint32_t ENDPTNAKEN; ///< Endpoint NAK Enable + __IO uint32_t ENDPTNAK; ///< Endpoint NAK + __IO uint32_t ENDPTNAKEN; ///< Endpoint NAK Enable __I uint32_t TU_RESERVED; - __IO uint32_t PORTSC1; ///< Port Status & Control + __IO uint32_t PORTSC1; ///< Port Status & Control __I uint32_t TU_RESERVED[7]; - __IO uint32_t OTGSC; ///< On-The-Go Status & control - __IO uint32_t USBMODE; ///< USB Device Mode - __IO uint32_t ENDPTSETUPSTAT; ///< Endpoint Setup Status - __IO uint32_t ENDPTPRIME; ///< Endpoint Prime - __IO uint32_t ENDPTFLUSH; ///< Endpoint Flush - __I uint32_t ENDPTSTAT; ///< Endpoint Status - __IO uint32_t ENDPTCOMPLETE; ///< Endpoint Complete - __IO uint32_t ENDPTCTRL[8]; ///< Endpoint Control 0 - 7 + __IO uint32_t OTGSC; ///< On-The-Go Status & control + __IO uint32_t USBMODE; ///< USB Device Mode + __IO uint32_t ENDPTSETUPSTAT; ///< Endpoint Setup Status + __IO uint32_t ENDPTPRIME; ///< Endpoint Prime + __IO uint32_t ENDPTFLUSH; ///< Endpoint Flush + __I uint32_t ENDPTSTAT; ///< Endpoint Status + __IO uint32_t ENDPTCOMPLETE; ///< Endpoint Complete + __IO uint32_t ENDPTCTRL[8]; ///< Endpoint Control 0 - 7 } dcd_registers_t; @@ -279,9 +279,9 @@ static void bus_reset(uint8_t rhport) dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT; dcd_reg->ENDPTCOMPLETE = dcd_reg->ENDPTCOMPLETE; - while (dcd_reg->ENDPTPRIME); + while (dcd_reg->ENDPTPRIME) {} dcd_reg->ENDPTFLUSH = 0xFFFFFFFF; - while (dcd_reg->ENDPTFLUSH); + while (dcd_reg->ENDPTFLUSH) {} // read reset bit in portsc -- cgit v1.3.1 From 04d8671641a7d4bfa07cc7f05ea03cb99771240b Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 17 Jan 2020 12:59:00 +0700 Subject: clean up, disable SOF for dcd trans since we don't use it for now --- .../nxp/transdimension/dcd_transdimension.c | 74 +++++++++++----------- 1 file changed, 36 insertions(+), 38 deletions(-) (limited to 'src/portable') diff --git a/src/portable/nxp/transdimension/dcd_transdimension.c b/src/portable/nxp/transdimension/dcd_transdimension.c index 89f595bd8..a9823a262 100644 --- a/src/portable/nxp/transdimension/dcd_transdimension.c +++ b/src/portable/nxp/transdimension/dcd_transdimension.c @@ -39,18 +39,19 @@ #if CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX #include "fsl_device_registers.h" -// RT1010 and RT1020 only has 1 USB controller -#if FSL_FEATURE_SOC_USBHS_COUNT == 1 - #define DCD_REGS_BASE { (dcd_registers_t*) USB_BASE } - IRQn_Type DCD_IRQn[] = { USB_OTG1_IRQn }; - -// RT1050, RT1060 has 2 USB controllers -#else - #define DCD_REGS_BASE { (dcd_registers_t*) USB1_BASE, (dcd_registers_t*) USB2_BASE } - IRQn_Type DCD_IRQn[] = { USB_OTG1_IRQn, USB_OTG2_IRQn }; -#endif + // RT1010 and RT1020 only has 1 USB controller + #if FSL_FEATURE_SOC_USBHS_COUNT == 1 + #define DCD_REGS_BASE { (dcd_registers_t*) USB_BASE } + IRQn_Type DCD_IRQn[] = { USB_OTG1_IRQn }; + + // RT1050, RT1060 has 2 USB controllers + #else + #define DCD_REGS_BASE { (dcd_registers_t*) USB1_BASE, (dcd_registers_t*) USB2_BASE } + IRQn_Type DCD_IRQn[] = { USB_OTG1_IRQn, USB_OTG2_IRQn }; + #endif #else + // LPCOpen for 18xx & 43xx #include "chip.h" #define DCD_REGS_BASE { (dcd_registers_t*) LPC_USB0_BASE, (dcd_registers_t*) LPC_USB1_BASE } IRQn_Type DCD_IRQn[] = { USB0_IRQn, USB1_IRQn }; @@ -218,19 +219,19 @@ typedef struct uint32_t : 0 ; // Word 1: Current qTD Pointer - volatile uint32_t qtd_addr; + volatile uint32_t qtd_addr; - // Word 2-9: Transfer Overlay - volatile dcd_qtd_t qtd_overlay; + // Word 2-9: Transfer Overlay + volatile dcd_qtd_t qtd_overlay; - // Word 10-11: Setup request (control OUT only) - volatile tusb_control_request_t setup_request; + // Word 10-11: Setup request (control OUT only) + volatile tusb_control_request_t setup_request; - //--------------------------------------------------------------------+ + //--------------------------------------------------------------------+ /// Due to the fact QHD is 64 bytes aligned but occupies only 48 bytes - /// thus there are 16 bytes padding free that we can make use of. + /// thus there are 16 bytes padding free that we can make use of. //--------------------------------------------------------------------+ - uint8_t reserved[16]; + uint8_t reserved[16]; } dcd_qhd_t; TU_VERIFY_STATIC( sizeof(dcd_qhd_t) == 64, "size is not correct"); @@ -251,6 +252,12 @@ typedef struct { static dcd_data_t _dcd_data CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(2048); static dcd_registers_t* DCD_REGS[] = DCD_REGS_BASE; +#if defined(__CORTEX_M) && __CORTEX_M == 7 && __DCACHE_PRESENT == 1 + #define CleanInvalidateDCache_by_Addr SCB_CleanInvalidateDCache_by_Addr +#else + #define CleanInvalidateDCache_by_Addr(_addr, _dsize) +#endif + //--------------------------------------------------------------------+ // CONTROLLER API //--------------------------------------------------------------------+ @@ -289,11 +296,11 @@ static void bus_reset(uint8_t rhport) tu_memclr(&_dcd_data, sizeof(dcd_data_t)); //------------- Set up Control Endpoints (0 OUT, 1 IN) -------------// - _dcd_data.qhd[0].zero_length_termination = _dcd_data.qhd[1].zero_length_termination = 1; - _dcd_data.qhd[0].max_package_size = _dcd_data.qhd[1].max_package_size = CFG_TUD_ENDPOINT0_SIZE; - _dcd_data.qhd[0].qtd_overlay.next = _dcd_data.qhd[1].qtd_overlay.next = QTD_NEXT_INVALID; + _dcd_data.qhd[0].zero_length_termination = _dcd_data.qhd[1].zero_length_termination = 1; + _dcd_data.qhd[0].max_package_size = _dcd_data.qhd[1].max_package_size = CFG_TUD_ENDPOINT0_SIZE; + _dcd_data.qhd[0].qtd_overlay.next = _dcd_data.qhd[1].qtd_overlay.next = QTD_NEXT_INVALID; - _dcd_data.qhd[0].int_on_setup = 1; // OUT only + _dcd_data.qhd[0].int_on_setup = 1; // OUT only } void dcd_init(uint8_t rhport) @@ -313,13 +320,11 @@ void dcd_init(uint8_t rhport) // TODO Force fullspeed on non-highspeed port // dcd_reg->PORTSC1 = PORTSC1_FORCE_FULL_SPEED; - #if defined(__CORTEX_M) && __CORTEX_M == 7 && __DCACHE_PRESENT == 1 - SCB_CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t)); - #endif + CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t)); dcd_reg->ENDPTLISTADDR = (uint32_t) _dcd_data.qhd; // Endpoint List Address has to be 2K alignment dcd_reg->USBSTS = dcd_reg->USBSTS; - dcd_reg->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_RESET | INTR_SUSPEND | INTR_SOF; + dcd_reg->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_RESET | INTR_SUSPEND /*| INTR_SOF*/; dcd_reg->USBCMD &= ~0x00FF0000; // Interrupt Threshold Interval = 0 dcd_reg->USBCMD |= TU_BIT(0); // connect @@ -423,9 +428,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) p_qhd->max_package_size = p_endpoint_desc->wMaxPacketSize.size; p_qhd->qtd_overlay.next = QTD_NEXT_INVALID; - #if defined(__CORTEX_M) && __CORTEX_M == 7 && __DCACHE_PRESENT == 1 - SCB_CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t)); - #endif + CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t)); // Enable EP Control DCD_REGS[rhport]->ENDPTCTRL[epnum] |= ((p_endpoint_desc->bmAttributes.xfer << 2) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET) << (dir ? 16 : 0); @@ -451,17 +454,14 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t // Force the CPU to flush the buffer. We increase the size by 32 because the call aligns the // address to 32-byte boundaries. - #if defined(__CORTEX_M) && __CORTEX_M == 7 && __DCACHE_PRESENT == 1 - SCB_CleanInvalidateDCache_by_Addr((uint32_t*) buffer, total_bytes + 31); - #endif + CleanInvalidateDCache_by_Addr((uint32_t*) buffer, total_bytes + 31); //------------- Prepare qtd -------------// qtd_init(p_qtd, buffer, total_bytes); p_qtd->int_on_complete = true; p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // link qtd to qhd - #if defined(__CORTEX_M) && __CORTEX_M == 7 && __DCACHE_PRESENT == 1 - SCB_CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t)); - #endif + + CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t)); // start transfer DCD_REGS[rhport]->ENDPTPRIME = TU_BIT( ep_idx2bit(ep_idx) ) ; @@ -502,9 +502,7 @@ void dcd_isr(uint8_t rhport) } // Make sure we read the latest version of _dcd_data. - #if defined(__CORTEX_M) && __CORTEX_M == 7 && __DCACHE_PRESENT == 1 - SCB_CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t)); - #endif + CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t)); // TODO disconnection does not generate interrupt !!!!!! // if (int_status & INTR_PORT_CHANGE) -- cgit v1.3.1 From e2f60a5134890ab9fdeea0e1794c157f38c25918 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 17 Jan 2020 17:09:23 +0700 Subject: rt10xx: correct max endpoint count is 8 --- .../nxp/transdimension/dcd_transdimension.c | 106 ++++++++++++--------- 1 file changed, 63 insertions(+), 43 deletions(-) (limited to 'src/portable') diff --git a/src/portable/nxp/transdimension/dcd_transdimension.c b/src/portable/nxp/transdimension/dcd_transdimension.c index a9823a262..d400a30e3 100644 --- a/src/portable/nxp/transdimension/dcd_transdimension.c +++ b/src/portable/nxp/transdimension/dcd_transdimension.c @@ -38,23 +38,15 @@ #if CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX #include "fsl_device_registers.h" - - // RT1010 and RT1020 only has 1 USB controller - #if FSL_FEATURE_SOC_USBHS_COUNT == 1 - #define DCD_REGS_BASE { (dcd_registers_t*) USB_BASE } - IRQn_Type DCD_IRQn[] = { USB_OTG1_IRQn }; - - // RT1050, RT1060 has 2 USB controllers - #else - #define DCD_REGS_BASE { (dcd_registers_t*) USB1_BASE, (dcd_registers_t*) USB2_BASE } - IRQn_Type DCD_IRQn[] = { USB_OTG1_IRQn, USB_OTG2_IRQn }; - #endif - #else // LPCOpen for 18xx & 43xx #include "chip.h" - #define DCD_REGS_BASE { (dcd_registers_t*) LPC_USB0_BASE, (dcd_registers_t*) LPC_USB1_BASE } - IRQn_Type DCD_IRQn[] = { USB0_IRQn, USB1_IRQn }; +#endif + +#if defined(__CORTEX_M) && __CORTEX_M == 7 && __DCACHE_PRESENT == 1 + #define CleanInvalidateDCache_by_Addr SCB_CleanInvalidateDCache_by_Addr +#else + #define CleanInvalidateDCache_by_Addr(_addr, _dsize) #endif //--------------------------------------------------------------------+ @@ -232,7 +224,7 @@ typedef struct /// thus there are 16 bytes padding free that we can make use of. //--------------------------------------------------------------------+ uint8_t reserved[16]; -} dcd_qhd_t; +} dcd_qhd_t; TU_VERIFY_STATIC( sizeof(dcd_qhd_t) == 64, "size is not correct"); @@ -240,23 +232,48 @@ TU_VERIFY_STATIC( sizeof(dcd_qhd_t) == 64, "size is not correct"); // Variables //--------------------------------------------------------------------+ -#define QHD_MAX 12 +typedef struct +{ + dcd_registers_t* regs; // registers + const IRQn_Type irqnum; // IRQ number + const uint8_t ep_count; // Max bi-directional Endpoints +}dcd_controller_t; + +#if CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX + // Each endpoint with direction (IN/OUT) occupies a queue head + // Therefore QHD_MAX is 2 x max endpoint count + #define QHD_MAX (8*2) + + dcd_controller_t _dcd_controller[] = + { + // RT1010 and RT1020 only has 1 USB controller + #if FSL_FEATURE_SOC_USBHS_COUNT == 1 + { .regs = (dcd_registers_t*) USB_BASE , .irqnum = USB_OTG1_IRQn, .ep_count = 8 } + #else + { .regs = (dcd_registers_t*) USB1_BASE, .irqnum = USB_OTG1_IRQn, .ep_count = 8 }, + { .regs = (dcd_registers_t*) USB2_BASE, .irqnum = USB_OTG2_IRQn, .ep_count = 8 } + #endif + }; + +#else + #define QHD_MAX (6*2) + + dcd_controller_t _dcd_controller[] = + { + { .regs = (dcd_registers_t*) LPC_USB0_BASE, .irqnum = USB0_IRQn, .ep_count = 6 }, + { .regs = (dcd_registers_t*) LPC_USB1_BASE, .irqnum = USB1_IRQn, .ep_count = 4 } + }; +#endif + #define QTD_NEXT_INVALID 0x01 typedef struct { // Must be at 2K alignment dcd_qhd_t qhd[QHD_MAX] TU_ATTR_ALIGNED(64); - dcd_qtd_t qtd[QHD_MAX] TU_ATTR_ALIGNED(32); + dcd_qtd_t qtd[QHD_MAX] TU_ATTR_ALIGNED(32); // for portability, TinyUSB only queue 1 TD for each Qhd }dcd_data_t; static dcd_data_t _dcd_data CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(2048); -static dcd_registers_t* DCD_REGS[] = DCD_REGS_BASE; - -#if defined(__CORTEX_M) && __CORTEX_M == 7 && __DCACHE_PRESENT == 1 - #define CleanInvalidateDCache_by_Addr SCB_CleanInvalidateDCache_by_Addr -#else - #define CleanInvalidateDCache_by_Addr(_addr, _dsize) -#endif //--------------------------------------------------------------------+ // CONTROLLER API @@ -265,16 +282,14 @@ static dcd_registers_t* DCD_REGS[] = DCD_REGS_BASE; /// follows LPC43xx User Manual 23.10.3 static void bus_reset(uint8_t rhport) { - dcd_registers_t* dcd_reg = DCD_REGS[rhport]; + dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs; // The reset value for all endpoint types is the control endpoint. If one endpoint // direction is enabled and the paired endpoint of opposite direction is disabled, then the - // endpoint type of the unused direction must bechanged from the control type to any other - // type (e.g. bulk). Leaving an unconfigured endpoint control will cause undefined behavior + // endpoint type of the unused direction must be changed from the control type to any other + // type (e.g. bulk). Leaving an un-configured endpoint control will cause undefined behavior // for the data PID tracking on the active endpoint. - - // USB0 has 5 but USB1 only has 3 non-control endpoints - for( int i=1; i < (rhport ? 6 : 4); i++) + for( int i=1; i < _dcd_controller[rhport].ep_count; i++) { dcd_reg->ENDPTCTRL[i] = (TUSB_XFER_BULK << 2) | (TUSB_XFER_BULK << 18); } @@ -307,7 +322,7 @@ void dcd_init(uint8_t rhport) { tu_memclr(&_dcd_data, sizeof(dcd_data_t)); - dcd_registers_t* const dcd_reg = DCD_REGS[rhport]; + dcd_registers_t* const dcd_reg = _dcd_controller[rhport].regs; // Reset controller dcd_reg->USBCMD |= USBCMD_RESET; @@ -332,12 +347,12 @@ void dcd_init(uint8_t rhport) void dcd_int_enable(uint8_t rhport) { - NVIC_EnableIRQ(DCD_IRQn[rhport]); + NVIC_EnableIRQ(_dcd_controller[rhport].irqnum); } void dcd_int_disable(uint8_t rhport) { - NVIC_DisableIRQ(DCD_IRQn[rhport]); + NVIC_DisableIRQ(_dcd_controller[rhport].irqnum); } void dcd_set_address(uint8_t rhport, uint8_t dev_addr) @@ -345,7 +360,8 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr) // Response with status first before changing device address dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); - DCD_REGS[rhport]->DEVICEADDR = (dev_addr << 25) | TU_BIT(24); + dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs; + dcd_reg->DEVICEADDR = (dev_addr << 25) | TU_BIT(24); } void dcd_set_config(uint8_t rhport, uint8_t config_num) @@ -395,7 +411,8 @@ void dcd_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_REGS[rhport]->ENDPTCTRL[epnum] |= ENDPTCTRL_STALL << (dir ? 16 : 0); + dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs; + dcd_reg->ENDPTCTRL[epnum] |= ENDPTCTRL_STALL << (dir ? 16 : 0); } void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) @@ -404,8 +421,9 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) uint8_t const dir = tu_edpt_dir(ep_addr); // data toggle also need to be reset - DCD_REGS[rhport]->ENDPTCTRL[epnum] |= ENDPTCTRL_TOGGLE_RESET << ( dir ? 16 : 0 ); - DCD_REGS[rhport]->ENDPTCTRL[epnum] &= ~(ENDPTCTRL_STALL << ( dir ? 16 : 0)); + dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs; + dcd_reg->ENDPTCTRL[epnum] |= ENDPTCTRL_TOGGLE_RESET << ( dir ? 16 : 0 ); + dcd_reg->ENDPTCTRL[epnum] &= ~(ENDPTCTRL_STALL << ( dir ? 16 : 0)); } bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) @@ -417,8 +435,8 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); uint8_t const ep_idx = 2*epnum + dir; - // USB0 has 5, USB1 has 3 non-control endpoints - TU_ASSERT( epnum <= (rhport ? 3 : 5) ); + // Must not exceed max endpoint number + TU_ASSERT( epnum < _dcd_controller[rhport].ep_count ); //------------- Prepare Queue Head -------------// dcd_qhd_t * p_qhd = &_dcd_data.qhd[ep_idx]; @@ -431,13 +449,15 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t)); // Enable EP Control - DCD_REGS[rhport]->ENDPTCTRL[epnum] |= ((p_endpoint_desc->bmAttributes.xfer << 2) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET) << (dir ? 16 : 0); + dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs; + dcd_reg->ENDPTCTRL[epnum] |= ((p_endpoint_desc->bmAttributes.xfer << 2) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET) << (dir ? 16 : 0); return true; } bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) { + dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs; uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const dir = tu_edpt_dir(ep_addr); uint8_t const ep_idx = 2*epnum + dir; @@ -446,7 +466,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t { // follows UM 24.10.8.1.1 Setup packet handling using setup lockout mechanism // wait until ENDPTSETUPSTAT before priming data/status in response TODO add time out - while(DCD_REGS[rhport]->ENDPTSETUPSTAT & TU_BIT(0)) {} + while(dcd_reg->ENDPTSETUPSTAT & TU_BIT(0)) {} } dcd_qhd_t * p_qhd = &_dcd_data.qhd[ep_idx]; @@ -464,7 +484,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t)); // start transfer - DCD_REGS[rhport]->ENDPTPRIME = TU_BIT( ep_idx2bit(ep_idx) ) ; + dcd_reg->ENDPTPRIME = TU_BIT( ep_idx2bit(ep_idx) ) ; return true; } @@ -474,7 +494,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t //--------------------------------------------------------------------+ void dcd_isr(uint8_t rhport) { - dcd_registers_t* const dcd_reg = DCD_REGS[rhport]; + dcd_registers_t* const dcd_reg = _dcd_controller[rhport].regs; uint32_t const int_enable = dcd_reg->USBINTR; uint32_t const int_status = dcd_reg->USBSTS & int_enable; -- cgit v1.3.1 From cd8eb2a06bfb14c25788b56f1821784cb9c5535c Mon Sep 17 00:00:00 2001 From: Peter Lawrence <12226419+majbthrd@users.noreply.github.com> Date: Sat, 25 Jan 2020 13:10:32 -0600 Subject: added support for Nuvoton NUC120 --- hw/bsp/nutiny_sdk_nuc120/board.mk | 56 ++++ hw/bsp/nutiny_sdk_nuc120/nuc120_flash.ld | 195 ++++++++++++ hw/bsp/nutiny_sdk_nuc120/nutiny_sdk_nuc120.c | 122 ++++++++ hw/mcu/nuvoton | 2 +- src/portable/nuvoton/nuc120/dcd_nuc120.c | 433 +++++++++++++++++++++++++++ src/tusb_option.h | 1 + 6 files changed, 808 insertions(+), 1 deletion(-) create mode 100644 hw/bsp/nutiny_sdk_nuc120/board.mk create mode 100644 hw/bsp/nutiny_sdk_nuc120/nuc120_flash.ld create mode 100644 hw/bsp/nutiny_sdk_nuc120/nutiny_sdk_nuc120.c create mode 100644 src/portable/nuvoton/nuc120/dcd_nuc120.c (limited to 'src/portable') diff --git a/hw/bsp/nutiny_sdk_nuc120/board.mk b/hw/bsp/nutiny_sdk_nuc120/board.mk new file mode 100644 index 000000000..f2ee09f19 --- /dev/null +++ b/hw/bsp/nutiny_sdk_nuc120/board.mk @@ -0,0 +1,56 @@ +CFLAGS += \ + -flto \ + -mthumb \ + -mabi=aapcs-linux \ + -mcpu=cortex-m0 \ + -DCFG_TUSB_MCU=OPT_MCU_NUC120 + +# All source paths should be relative to the top level. +LD_FILE = hw/bsp/nutiny_sdk_nuc120/nuc120_flash.ld + +SRC_C += \ + hw/mcu/nuvoton/nuc100_120/Device/Nuvoton/NUC100Series/Source/system_NUC100Series.c \ + hw/mcu/nuvoton/nuc100_120/StdDriver/src/acmp.c \ + hw/mcu/nuvoton/nuc100_120/StdDriver/src/adc.c \ + hw/mcu/nuvoton/nuc100_120/StdDriver/src/clk.c \ + hw/mcu/nuvoton/nuc100_120/StdDriver/src/crc.c \ + hw/mcu/nuvoton/nuc100_120/StdDriver/src/fmc.c \ + hw/mcu/nuvoton/nuc100_120/StdDriver/src/gpio.c \ + hw/mcu/nuvoton/nuc100_120/StdDriver/src/i2c.c \ + hw/mcu/nuvoton/nuc100_120/StdDriver/src/i2s.c \ + hw/mcu/nuvoton/nuc100_120/StdDriver/src/pdma.c \ + hw/mcu/nuvoton/nuc100_120/StdDriver/src/ps2.c \ + hw/mcu/nuvoton/nuc100_120/StdDriver/src/pwm.c \ + hw/mcu/nuvoton/nuc100_120/StdDriver/src/rtc.c \ + hw/mcu/nuvoton/nuc100_120/StdDriver/src/sc.c \ + hw/mcu/nuvoton/nuc100_120/StdDriver/src/spi.c \ + hw/mcu/nuvoton/nuc100_120/StdDriver/src/sys.c \ + hw/mcu/nuvoton/nuc100_120/StdDriver/src/timer.c \ + hw/mcu/nuvoton/nuc100_120/StdDriver/src/uart.c \ + hw/mcu/nuvoton/nuc100_120/StdDriver/src/usbd.c \ + hw/mcu/nuvoton/nuc100_120/StdDriver/src/wdt.c \ + hw/mcu/nuvoton/nuc100_120/StdDriver/src/wwdt.c + +SRC_S += \ + hw/mcu/nuvoton/nuc100_120/Device/Nuvoton/NUC100Series/Source/GCC/startup_NUC100Series.S + +INC += \ + $(TOP)/hw/mcu/nuvoton/nuc100_120/Device/Nuvoton/NUC100Series/Include \ + $(TOP)/hw/mcu/nuvoton/nuc100_120/StdDriver/inc \ + $(TOP)/hw/mcu/nuvoton/nuc100_120/CMSIS/Include + +# For TinyUSB port source +VENDOR = nuvoton +CHIP_FAMILY = nuc120 + +# For freeRTOS port source +FREERTOS_PORT = ARM_CM0 + +# For flash-jlink target +JLINK_DEVICE = NUC120LE3 +JLINK_IF = swd + +# Flash using Nuvoton's openocd fork at https://github.com/OpenNuvoton/OpenOCD-Nuvoton +# Please compile and install it from github source +flash: $(BUILD)/$(BOARD)-firmware.elf + openocd -f interface/nulink.cfg -f target/numicroM0.cfg -c "program $< reset exit" diff --git a/hw/bsp/nutiny_sdk_nuc120/nuc120_flash.ld b/hw/bsp/nutiny_sdk_nuc120/nuc120_flash.ld new file mode 100644 index 000000000..cab12c8b9 --- /dev/null +++ b/hw/bsp/nutiny_sdk_nuc120/nuc120_flash.ld @@ -0,0 +1,195 @@ +/* Linker script to configure memory regions. */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x20000 /* 128k */ + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x4000 /* 16k */ +} + +/* Library configurations */ +GROUP(libgcc.a libc.a libm.a libnosys.a) + +/* Linker script to place sections and symbol values. Should be used together + * with other linker script that defines memory regions FLASH and RAM. + * It references following symbols, which must be defined in code: + * Reset_Handler : Entry of reset handler + * + * It defines following symbols, which code can use without definition: + * __exidx_start + * __exidx_end + * __copy_table_start__ + * __copy_table_end__ + * __zero_table_start__ + * __zero_table_end__ + * __etext + * __data_start__ + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __data_end__ + * __bss_start__ + * __bss_end__ + * __end__ + * end + * __HeapLimit + * __StackLimit + * __StackTop + * __stack + * __Vectors_End + * __Vectors_Size + */ +ENTRY(Reset_Handler) + +SECTIONS +{ + .text : + { + KEEP(*(.vectors)) + __Vectors_End = .; + __Vectors_Size = __Vectors_End - __Vectors; + __end__ = .; + + *(.text*) + + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.rodata*) + + KEEP(*(.eh_frame*)) + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* To copy multiple ROM to RAM sections, + * uncomment .copy.table section and, + * define __STARTUP_COPY_MULTIPLE in startup_ARMCMx.S */ + /* + .copy.table : + { + . = ALIGN(4); + __copy_table_start__ = .; + LONG (__etext) + LONG (__data_start__) + LONG (__data_end__ - __data_start__) + LONG (__etext2) + LONG (__data2_start__) + LONG (__data2_end__ - __data2_start__) + __copy_table_end__ = .; + } > FLASH + */ + + /* To clear multiple BSS sections, + * uncomment .zero.table section and, + * define __STARTUP_CLEAR_BSS_MULTIPLE in startup_ARMCMx.S */ + /* + .zero.table : + { + . = ALIGN(4); + __zero_table_start__ = .; + LONG (__bss_start__) + LONG (__bss_end__ - __bss_start__) + LONG (__bss2_start__) + LONG (__bss2_end__ - __bss2_start__) + __zero_table_end__ = .; + } > FLASH + */ + + __etext = .; + + .data : AT (__etext) + { + __data_start__ = .; + *(vtable) + *(.data*) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(4); + /* All data end */ + __data_end__ = .; + + } > RAM + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (COPY): + { + __HeapBase = .; + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + __HeapLimit = .; + } > RAM + + /* .stack_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later */ + .stack_dummy (COPY): + { + KEEP(*(.stack*)) + } > RAM + + /* Set stack top to end of RAM, and stack limit move down by + * size of stack_dummy section */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") +} diff --git a/hw/bsp/nutiny_sdk_nuc120/nutiny_sdk_nuc120.c b/hw/bsp/nutiny_sdk_nuc120/nutiny_sdk_nuc120.c new file mode 100644 index 000000000..0650a4e3a --- /dev/null +++ b/hw/bsp/nutiny_sdk_nuc120/nutiny_sdk_nuc120.c @@ -0,0 +1,122 @@ +/* + * 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 "bsp/board.h" +#include "NUC100Series.h" +#include "clk.h" +#include "sys.h" + +#define LED_PORT PB +#define LED_PIN 0 +#define LED_PIN_IO PB0 +#define LED_STATE_ON 0 + +void board_init(void) +{ + SYS_UnlockReg(); + + /* Enable Internal RC 22.1184 MHz clock */ + CLK_EnableXtalRC(CLK_PWRCON_OSC22M_EN_Msk); + + /* Waiting for Internal RC clock ready */ + CLK_WaitClockReady(CLK_CLKSTATUS_OSC22M_STB_Msk); + + /* Switch HCLK clock source to Internal RC and HCLK source divide 1 */ + CLK_SetHCLK(CLK_CLKSEL0_HCLK_S_HIRC, CLK_CLKDIV_HCLK(1)); + + /* Enable external XTAL 12 MHz clock */ + CLK_EnableXtalRC(CLK_PWRCON_XTL12M_EN_Msk); + + /* Waiting for external XTAL clock ready */ + CLK_WaitClockReady(CLK_CLKSTATUS_XTL12M_STB_Msk); + + /* Set core clock */ + CLK_SetCoreClock(48000000); + + /* Enable module clock */ + CLK_EnableModuleClock(USBD_MODULE); + + /* Select module clock source */ + CLK_SetModuleClock(USBD_MODULE, 0, CLK_CLKDIV_USB(1)); + + SYS_LockReg(); + +#if CFG_TUSB_OS == OPT_OS_NONE + // 1ms tick timer + SysTick_Config(48000000 / 1000); +#endif + + GPIO_SetMode(LED_PORT, 1UL << LED_PIN, GPIO_PMD_OUTPUT); +} + +#if CFG_TUSB_OS == OPT_OS_NONE +volatile uint32_t system_ticks = 0; +void SysTick_Handler (void) +{ + system_ticks++; +} + +uint32_t board_millis(void) +{ + return system_ticks; +} +#endif + +//--------------------------------------------------------------------+ +// Board porting API +//--------------------------------------------------------------------+ + +void board_led_write(bool state) +{ +#if 0 + /* this would be the simplest solution... *IF* the part supported the pin data interface */ + LED_PIN_IO = (state) ? LED_STATE_ON : (1-LED_STATE_ON); +#else + /* if the part's *PDIO pin data registers don't work, a more elaborate approach is needed */ + uint32_t irq_state = __get_PRIMASK(); + __disable_irq(); + uint32_t current = LED_PORT->DOUT & ~(1UL << LED_PIN); + LED_PORT->DOUT = current | (((state) ? LED_STATE_ON : (1UL-LED_STATE_ON)) << LED_PIN); + __set_PRIMASK(irq_state); +#endif +} + +uint32_t board_button_read(void) +{ + return 0; +} + +int board_uart_read(uint8_t* buf, int len) +{ + (void) buf; (void) len; + return 0; +} + +int board_uart_write(void const * buf, int len) +{ + (void) buf; (void) len; + return 0; +} diff --git a/hw/mcu/nuvoton b/hw/mcu/nuvoton index dc96fff79..797dc6cd5 160000 --- a/hw/mcu/nuvoton +++ b/hw/mcu/nuvoton @@ -1 +1 @@ -Subproject commit dc96fff794d14818c93ea1d4d760d51a014d70c5 +Subproject commit 797dc6cd55c84aa6c23c4690da2f3900339a6e24 diff --git a/src/portable/nuvoton/nuc120/dcd_nuc120.c b/src/portable/nuvoton/nuc120/dcd_nuc120.c new file mode 100644 index 000000000..4e1db24a4 --- /dev/null +++ b/src/portable/nuvoton/nuc120/dcd_nuc120.c @@ -0,0 +1,433 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2020 Peter Lawrence + * + * 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. + */ + +/* + Theory of operation: + + The NUC100/NUC120 USBD peripheral has six "EP"s, but each is simplex, + so two collectively (peripheral nomenclature of "EP0" and "EP1") are needed to + implement USB EP0. PERIPH_EP0 and PERIPH_EP1 are used by this driver for + EP0_IN and EP0_OUT respectively. This leaves up to four for user usage. +*/ + +#include "tusb_option.h" + +#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_NUC120) + +#include "device/dcd.h" +#include "NUC100Series.h" + +/* allocation of USBD RAM for Setup, EP0_IN, and and EP_OUT */ +#define PERIPH_SETUP_BUF_BASE 0 +#define PERIPH_SETUP_BUF_LEN 8 +#define PERIPH_EP0_BUF_BASE (PERIPH_SETUP_BUF_BASE + PERIPH_SETUP_BUF_LEN) +#define PERIPH_EP0_BUF_LEN CFG_TUD_ENDPOINT0_SIZE +#define PERIPH_EP1_BUF_BASE (PERIPH_EP0_BUF_BASE + PERIPH_EP0_BUF_LEN) +#define PERIPH_EP1_BUF_LEN CFG_TUD_ENDPOINT0_SIZE +#define PERIPH_EP2_BUF_BASE (PERIPH_EP1_BUF_BASE + PERIPH_EP1_BUF_LEN) + +/* rather important info unfortunately not provided by device include files: how much there is */ +#define USBD_BUF_SIZE 512 + +enum ep_enum +{ + PERIPH_EP0 = 0, + PERIPH_EP1 = 1, + PERIPH_EP2 = 2, + PERIPH_EP3 = 3, + PERIPH_EP4 = 4, + PERIPH_EP5 = 5, + PERIPH_MAX_EP, +}; + +/* set by dcd_set_address() */ +static volatile uint8_t assigned_address; + +/* reset by dcd_init(), this is used by dcd_edpt_open() to assign USBD peripheral buffer addresses */ +static uint32_t bufseg_addr; + +/* used by dcd_edpt_xfer() and the ISR to reset the data sync (DATA0/DATA1) in an EP0_IN transfer */ +static bool active_ep0_xfer; + +/* RAM table needed to track ongoing transfers performed by dcd_edpt_xfer(), dcd_in_xfer(), and the ISR */ +static struct xfer_ctl_t +{ + uint8_t *data_ptr; /* data_ptr tracks where to next copy data to (for OUT) or from (for IN) */ + union { + uint16_t in_remaining_bytes; /* for IN endpoints, we track how many bytes are left to transfer */ + uint16_t out_bytes_so_far; /* but for OUT endpoints, we track how many bytes we've transferred so far */ + }; + uint16_t max_packet_size; /* needed since device driver only finds out this at runtime */ + uint16_t total_bytes; /* quantity needed to pass as argument to dcd_event_xfer_complete() (for IN endpoints) */ +} xfer_table[PERIPH_MAX_EP]; + +/* + local helper functions +*/ + +static void usb_attach(void) +{ + USBD->DRVSE0 &= ~USBD_DRVSE0_DRVSE0_Msk; +} + +static void usb_detach(void) +{ + USBD->DRVSE0 |= USBD_DRVSE0_DRVSE0_Msk; +} + +static void usb_control_send_zlp(void) +{ + USBD->EP[PERIPH_EP0].CFG |= USBD_CFG_DSQ_SYNC_Msk; + USBD->EP[PERIPH_EP0].MXPLD = 0; +} + +/* reconstruct ep_addr from particular USB Configuration Register */ +static uint8_t decode_ep_addr(USBD_EP_T *ep) +{ + uint8_t ep_addr = ep->CFG & USBD_CFG_EP_NUM_Msk; + if ( USBD_CFG_EPMODE_IN == (ep->CFG & USBD_CFG_STATE_Msk) ) + ep_addr |= TUSB_DIR_IN_MASK; + return ep_addr; +} + +/* map 8-bit ep_addr into peripheral endpoint index (PERIPH_EP0...) */ +static USBD_EP_T *ep_entry(uint8_t ep_addr, bool add) +{ + USBD_EP_T *ep; + enum ep_enum ep_index; + + for (ep_index = PERIPH_EP0, ep = USBD->EP; ep_index < PERIPH_MAX_EP; ep_index++, ep++) + { + if (add) + { + /* take first peripheral endpoint that is unused */ + if (0 == (ep->CFG & USBD_CFG_STATE_Msk)) return ep; + } + else + { + /* find a peripheral endpoint that matches ep_addr */ + uint8_t candidate_ep_addr = decode_ep_addr(ep); + if (candidate_ep_addr == ep_addr) return ep; + } + } + + return NULL; +} + +/* perform an IN endpoint transfer; this is called by dcd_edpt_xfer() and the ISR */ +static void dcd_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep) +{ + uint16_t bytes_now = tu_min16(xfer->in_remaining_bytes, xfer->max_packet_size); + + memcpy((uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), xfer->data_ptr, bytes_now); + ep->MXPLD = bytes_now; +} + +/* called by dcd_init() as well as by the ISR during a USB bus reset */ +static void bus_reset(void) +{ + USBD->STBUFSEG = PERIPH_SETUP_BUF_BASE; + + for (enum ep_enum ep_index = PERIPH_EP0; ep_index < PERIPH_MAX_EP; ep_index++) + { + USBD->EP[ep_index].CFG = 0; + USBD->EP[ep_index].CFGP = 0; + } + + /* allocate the default EP0 endpoints */ + + USBD->EP[PERIPH_EP0].CFG = USBD_CFG_CSTALL_Msk | USBD_CFG_EPMODE_IN; + USBD->EP[PERIPH_EP0].BUFSEG = PERIPH_EP0_BUF_BASE; + xfer_table[PERIPH_EP0].max_packet_size = PERIPH_EP0_BUF_LEN; + + USBD->EP[PERIPH_EP1].CFG = USBD_CFG_CSTALL_Msk | USBD_CFG_EPMODE_OUT; + USBD->EP[PERIPH_EP1].BUFSEG = PERIPH_EP1_BUF_BASE; + xfer_table[PERIPH_EP1].max_packet_size = PERIPH_EP1_BUF_LEN; + + /* USB RAM beyond what we've allocated above is available to the user */ + bufseg_addr = PERIPH_EP2_BUF_BASE; + + /* Reset USB device address */ + USBD->FADDR = 0; + + /* reset EP0_IN flag */ + active_ep0_xfer = false; +} + +/* centralized location for USBD interrupt enable bit mask */ +static const uint32_t enabled_irqs = USBD_INTSTS_FLDET_STS_Msk | USBD_INTSTS_BUS_STS_Msk | USBD_INTSTS_SETUP_Msk | USBD_INTSTS_USB_STS_Msk; + +/* + NUC100/NUC120 TinyUSB API driver implementation +*/ + +void dcd_init(uint8_t rhport) +{ + (void) rhport; + + USBD->ATTR = 0x7D0; + + usb_detach(); + + bus_reset(); + + usb_attach(); + + USBD->INTSTS = enabled_irqs; + USBD->INTEN = enabled_irqs; +} + +void dcd_int_enable(uint8_t rhport) +{ + (void) rhport; + NVIC_EnableIRQ(USBD_IRQn); +} + +void dcd_int_disable(uint8_t rhport) +{ + (void) rhport; + NVIC_DisableIRQ(USBD_IRQn); +} + +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) +{ + (void) rhport; + usb_control_send_zlp(); /* SET_ADDRESS is the one exception where TinyUSB doesn't use dcd_edpt_xfer() to generate a ZLP */ + assigned_address = dev_addr; +} + +void dcd_set_config(uint8_t rhport, uint8_t config_num) +{ + (void) rhport; + (void) config_num; +} + +void dcd_remote_wakeup(uint8_t rhport) +{ + (void) rhport; + USBD->ATTR = USBD_ATTR_RWAKEUP_Msk; +} + +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) +{ + (void) rhport; + + USBD_EP_T *ep = ep_entry(p_endpoint_desc->bEndpointAddress, true); + TU_ASSERT(ep); + + /* mine the data for the information we need */ + int const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); + int const size = p_endpoint_desc->wMaxPacketSize.size; + tusb_xfer_type_t const type = p_endpoint_desc->bmAttributes.xfer; + struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP]; + + /* allocate buffer from USB RAM */ + ep->BUFSEG = bufseg_addr; + bufseg_addr += size; + TU_ASSERT(bufseg_addr <= USBD_BUF_SIZE); + + /* construct USB Configuration Register value and then write it */ + uint32_t cfg = tu_edpt_number(p_endpoint_desc->bEndpointAddress); + cfg |= (TUSB_DIR_IN == dir) ? USBD_CFG_EPMODE_IN : USBD_CFG_EPMODE_OUT; + if (TUSB_XFER_ISOCHRONOUS == type) + cfg |= USBD_CFG_TYPE_ISO; + ep->CFG = cfg; + + /* make a note of the endpoint size */ + xfer->max_packet_size = size; + + return true; +} + +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) +{ + (void) rhport; + + /* mine the data for the information we need */ + tusb_dir_t dir = tu_edpt_dir(ep_addr); + USBD_EP_T *ep = ep_entry(ep_addr, false); + struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP]; + + /* store away the information we'll needing now and later */ + xfer->data_ptr = buffer; + xfer->in_remaining_bytes = total_bytes; + xfer->total_bytes = total_bytes; + + /* for the first of one or more EP0_IN packets in a message, the first must be DATA1 */ + if ( (0x80 == ep_addr) && !active_ep0_xfer ) ep->CFG |= USBD_CFG_DSQ_SYNC_Msk; + + if (TUSB_DIR_IN == dir) + { + dcd_in_xfer(xfer, ep); + } + else + { + xfer->out_bytes_so_far = 0; + ep->MXPLD = xfer->max_packet_size; + } + + return true; +} + +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + USBD_EP_T *ep = ep_entry(ep_addr, false); + ep->CFGP |= USBD_CFGP_SSTALL_Msk; +} + +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + USBD_EP_T *ep = ep_entry(ep_addr, false); + ep->CFG |= USBD_CFG_CSTALL_Msk; +} + +void USBD_IRQHandler(void) +{ + uint32_t status = USBD->INTSTS; + uint32_t state = USBD->ATTR & 0xf; + + if(status & USBD_INTSTS_FLDET_STS_Msk) + { + if(USBD->FLDET & USBD_FLDET_FLDET_Msk) + { + /* USB connect */ + USBD->ATTR |= USBD_ATTR_USB_EN_Msk | USBD_ATTR_PHY_EN_Msk; + } + else + { + /* USB disconnect */ + USBD->ATTR &= ~USBD_ATTR_USB_EN_Msk; + } + } + + if(status & USBD_INTSTS_BUS_STS_Msk) + { + if(state & USBD_STATE_USBRST) + { + /* USB bus reset */ + USBD->ATTR |= USBD_ATTR_USB_EN_Msk | USBD_ATTR_PHY_EN_Msk; + + bus_reset(); + + dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true); + } + + if(state & USBD_STATE_SUSPEND) + { + /* Enable USB but disable PHY */ + USBD->ATTR &= ~USBD_ATTR_PHY_EN_Msk; + dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true); + } + + if(state & USBD_STATE_RESUME) + { + /* Enable USB and enable PHY */ + USBD->ATTR |= USBD_ATTR_USB_EN_Msk | USBD_ATTR_PHY_EN_Msk; + dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); + } + } + + if(status & USBD_INTSTS_SETUP_Msk) + { + /* clear the data ready flag of control endpoints */ + USBD->EP[PERIPH_EP0].CFGP |= USBD_CFGP_CLRRDY_Msk; + USBD->EP[PERIPH_EP1].CFGP |= USBD_CFGP_CLRRDY_Msk; + + /* get SETUP packet from USB buffer */ + dcd_event_setup_received(0, (uint8_t *)USBD_BUF_BASE, true); + } + + if(status & USBD_INTSTS_USB_STS_Msk) + { + if (status & (1UL << USBD_INTSTS_EPEVT_Pos)) /* PERIPH_EP0 (EP0_IN) event: this is treated separately from the rest */ + { + /* given ACK from host has happened, we can now set the address (if not already done) */ + if((USBD->FADDR != assigned_address) && (USBD->FADDR == 0)) USBD->FADDR = assigned_address; + + uint16_t const available_bytes = USBD->EP[PERIPH_EP0].MXPLD; + + active_ep0_xfer = (available_bytes == xfer_table[PERIPH_EP0].max_packet_size); + + dcd_event_xfer_complete(0, 0x80, available_bytes, XFER_RESULT_SUCCESS, true); + } + + /* service PERIPH_EP1 through PERIPH_EP7 */ + enum ep_enum ep_index; + uint32_t mask; + struct xfer_ctl_t *xfer; + USBD_EP_T *ep; + for (ep_index = PERIPH_EP1, mask = (2UL << USBD_INTSTS_EPEVT_Pos), xfer = &xfer_table[PERIPH_EP1], ep = &USBD->EP[PERIPH_EP1]; ep_index < PERIPH_MAX_EP; ep_index++, mask <<= 1, xfer++, ep++) + { + if(status & mask) + { + USBD->INTSTS = mask; + + uint16_t const available_bytes = ep->MXPLD; + uint8_t const ep_addr = decode_ep_addr(ep); + bool const out_ep = !(ep_addr & TUSB_DIR_IN_MASK); + + if (out_ep) + { + /* copy the data from the PC to the previously provided buffer */ + memcpy(xfer->data_ptr, (uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), available_bytes); + xfer->out_bytes_so_far += available_bytes; + xfer->data_ptr += available_bytes; + + /* when the transfer is finished, alert TinyUSB; otherwise, accept more data */ + if ( (xfer->total_bytes == xfer->out_bytes_so_far) || (available_bytes < xfer->max_packet_size) ) + dcd_event_xfer_complete(0, ep_addr, xfer->out_bytes_so_far, XFER_RESULT_SUCCESS, true); + else + ep->MXPLD = xfer->max_packet_size; + } + else + { + /* update the bookkeeping to reflect the data that has now been sent to the PC */ + xfer->in_remaining_bytes -= available_bytes; + xfer->data_ptr += available_bytes; + + /* if more data to send, send it; otherwise, alert TinyUSB that we've finished */ + if (xfer->in_remaining_bytes) + dcd_in_xfer(xfer, ep); + else + dcd_event_xfer_complete(0, ep_addr, xfer->total_bytes, XFER_RESULT_SUCCESS, true); + } + } + } + } + + /* acknowledge all interrupts */ + USBD->INTSTS = status & enabled_irqs; +} + +void dcd_isr(uint8_t rhport) +{ + (void) rhport; + USBD_IRQHandler(); +} + +#endif diff --git a/src/tusb_option.h b/src/tusb_option.h index d39dd1046..46684ff7f 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -79,6 +79,7 @@ #define OPT_MCU_NUC121 800 #define OPT_MCU_NUC126 801 +#define OPT_MCU_NUC120 802 /** @} */ -- cgit v1.3.1 From 9ca6311ac49559570c5d7366344fb5887b09e953 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 7 Feb 2020 16:46:18 +0700 Subject: implement walkaround for incorrect DATA Toggle for status control. --- src/portable/microchip/samg/dcd_samg.c | 75 ++++++++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 9 deletions(-) (limited to 'src/portable') diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c index a93b26caa..835bbf01e 100644 --- a/src/portable/microchip/samg/dcd_samg.c +++ b/src/portable/microchip/samg/dcd_samg.c @@ -51,6 +51,11 @@ typedef struct // Endpoint 0-5, each can only be either OUT or In xfer_desc_t _dcd_xfer[EP_COUNT]; +// Indicate that DATA Toggle for Control Status is incorrect, which must always be DATA1 by USB Specs. +// However SAMG DToggle is read-only, therefore we must duplicate the status phase ( D0 then D1 ) +// as walk-around to resolve this. The D0 status packet is likely to be discarded by USB Host safely. +volatile bool _walkaround_incorrect_dtoggle_control_status; + void xfer_epsize_set(xfer_desc_t* xfer, uint16_t epsize) { xfer->epsize = epsize; @@ -111,6 +116,7 @@ static void xact_ep_read(uint8_t epnum, uint8_t* buffer, uint16_t xact_len) // Set up endpoint 0, clear all other endpoints static void bus_reset(void) { + _walkaround_incorrect_dtoggle_control_status = false; tu_memclr(_dcd_xfer, sizeof(_dcd_xfer)); xfer_epsize_set(&_dcd_xfer[0], CFG_TUD_ENDPOINT0_SIZE); @@ -201,7 +207,8 @@ void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * re // Set new address & Function enable bit UDP->UDP_FADDR = UDP_FADDR_FEN_Msk | UDP_FADDR_FADD(dev_addr); - }else if (request->bRequest == TUSB_REQ_SET_CONFIGURATION) + } + else if (request->bRequest == TUSB_REQ_SET_CONFIGURATION) { // Configured State UDP->UDP_GLB_STAT |= UDP_GLB_STAT_CONFG_Msk; @@ -248,10 +255,47 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t xfer_desc_t* xfer = &_dcd_xfer[epnum]; xfer_begin(xfer, buffer, total_bytes); + // Control Endpoint direction and data toggle + if (epnum == 0) + { + // Transfer direction is opposite to one previously set on EP0 + // This transfer is Control Status Stage + if ( dir != tu_bit_test(UDP->UDP_CSR[epnum], UDP_CSR_DIR_Pos) ) + { + // Set/Clear DIR bit accordingly + if (dir) + { + UDP->UDP_CSR[epnum] |= UDP_CSR_DIR_Msk; + }else + { + UDP->UDP_CSR[epnum] &= ~UDP_CSR_DIR_Msk; + } + + // DATA Toggle is 0, USB Specs requires Status Stage must be DATA1 + // Since SAMG DToggle is read-only, we mark this and implement a walk-around + if ( !(UDP->UDP_CSR[epnum] & UDP_CSR_DTGLE_Msk) ) + { + TU_LOG2("Incorrect DATA TOGGLE, Control Status must be DATA1\n"); + + // DTGLE is read-only on SAMG, this statement has no effect + UDP->UDP_CSR[epnum] |= UDP_CSR_DTGLE_Msk; + + _walkaround_incorrect_dtoggle_control_status = true; + } + } + } + + if (dir == TUSB_DIR_IN) { - // Set DIR bit for EP0 - if ( epnum == 0 ) UDP->UDP_CSR[epnum] |= UDP_CSR_DIR_Msk; + // WALKROUND: duplicate IN transfer to send DATA1 status packet + if (_walkaround_incorrect_dtoggle_control_status) + { + UDP->UDP_CSR[epnum] |= UDP_CSR_TXPKTRDY_Msk; + while ( UDP->UDP_CSR[epnum] & UDP_CSR_TXPKTRDY_Msk ) {} + + _walkaround_incorrect_dtoggle_control_status = false; + } xact_ep_write(epnum, xfer->buffer, xfer_packet_len(xfer)); @@ -368,8 +412,17 @@ void dcd_isr(uint8_t rhport) // notify usbd dcd_event_setup_received(rhport, setup, true); - // Clear Setup bit - UDP->UDP_CSR[0] &= ~UDP_CSR_RXSETUP_Msk; + // Set EP direction bit according to DATA stage + if (setup[0] & 0x80) + { + UDP->UDP_CSR[0] |= UDP_CSR_DIR_Msk; + }else + { + UDP->UDP_CSR[0] &= ~UDP_CSR_DIR_Msk; + } + + // Clear Setup bit & stall bit if needed + UDP->UDP_CSR[0] &= ~(UDP_CSR_RXSETUP_Msk | UDP_CSR_FORCESTALL_Msk); return; } @@ -397,8 +450,12 @@ void dcd_isr(uint8_t rhport) UDP->UDP_CSR[epnum] |= UDP_CSR_TXPKTRDY_Msk; }else { - // xfer is complete - dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true); + // WALKAROUND: Skip reporting this incorrect DATA Toggle status transfer + if ( !(_walkaround_incorrect_dtoggle_control_status && (epnum == 0) && (xfer->actual_len == 0)) ) + { + // xfer is complete + dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true); + } } // Clear TX Complete bit @@ -406,8 +463,8 @@ void dcd_isr(uint8_t rhport) } // Endpoint OUT - // Ping-Pong is a must for Bulk/Iso - // When both Bank0 and Bank1 are both set, there is not way to know which one comes first + // Ping-Pong is a MUST for Bulk/Iso + // When both Bank0 and Bank1 are both set, there is no way to know which one comes first if (UDP->UDP_CSR[epnum] & (UDP_CSR_RX_DATA_BK0_Msk | UDP_CSR_RX_DATA_BK1_Msk)) { uint16_t const xact_len = (uint16_t) ((UDP->UDP_CSR[epnum] & UDP_CSR_RXBYTECNT_Msk) >> UDP_CSR_RXBYTECNT_Pos); -- cgit v1.3.1 From 02b2c602319a55226429582dfd8002c95cc3ff87 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 7 Feb 2020 17:39:20 +0700 Subject: samg55 work with cdc msc example --- src/portable/microchip/samg/dcd_samg.c | 56 +++++++++------------------------- 1 file changed, 15 insertions(+), 41 deletions(-) (limited to 'src/portable') diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c index 835bbf01e..53ccba961 100644 --- a/src/portable/microchip/samg/dcd_samg.c +++ b/src/portable/microchip/samg/dcd_samg.c @@ -304,34 +304,6 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t } else { - // Clear DIR bit for EP0 - if ( epnum == 0 ) UDP->UDP_CSR[epnum] &= ~UDP_CSR_DIR_Msk; - - // OUT Data may already received and acked by hardware - // Read it as 1st packet then continue with transfer if needed - if ( UDP->UDP_CSR[epnum] & (UDP_CSR_RX_DATA_BK0_Msk | UDP_CSR_RX_DATA_BK1_Msk) ) - { -// uint16_t const xact_len = (uint16_t) ((UDP->UDP_CSR[epnum] & UDP_CSR_RXBYTECNT_Msk) >> UDP_CSR_RXBYTECNT_Pos); - -// TU_LOG2("xact_len = %d\r", xact_len); - -// // Read from EP fifo -// xact_ep_read(epnum, xfer->buffer, xact_len); -// xfer_packet_done(xfer); -// -// // Clear DATA Bank0 bit -// UDP->UDP_CSR[epnum] &= ~UDP_CSR_RX_DATA_BK0_Msk; -// -// if ( 0 == xfer_packet_len(xfer) ) -// { -// // Disable OUT EP interrupt when transfer is complete -// UDP->UDP_IER &= ~(1 << epnum); -// -// dcd_event_xfer_complete(rhport, epnum, xact_len, XFER_RESULT_SUCCESS, false); -// return true; // complete -// } - } - // Enable interrupt when starting OUT transfer if (epnum != 0) UDP->UDP_IER |= (1 << epnum); } @@ -387,13 +359,13 @@ void dcd_isr(uint8_t rhport) // if (intr_status & UDP_ISR_SOFINT_Msk) dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true); // Suspend -// if (intr_status & UDP_ISR_RXSUSP_Msk) dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); + if (intr_status & UDP_ISR_RXSUSP_Msk) dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); // Resume -// if (intr_status & UDP_ISR_RXRSM_Msk) dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); + if (intr_status & UDP_ISR_RXRSM_Msk) dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); // Wakeup -// if (intr_status & UDP_ISR_WAKEUP_Msk) dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); + if (intr_status & UDP_ISR_WAKEUP_Msk) dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); //------------- Endpoints -------------// @@ -434,7 +406,7 @@ void dcd_isr(uint8_t rhport) { xfer_desc_t* xfer = &_dcd_xfer[epnum]; - // Endpoint IN + //------------- Endpoint IN -------------// if (UDP->UDP_CSR[epnum] & UDP_CSR_TXCOMP_Msk) { xfer_packet_done(xfer); @@ -455,6 +427,9 @@ void dcd_isr(uint8_t rhport) { // xfer is complete dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true); + + // Required since control OUT can happen right after before stack handle this event + xfer_end(xfer); } } @@ -462,15 +437,14 @@ void dcd_isr(uint8_t rhport) UDP->UDP_CSR[epnum] &= ~UDP_CSR_TXCOMP_Msk; } - // Endpoint OUT + //------------- Endpoint OUT -------------// // Ping-Pong is a MUST for Bulk/Iso - // When both Bank0 and Bank1 are both set, there is no way to know which one comes first - if (UDP->UDP_CSR[epnum] & (UDP_CSR_RX_DATA_BK0_Msk | UDP_CSR_RX_DATA_BK1_Msk)) + // NOTE: When both Bank0 and Bank1 are both set, there is no way to know which one comes first + uint32_t const banks_complete = UDP->UDP_CSR[epnum] & (UDP_CSR_RX_DATA_BK0_Msk | UDP_CSR_RX_DATA_BK1_Msk); + if (banks_complete) { uint16_t const xact_len = (uint16_t) ((UDP->UDP_CSR[epnum] & UDP_CSR_RXBYTECNT_Msk) >> UDP_CSR_RXBYTECNT_Pos); - //if (epnum != 0) TU_LOG2("xact_len = %d\r", xact_len); - // Read from EP fifo xact_ep_read(epnum, xfer->buffer, xact_len); xfer_packet_done(xfer); @@ -480,12 +454,12 @@ void dcd_isr(uint8_t rhport) // Disable OUT EP interrupt when transfer is complete if (epnum != 0) UDP->UDP_IDR |= (1 << epnum); - dcd_event_xfer_complete(rhport, epnum, xact_len, XFER_RESULT_SUCCESS, true); -// xfer_end(xfer); + dcd_event_xfer_complete(rhport, epnum, xfer->actual_len, XFER_RESULT_SUCCESS, true); + xfer_end(xfer); } - // Clear DATA Bank0 bit - UDP->UDP_CSR[epnum] &= ~(UDP_CSR_RX_DATA_BK0_Msk | UDP_CSR_RX_DATA_BK1_Msk); + // Clear DATA Bank0/1 bit + UDP->UDP_CSR[epnum] &= ~banks_complete; } // Stall sent to host -- cgit v1.3.1 From 46f22860fb6eaf0b65469b47b099e20f489b2e47 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 7 Feb 2020 22:13:38 +0700 Subject: correct waklaround with only status IN correct usb descriptor msc dual example to work with samg --- examples/device/cdc_msc/src/usb_descriptors.c | 2 +- examples/device/msc_dual_lun/src/usb_descriptors.c | 18 ++++-- src/portable/microchip/samg/dcd_samg.c | 67 ++++++++++------------ 3 files changed, 45 insertions(+), 42 deletions(-) (limited to 'src/portable') diff --git a/examples/device/cdc_msc/src/usb_descriptors.c b/examples/device/cdc_msc/src/usb_descriptors.c index e8527df0b..5cae7760e 100644 --- a/examples/device/cdc_msc/src/usb_descriptors.c +++ b/examples/device/cdc_msc/src/usb_descriptors.c @@ -95,7 +95,7 @@ enum #define EPNUM_MSC_IN 0x85 #elif CFG_TUSB_MCU == OPT_MCU_SAMG - // SAMG doesn't support a same endpoint number with IN and OUT + // SAMG doesn't support a same endpoint number with different direction IN and OUT // e.g EP1 OUT & EP1 IN cannot exist together #define EPNUM_CDC_NOTIF 0x81 #define EPNUM_CDC_OUT 0x02 diff --git a/examples/device/msc_dual_lun/src/usb_descriptors.c b/examples/device/msc_dual_lun/src/usb_descriptors.c index e0a5904a4..88a96fb9a 100644 --- a/examples/device/msc_dual_lun/src/usb_descriptors.c +++ b/examples/device/msc_dual_lun/src/usb_descriptors.c @@ -80,10 +80,20 @@ enum #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_MSC 0x02 + // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In, 5 Bulk etc ... + #define EPNUM_MSC_OUT 0x02 + #define EPNUM_MSC_IN 0x82 + +#elif CFG_TUSB_MCU == OPT_MCU_SAMG + // SAMG doesn't support a same endpoint number with different direction IN and OUT + // e.g EP1 OUT & EP1 IN cannot exist together + #define EPNUM_MSC_OUT 0x01 + #define EPNUM_MSC_IN 0x82 + #else - #define EPNUM_MSC 0x01 + #define EPNUM_MSC_OUT 0x01 + #define EPNUM_MSC_IN 0x81 + #endif uint8_t const desc_configuration[] = @@ -92,7 +102,7 @@ uint8_t const desc_configuration[] = TUD_CONFIG_DESCRIPTOR(ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), // Interface number, string index, EP Out & EP In address, EP size - TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EPNUM_MSC, 0x80 | EPNUM_MSC, (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 512 : 64), + TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EPNUM_MSC_OUT, EPNUM_MSC_IN, (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 512 : 64), }; // Invoked when received GET CONFIGURATION DESCRIPTOR diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c index 53ccba961..882072521 100644 --- a/src/portable/microchip/samg/dcd_samg.c +++ b/src/portable/microchip/samg/dcd_samg.c @@ -54,6 +54,7 @@ xfer_desc_t _dcd_xfer[EP_COUNT]; // Indicate that DATA Toggle for Control Status is incorrect, which must always be DATA1 by USB Specs. // However SAMG DToggle is read-only, therefore we must duplicate the status phase ( D0 then D1 ) // as walk-around to resolve this. The D0 status packet is likely to be discarded by USB Host safely. +// Note: Only needed for IN Status e.g CDC_SET_LINE_CODING, since out data is sent by host volatile bool _walkaround_incorrect_dtoggle_control_status; void xfer_epsize_set(xfer_desc_t* xfer, uint16_t epsize) @@ -255,46 +256,43 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t xfer_desc_t* xfer = &_dcd_xfer[epnum]; xfer_begin(xfer, buffer, total_bytes); - // Control Endpoint direction and data toggle - if (epnum == 0) + if (dir == TUSB_DIR_OUT) { - // Transfer direction is opposite to one previously set on EP0 - // This transfer is Control Status Stage - if ( dir != tu_bit_test(UDP->UDP_CSR[epnum], UDP_CSR_DIR_Pos) ) + // Clear EP0 direction bit + if (epnum == 0) UDP->UDP_CSR[epnum] &= ~UDP_CSR_DIR_Msk; + + // Enable interrupt when starting OUT transfer + if (epnum != 0) UDP->UDP_IER |= (1 << epnum); + } + else + { + if (epnum == 0) { - // Set/Clear DIR bit accordingly - if (dir) + // Previous EP0 direction is OUT --> This transfer is ZLP control status. + if ( !(UDP->UDP_CSR[epnum] & UDP_CSR_DIR_Msk) ) { + // Set EP0 dir bit UDP->UDP_CSR[epnum] |= UDP_CSR_DIR_Msk; - }else - { - UDP->UDP_CSR[epnum] &= ~UDP_CSR_DIR_Msk; - } - // DATA Toggle is 0, USB Specs requires Status Stage must be DATA1 - // Since SAMG DToggle is read-only, we mark this and implement a walk-around - if ( !(UDP->UDP_CSR[epnum] & UDP_CSR_DTGLE_Msk) ) - { - TU_LOG2("Incorrect DATA TOGGLE, Control Status must be DATA1\n"); + // DATA Toggle is 0, USB Specs requires Status Stage must be DATA1 + // Since SAMG DToggle is read-only, we mark this and implement the walk-around + if ( !(UDP->UDP_CSR[epnum] & UDP_CSR_DTGLE_Msk) ) + { + TU_LOG2("Incorrect DATA TOGGLE, Control Status must be DATA1\n"); - // DTGLE is read-only on SAMG, this statement has no effect - UDP->UDP_CSR[epnum] |= UDP_CSR_DTGLE_Msk; - - _walkaround_incorrect_dtoggle_control_status = true; - } - } - } + // DTGLE is read-only on SAMG, this statement has no effect + UDP->UDP_CSR[epnum] |= UDP_CSR_DTGLE_Msk; + // WALKROUND: duplicate IN transfer to send DATA1 status packet + // set flag for irq to skip reporting first incorrect packet + _walkaround_incorrect_dtoggle_control_status = true; - if (dir == TUSB_DIR_IN) - { - // WALKROUND: duplicate IN transfer to send DATA1 status packet - if (_walkaround_incorrect_dtoggle_control_status) - { - UDP->UDP_CSR[epnum] |= UDP_CSR_TXPKTRDY_Msk; - while ( UDP->UDP_CSR[epnum] & UDP_CSR_TXPKTRDY_Msk ) {} + UDP->UDP_CSR[epnum] |= UDP_CSR_TXPKTRDY_Msk; + while ( UDP->UDP_CSR[epnum] & UDP_CSR_TXPKTRDY_Msk ) {} - _walkaround_incorrect_dtoggle_control_status = false; + _walkaround_incorrect_dtoggle_control_status = false; + } + } } xact_ep_write(epnum, xfer->buffer, xfer_packet_len(xfer)); @@ -302,11 +300,6 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t // TX ready for transfer UDP->UDP_CSR[epnum] |= UDP_CSR_TXPKTRDY_Msk; } - else - { - // Enable interrupt when starting OUT transfer - if (epnum != 0) UDP->UDP_IER |= (1 << epnum); - } return true; } @@ -422,7 +415,7 @@ void dcd_isr(uint8_t rhport) UDP->UDP_CSR[epnum] |= UDP_CSR_TXPKTRDY_Msk; }else { - // WALKAROUND: Skip reporting this incorrect DATA Toggle status transfer + // WALKAROUND: Skip reporting this incorrect DATA Toggle status IN transfer if ( !(_walkaround_incorrect_dtoggle_control_status && (epnum == 0) && (xfer->actual_len == 0)) ) { // xfer is complete -- cgit v1.3.1 From 7d8ecc63a60b039b6e59961023ffb1c228e20d4f Mon Sep 17 00:00:00 2001 From: Peter Lawrence <12226419+majbthrd@users.noreply.github.com> Date: Fri, 7 Feb 2020 20:11:00 -0600 Subject: add Nuvoton NUC505 --- README.md | 2 +- examples/device/cdc_dual_ports/src/tusb_config.h | 2 +- examples/device/cdc_msc/src/tusb_config.h | 2 +- .../device/cdc_msc_hid_freertos/src/tusb_config.h | 2 +- examples/device/dfu_rt/src/tusb_config.h | 2 +- examples/device/hid_composite/src/tusb_config.h | 2 +- .../device/hid_generic_inout/src/tusb_config.h | 2 +- examples/device/midi_test/src/tusb_config.h | 2 +- examples/device/msc_dual_lun/src/tusb_config.h | 2 +- examples/device/usbtmc/src/tusb_config.h | 2 +- examples/device/webusb_serial/src/tusb_config.h | 2 +- hw/bsp/nutiny_sdk_nuc505/board.mk | 52 ++ hw/bsp/nutiny_sdk_nuc505/nuc505_flashtoram.ld | 199 +++++++ hw/bsp/nutiny_sdk_nuc505/nutiny_sdk_nuc505.c | 115 ++++ hw/mcu/nuvoton | 2 +- src/portable/nuvoton/nuc505/dcd_nuc505.c | 639 +++++++++++++++++++++ src/tusb_option.h | 1 + 17 files changed, 1018 insertions(+), 12 deletions(-) create mode 100644 hw/bsp/nutiny_sdk_nuc505/board.mk create mode 100644 hw/bsp/nutiny_sdk_nuc505/nuc505_flashtoram.ld create mode 100644 hw/bsp/nutiny_sdk_nuc505/nutiny_sdk_nuc505.c create mode 100644 src/portable/nuvoton/nuc505/dcd_nuc505.c (limited to 'src/portable') diff --git a/README.md b/README.md index 29052d01a..7ab55f781 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ The stack supports the following MCUs: - **Sony:** CXD56 - **ST:** STM32 series: L0, F0, F1, F2, F3, F4, F7, H7 (device only) - **[ValentyUSB](https://github.com/im-tomu/valentyusb)** eptri -- **Nuvoton:** NUC121/NUC125, NUC126 +- **Nuvoton:** NUC121/NUC125, NUC126, NUC505 [Here is the list of supported Boards](docs/boards.md) that can be used with provided examples. diff --git a/examples/device/cdc_dual_ports/src/tusb_config.h b/examples/device/cdc_dual_ports/src/tusb_config.h index 734b96c16..9783bd4f6 100644 --- a/examples/device/cdc_dual_ports/src/tusb_config.h +++ b/examples/device/cdc_dual_ports/src/tusb_config.h @@ -39,7 +39,7 @@ #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 +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || CFG_TUSB_MCU == OPT_MCU_NUC505 #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) #else #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE diff --git a/examples/device/cdc_msc/src/tusb_config.h b/examples/device/cdc_msc/src/tusb_config.h index 5b7a1671e..d881cb638 100644 --- a/examples/device/cdc_msc/src/tusb_config.h +++ b/examples/device/cdc_msc/src/tusb_config.h @@ -39,7 +39,7 @@ #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 +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || CFG_TUSB_MCU == OPT_MCU_NUC505 #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) #else #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE diff --git a/examples/device/cdc_msc_hid_freertos/src/tusb_config.h b/examples/device/cdc_msc_hid_freertos/src/tusb_config.h index 6788a7483..39738333c 100644 --- a/examples/device/cdc_msc_hid_freertos/src/tusb_config.h +++ b/examples/device/cdc_msc_hid_freertos/src/tusb_config.h @@ -39,7 +39,7 @@ #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 +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || CFG_TUSB_MCU == OPT_MCU_NUC505 #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) #else #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE diff --git a/examples/device/dfu_rt/src/tusb_config.h b/examples/device/dfu_rt/src/tusb_config.h index 27afdb6b6..96c3a2468 100644 --- a/examples/device/dfu_rt/src/tusb_config.h +++ b/examples/device/dfu_rt/src/tusb_config.h @@ -21,7 +21,7 @@ #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 +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || CFG_TUSB_MCU == OPT_MCU_NUC505 #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) #else #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE diff --git a/examples/device/hid_composite/src/tusb_config.h b/examples/device/hid_composite/src/tusb_config.h index dd360741d..6d2fd0836 100644 --- a/examples/device/hid_composite/src/tusb_config.h +++ b/examples/device/hid_composite/src/tusb_config.h @@ -39,7 +39,7 @@ #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 +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || CFG_TUSB_MCU == OPT_MCU_NUC505 #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) #else #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE diff --git a/examples/device/hid_generic_inout/src/tusb_config.h b/examples/device/hid_generic_inout/src/tusb_config.h index 0053a7d0a..2debafd2a 100644 --- a/examples/device/hid_generic_inout/src/tusb_config.h +++ b/examples/device/hid_generic_inout/src/tusb_config.h @@ -39,7 +39,7 @@ #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 +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || CFG_TUSB_MCU == OPT_MCU_NUC505 #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) #else #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE diff --git a/examples/device/midi_test/src/tusb_config.h b/examples/device/midi_test/src/tusb_config.h index 5837db4eb..56f326f31 100644 --- a/examples/device/midi_test/src/tusb_config.h +++ b/examples/device/midi_test/src/tusb_config.h @@ -39,7 +39,7 @@ #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 +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || CFG_TUSB_MCU == OPT_MCU_NUC505 #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) #else #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE diff --git a/examples/device/msc_dual_lun/src/tusb_config.h b/examples/device/msc_dual_lun/src/tusb_config.h index baf857b71..82d07c60a 100644 --- a/examples/device/msc_dual_lun/src/tusb_config.h +++ b/examples/device/msc_dual_lun/src/tusb_config.h @@ -39,7 +39,7 @@ #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 +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || CFG_TUSB_MCU == OPT_MCU_NUC505 #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) #else #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE diff --git a/examples/device/usbtmc/src/tusb_config.h b/examples/device/usbtmc/src/tusb_config.h index 2932ea4d1..5dddc2c46 100644 --- a/examples/device/usbtmc/src/tusb_config.h +++ b/examples/device/usbtmc/src/tusb_config.h @@ -21,7 +21,7 @@ #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 +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || CFG_TUSB_MCU == OPT_MCU_NUC505 #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) #else #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE diff --git a/examples/device/webusb_serial/src/tusb_config.h b/examples/device/webusb_serial/src/tusb_config.h index 30acdaccf..b3f449f93 100644 --- a/examples/device/webusb_serial/src/tusb_config.h +++ b/examples/device/webusb_serial/src/tusb_config.h @@ -39,7 +39,7 @@ #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 +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || CFG_TUSB_MCU == OPT_MCU_NUC505 #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) #else #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE diff --git a/hw/bsp/nutiny_sdk_nuc505/board.mk b/hw/bsp/nutiny_sdk_nuc505/board.mk new file mode 100644 index 000000000..259d11082 --- /dev/null +++ b/hw/bsp/nutiny_sdk_nuc505/board.mk @@ -0,0 +1,52 @@ +CFLAGS += \ + -flto \ + -mthumb \ + -mabi=aapcs-linux \ + -mcpu=cortex-m4 \ + -DCFG_TUSB_MCU=OPT_MCU_NUC505 + +# All source paths should be relative to the top level. +LD_FILE = hw/bsp/nutiny_sdk_nuc505/nuc505_flashtoram.ld + +SRC_C += \ + hw/mcu/nuvoton/nuc505/Device/Nuvoton/NUC505Series/Source/system_NUC505Series.c \ + hw/mcu/nuvoton/nuc505/StdDriver/src/adc.c \ + hw/mcu/nuvoton/nuc505/StdDriver/src/clk.c \ + hw/mcu/nuvoton/nuc505/StdDriver/src/gpio.c \ + hw/mcu/nuvoton/nuc505/StdDriver/src/i2c.c \ + hw/mcu/nuvoton/nuc505/StdDriver/src/i2s.c \ + hw/mcu/nuvoton/nuc505/StdDriver/src/pwm.c \ + hw/mcu/nuvoton/nuc505/StdDriver/src/rtc.c \ + hw/mcu/nuvoton/nuc505/StdDriver/src/sd.c \ + hw/mcu/nuvoton/nuc505/StdDriver/src/spi.c \ + hw/mcu/nuvoton/nuc505/StdDriver/src/spim.c \ + hw/mcu/nuvoton/nuc505/StdDriver/src/sys.c \ + hw/mcu/nuvoton/nuc505/StdDriver/src/timer.c \ + hw/mcu/nuvoton/nuc505/StdDriver/src/uart.c \ + hw/mcu/nuvoton/nuc505/StdDriver/src/usbd.c \ + hw/mcu/nuvoton/nuc505/StdDriver/src/wdt.c \ + hw/mcu/nuvoton/nuc505/StdDriver/src/wwdt.c + +SRC_S += \ + hw/mcu/nuvoton/nuc505/Device/Nuvoton/NUC505Series/Source/GCC/startup_NUC505Series.S + +INC += \ + $(TOP)/hw/mcu/nuvoton/nuc505/Device/Nuvoton/NUC505Series/Include \ + $(TOP)/hw/mcu/nuvoton/nuc505/StdDriver/inc \ + $(TOP)/hw/mcu/nuvoton/nuc505/CMSIS/Include + +# For TinyUSB port source +VENDOR = nuvoton +CHIP_FAMILY = nuc505 + +# For freeRTOS port source +FREERTOS_PORT = ARM_CM4 + +# For flash-jlink target +JLINK_DEVICE = NUC505YO13Y +JLINK_IF = swd + +# Flash using Nuvoton's openocd fork at https://github.com/OpenNuvoton/OpenOCD-Nuvoton +# Please compile and install it from github source +flash: $(BUILD)/$(BOARD)-firmware.elf + openocd -f interface/nulink.cfg -f target/numicroM4.cfg -c "program $< reset exit" diff --git a/hw/bsp/nutiny_sdk_nuc505/nuc505_flashtoram.ld b/hw/bsp/nutiny_sdk_nuc505/nuc505_flashtoram.ld new file mode 100644 index 000000000..53d385cd0 --- /dev/null +++ b/hw/bsp/nutiny_sdk_nuc505/nuc505_flashtoram.ld @@ -0,0 +1,199 @@ +/* Linker script to configure memory regions. */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x80000 /* 512k */ + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x20000 /* 128k */ +} + +/* Library configurations */ +GROUP(libgcc.a libc.a libm.a libnosys.a) + +/* Linker script to place sections and symbol values. Should be used together + * with other linker script that defines memory regions FLASH and RAM. + * It references following symbols, which must be defined in code: + * Reset_Handler : Entry of reset handler + * + * It defines following symbols, which code can use without definition: + * __exidx_start + * __exidx_end + * __copy_table_start__ + * __copy_table_end__ + * __zero_table_start__ + * __zero_table_end__ + * __etext + * __data_start__ + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __data_end__ + * __bss_start__ + * __bss_end__ + * __end__ + * end + * __HeapLimit + * __StackLimit + * __StackTop + * __stack + * __Vectors_End + * __Vectors_Size + */ +ENTRY(Reset_Handler) + +SECTIONS +{ + .startup : + { + KEEP(*(.vectors)) + __Vectors_End = .; + __Vectors_Size = __Vectors_End - __Vectors; + __end__ = .; + + KEEP(*(.preinit)) + + KEEP(*(.init)) + KEEP(*(.fini)) + + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* To copy multiple ROM to RAM sections, + * uncomment .copy.table section and, + * define __STARTUP_COPY_MULTIPLE in startup_ARMCMx.S */ + /* + .copy.table : + { + . = ALIGN(4); + __copy_table_start__ = .; + LONG (__etext) + LONG (__data_start__) + LONG (__data_end__ - __data_start__) + LONG (__etext2) + LONG (__data2_start__) + LONG (__data2_end__ - __data2_start__) + __copy_table_end__ = .; + } > FLASH + */ + + /* To clear multiple BSS sections, + * uncomment .zero.table section and, + * define __STARTUP_CLEAR_BSS_MULTIPLE in startup_ARMCMx.S */ + /* + .zero.table : + { + . = ALIGN(4); + __zero_table_start__ = .; + LONG (__bss_start__) + LONG (__bss_end__ - __bss_start__) + LONG (__bss2_start__) + LONG (__bss2_end__ - __bss2_start__) + __zero_table_end__ = .; + } > FLASH + */ + + __etext = .; + + .data : AT (__etext) + { + __data_start__ = .; + + *(.text*) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.rodata*) + + KEEP(*(.eh_frame*)) + + *(vtable) + *(.data*) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(4); + /* All data end */ + __data_end__ = .; + + } > RAM + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (COPY): + { + __HeapBase = .; + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + __HeapLimit = .; + } > RAM + + /* .stack_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later */ + .stack_dummy (COPY): + { + KEEP(*(.stack*)) + } > RAM + + /* Set stack top to end of RAM, and stack limit move down by + * size of stack_dummy section */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") +} diff --git a/hw/bsp/nutiny_sdk_nuc505/nutiny_sdk_nuc505.c b/hw/bsp/nutiny_sdk_nuc505/nutiny_sdk_nuc505.c new file mode 100644 index 000000000..e666a10aa --- /dev/null +++ b/hw/bsp/nutiny_sdk_nuc505/nutiny_sdk_nuc505.c @@ -0,0 +1,115 @@ +/* + * 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 "bsp/board.h" +#include "NUC505Series.h" + +#define LED_PORT PC +#define LED_PIN 3 +#define LED_STATE_ON 0 + +void board_init(void) +{ + /* Enable XTAL */ + CLK->PWRCTL |= CLK_PWRCTL_HXTEN_Msk; + + CLK_SetCoreClock(96000000); + + /* Set PCLK divider */ + CLK_SetModuleClock(PCLK_MODULE, 0, 1); + + /* Update System Core Clock */ + SystemCoreClockUpdate(); + + /* Enable USB IP clock */ + CLK_EnableModuleClock(USBD_MODULE); + + /* Select USB IP clock source */ + CLK_SetModuleClock(USBD_MODULE, CLK_USBD_SRC_EXT, 0); + + CLK_SetModuleClock(PCLK_MODULE, 0, 1); + + /* Enable PHY */ + USBD_ENABLE_PHY(); + /* wait PHY clock ready */ + while (1) { + USBD->EP[EPA].EPMPS = 0x20; + if (USBD->EP[EPA].EPMPS == 0x20) + break; + } + + /* Force SE0, and then clear it to connect*/ + USBD_SET_SE0(); + +#if CFG_TUSB_OS == OPT_OS_NONE + // 1ms tick timer + SysTick_Config(96000000 / 1000); +#endif + + GPIO_SetMode(LED_PORT, 1UL << LED_PIN, GPIO_MODE_OUTPUT); +} + +#if CFG_TUSB_OS == OPT_OS_NONE +volatile uint32_t system_ticks = 0; +void SysTick_Handler (void) +{ + system_ticks++; +} + +uint32_t board_millis(void) +{ + return system_ticks; +} +#endif + +//--------------------------------------------------------------------+ +// Board porting API +//--------------------------------------------------------------------+ + +void board_led_write(bool state) +{ + uint32_t current = (state) ? LED_STATE_ON : (1-LED_STATE_ON); + current <<= LED_PIN; + current |= LED_PORT->DOUT & ~(1UL << LED_PIN); + LED_PORT->DOUT = current; +} + +uint32_t board_button_read(void) +{ + return 0; +} + +int board_uart_read(uint8_t* buf, int len) +{ + (void) buf; (void) len; + return 0; +} + +int board_uart_write(void const * buf, int len) +{ + (void) buf; (void) len; + return 0; +} diff --git a/hw/mcu/nuvoton b/hw/mcu/nuvoton index dc96fff79..2204191ec 160000 --- a/hw/mcu/nuvoton +++ b/hw/mcu/nuvoton @@ -1 +1 @@ -Subproject commit dc96fff794d14818c93ea1d4d760d51a014d70c5 +Subproject commit 2204191ec76283371419fbcec207da02e1bc22fa diff --git a/src/portable/nuvoton/nuc505/dcd_nuc505.c b/src/portable/nuvoton/nuc505/dcd_nuc505.c new file mode 100644 index 000000000..2b6a66bc2 --- /dev/null +++ b/src/portable/nuvoton/nuc505/dcd_nuc505.c @@ -0,0 +1,639 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020 Peter Lawrence + * + * 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. + */ + +/* + Theory of operation: + + The NUC505 USBD peripheral has twelve "EP"s, where each is simplex, in addition + to dedicated support for the control endpoint (EP0). The non-user endpoints + are referred to as "user" EPs in this code, and follow the datasheet + nomenclature of EPA through EPL. +*/ + +#include "tusb_option.h" + +#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_NUC505) + +#include "device/dcd.h" +#include "NUC505Series.h" + +/* rather important info unfortunately not provided by device include files */ +#define USBD_BUF_SIZE 2048 /* how much USB buffer space there is */ +#define USBD_MAX_DMA_LEN 0x1000 /* max bytes that can be DMAed at one time */ + +enum ep_enum +{ + PERIPH_EPA = 0, + PERIPH_EPB = 1, + PERIPH_EPC = 2, + PERIPH_EPD = 3, + PERIPH_EPE = 4, + PERIPH_EPF = 5, + PERIPH_EPG = 6, + PERIPH_EPH = 7, + PERIPH_EPI = 8, + PERIPH_EPJ = 9, + PERIPH_EPK = 10, + PERIPH_EPL = 11, + PERIPH_MAX_EP, +}; + +static const uint8_t epcfg_eptype_table[] = +{ + [TUSB_XFER_CONTROL] = 0, /* won't happen, since control EPs have dedicated registers */ + [TUSB_XFER_ISOCHRONOUS] = 3 << USBD_EPCFG_EPTYPE_Pos, + [TUSB_XFER_BULK] = 1 << USBD_EPCFG_EPTYPE_Pos, + [TUSB_XFER_INTERRUPT] = 2 << USBD_EPCFG_EPTYPE_Pos, +}; + +static const uint8_t eprspctl_eptype_table[] = +{ + [TUSB_XFER_CONTROL] = 0, /* won't happen, since control EPs have dedicated registers */ + [TUSB_XFER_ISOCHRONOUS] = 2 << USBD_EPRSPCTL_MODE_Pos, /* Fly Mode */ + [TUSB_XFER_BULK] = 0 << USBD_EPRSPCTL_MODE_Pos, /* Auto-Validate Mode */ + [TUSB_XFER_INTERRUPT] = 1 << USBD_EPRSPCTL_MODE_Pos, /* Manual-Validate Mode */ +}; + +/* set by dcd_set_address() */ +static volatile uint8_t assigned_address; + +/* reset by bus_reset(), this is used by dcd_edpt_open() to assign USBD peripheral buffer addresses */ +static uint32_t bufseg_addr; + +/* RAM table needed to track ongoing transfers performed by dcd_edpt_xfer(), dcd_userEP_in_xfer(), and the ISR */ +static struct xfer_ctl_t +{ + uint8_t *data_ptr; /* data_ptr tracks where to next copy data to (for OUT) or from (for IN) */ + union { + uint16_t in_remaining_bytes; /* for IN endpoints, we track how many bytes are left to transfer */ + uint16_t out_bytes_so_far; /* but for OUT endpoints, we track how many bytes we've transferred so far */ + }; + uint16_t max_packet_size; /* needed since device driver only finds out this at runtime */ + uint16_t total_bytes; /* quantity needed to pass as argument to dcd_event_xfer_complete() (for IN endpoints) */ + uint8_t ep_addr; + bool dma_requested; +} xfer_table[PERIPH_MAX_EP]; + +/* in addition to xfer_table, additional bespoke bookkeeping is maintained for control EP0 IN */ +static struct +{ + uint8_t *data_ptr; + uint16_t in_remaining_bytes; + uint16_t total_bytes; +} ctrl_in_xfer; + +static volatile bool configuration_changed; + +static volatile struct xfer_ctl_t *current_dma_xfer; + + +/* + local helper functions +*/ + +static void usb_attach(void) +{ + USBD->PHYCTL |= USBD_PHYCTL_DPPUEN_Msk; +} + +static void usb_control_send_zlp(void) +{ + USBD->CEPINTSTS = USBD_CEPINTSTS_STSDONEIF_Msk; + USBD->CEPCTL = 0; /* clear NAKCLR bit */ + USBD->CEPINTEN = USBD_CEPINTEN_STSDONEIEN_Msk; +} + +/* map 8-bit ep_addr into peripheral endpoint index (PERIPH_EPA...) */ +static USBD_EP_T *ep_entry(uint8_t ep_addr, bool add) +{ + USBD_EP_T *ep; + enum ep_enum ep_index; + struct xfer_ctl_t *xfer; + + for (ep_index = PERIPH_EPA, xfer = &xfer_table[PERIPH_EPA], ep = USBD->EP; ep_index < PERIPH_MAX_EP; ep_index++, xfer++, ep++) + { + if (add) + { + /* take first peripheral endpoint that is unused */ + if (0 == (ep->EPCFG & USBD_EPCFG_EPEN_Msk)) return ep; + } + else + { + /* find a peripheral endpoint that matches ep_addr */ + if (xfer->ep_addr == ep_addr) return ep; + } + } + + return NULL; +} + +/* perform a non-control IN endpoint transfer; this is called by the ISR */ +static void dcd_userEP_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep) +{ + uint16_t bytes_now = tu_min16(xfer->in_remaining_bytes, xfer->max_packet_size); + uint16_t countdown = bytes_now; + + /* precompute what amount of data will be left */ + xfer->in_remaining_bytes -= bytes_now; + + /* + if there will be no more data to send, we replace the BUFEMPTYIF EP interrupt with TXPKIF; + that way, we alert TinyUSB as soon as this last packet has been sent + */ + if (0 == xfer->in_remaining_bytes) + { + ep->EPINTSTS = USBD_EPINTSTS_TXPKIF_Msk; + ep->EPINTEN = USBD_EPINTEN_TXPKIEN_Msk; + } + + /* provided buffers are thankfully 32-bit aligned, allowing most data to be transfered as 32-bit */ + while (countdown > 3) + { + ep->EPDAT = *(uint32_t *)xfer->data_ptr; + xfer->data_ptr += 4; countdown -= 4; + } + while (countdown--) + ep->EPDAT_BYTE = *xfer->data_ptr++; + + /* for short packets, we must nudge the peripheral to say 'that's all folks' */ + if (bytes_now != xfer->max_packet_size) + ep->EPRSPCTL = USBD_EPRSPCTL_SHORTTXEN_Msk; +} + +/* called by dcd_init() as well as by the ISR during a USB bus reset */ +static void bus_reset(void) +{ + for (enum ep_enum ep_index = PERIPH_EPA; ep_index < PERIPH_MAX_EP; ep_index++) + { + USBD->EP[ep_index].EPCFG = 0; + xfer_table[ep_index].dma_requested = false; + } + + USBD->DMACNT = 0; + USBD->DMACTL = USBD_DMACTL_DMARST_Msk; + USBD->DMACTL = 0; + + /* allocate the default EP0 endpoints */ + + USBD->CEPBUFSTART = 0; + USBD->CEPBUFEND = 0 + CFG_TUD_ENDPOINT0_SIZE - 1; + + /* USB RAM beyond what we've allocated above is available to the user */ + bufseg_addr = CFG_TUD_ENDPOINT0_SIZE; + + /* Reset USB device address */ + USBD->FADDR = 0; + + configuration_changed = false; + current_dma_xfer = NULL; +} + +/* this must only be called by the ISR; it does its best to share the single DMA engine across all user EPs (IN and OUT) */ +static void service_dma(void) +{ + if (current_dma_xfer) + return; + + enum ep_enum ep_index; + struct xfer_ctl_t *xfer; + USBD_EP_T *ep; + + for (ep_index = PERIPH_EPA, xfer = &xfer_table[PERIPH_EPA], ep = &USBD->EP[PERIPH_EPA]; ep_index < PERIPH_MAX_EP; ep_index++, xfer++, ep++) + { + uint16_t const available_bytes = ep->EPDATCNT & USBD_EPDATCNT_DATCNT_Msk; + + if (!xfer->dma_requested || !available_bytes) + continue; + + /* + instruct DMA to copy the data from the PC to the previously provided buffer + when the bus interrupt DMADONEIEN subsequently fires, the transfer will have finished + */ + USBD->DMACTL = xfer->ep_addr & USBD_DMACTL_EPNUM_Msk; + USBD->DMAADDR = (uint32_t)xfer->data_ptr; + USBD->DMACNT = available_bytes; + USBD->BUSINTSTS = USBD_BUSINTSTS_DMADONEIF_Msk; + xfer->out_bytes_so_far += available_bytes; + current_dma_xfer = xfer; + USBD->DMACTL |= USBD_DMACTL_DMAEN_Msk; + + return; + } +} + +/* centralized location for USBD interrupt enable bit masks */ +static const uint32_t enabled_irqs = USBD_GINTEN_USBIEN_Msk | \ + USBD_GINTEN_EPAIEN_Msk | USBD_GINTEN_EPBIEN_Msk | USBD_GINTEN_EPCIEN_Msk | USBD_GINTEN_EPDIEN_Msk | USBD_GINTEN_EPEIEN_Msk | USBD_GINTEN_EPFIEN_Msk | \ + USBD_GINTEN_EPGIEN_Msk | USBD_GINTEN_EPHIEN_Msk | USBD_GINTEN_EPIIEN_Msk | USBD_GINTEN_EPJIEN_Msk | USBD_GINTEN_EPKIEN_Msk | USBD_GINTEN_EPLIEN_Msk | \ + USBD_GINTEN_CEPIEN_Msk; + +/* + NUC505 TinyUSB API driver implementation +*/ + +void dcd_init(uint8_t rhport) +{ + (void) rhport; + + /* configure interrupts in their initial state; BUSINTEN and CEPINTEN will be subsequently and dynamically re-written as needed */ + USBD->GINTEN = enabled_irqs; + USBD->BUSINTEN = USBD_BUSINTEN_RSTIEN_Msk | USBD_BUSINTEN_VBUSDETIEN_Msk | USBD_BUSINTEN_RESUMEIEN_Msk | USBD_BUSINTEN_DMADONEIEN_Msk; + USBD->CEPINTEN = 0; + + bus_reset(); + + usb_attach(); +} + +void dcd_int_enable(uint8_t rhport) +{ + (void) rhport; + NVIC_EnableIRQ(USBD_IRQn); +} + +void dcd_int_disable(uint8_t rhport) +{ + (void) rhport; + NVIC_DisableIRQ(USBD_IRQn); +} + +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) +{ + (void) rhport; + usb_control_send_zlp(); /* SET_ADDRESS is the one exception where TinyUSB doesn't use dcd_edpt_xfer() to generate a ZLP */ + assigned_address = dev_addr; +} + +void dcd_set_config(uint8_t rhport, uint8_t config_num) +{ + (void) rhport; + (void) config_num; + configuration_changed = true; +} + +void dcd_remote_wakeup(uint8_t rhport) +{ + (void) rhport; + USBD->OPER |= USBD_OPER_RESUMEEN_Msk; +} + +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) +{ + (void) rhport; + + USBD_EP_T *ep = ep_entry(p_endpoint_desc->bEndpointAddress, true); + TU_ASSERT(ep); + + /* mine the data for the information we need */ + int const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); + int const size = p_endpoint_desc->wMaxPacketSize.size; + tusb_xfer_type_t const type = p_endpoint_desc->bmAttributes.xfer; + struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP]; + + /* allocate buffer from USB RAM */ + ep->EPBUFSTART = bufseg_addr; + bufseg_addr += size; + ep->EPBUFEND = bufseg_addr - 1; + TU_ASSERT(bufseg_addr <= USBD_BUF_SIZE); + + ep->EPMPS = size; + + ep->EPRSPCTL = USB_EP_RSPCTL_FLUSH | eprspctl_eptype_table[type]; + + /* construct USB Configuration Register value and then write it */ + uint32_t cfg = (uint32_t)tu_edpt_number(p_endpoint_desc->bEndpointAddress) << USBD_EPCFG_EPNUM_Pos; + if (TUSB_DIR_IN == dir) + cfg |= USBD_EPCFG_EPDIR_Msk; + cfg |= epcfg_eptype_table[type] | USBD_EPCFG_EPEN_Msk; + ep->EPCFG = cfg; + + /* make a note of the endpoint particulars */ + xfer->max_packet_size = size; + xfer->ep_addr = p_endpoint_desc->bEndpointAddress; + + return true; +} + +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) +{ + (void) rhport; + + if (0x80 == ep_addr) /* control EP0 IN */ + { + if (total_bytes) + { + USBD->CEPCTL = USBD_CEPCTL_FLUSH_Msk; + ctrl_in_xfer.data_ptr = buffer; + ctrl_in_xfer.in_remaining_bytes = total_bytes; + ctrl_in_xfer.total_bytes = total_bytes; + USBD->CEPINTSTS = USBD_CEPINTSTS_INTKIF_Msk; + USBD->CEPINTEN = USBD_CEPINTEN_INTKIEN_Msk; + } + else + { + usb_control_send_zlp(); + } + } + else if (0x00 == ep_addr) /* control EP0 OUT */ + { + if (total_bytes) + { + /* if TinyUSB is asking for EP0 OUT data, it is almost certainly already in the buffer */ + while (total_bytes < USBD->CEPRXCNT); + for (int count = 0; count < total_bytes; count++) + *buffer++ = USBD->CEPDAT_BYTE; + + usb_control_send_zlp(); + } + } + else + { + /* mine the data for the information we need */ + tusb_dir_t dir = tu_edpt_dir(ep_addr); + USBD_EP_T *ep = ep_entry(ep_addr, false); + struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP]; + + /* store away the information we'll needing now and later */ + xfer->data_ptr = buffer; + xfer->in_remaining_bytes = total_bytes; + xfer->total_bytes = total_bytes; + + if (TUSB_DIR_IN == dir) + { + ep->EPINTEN = USBD_EPINTEN_BUFEMPTYIEN_Msk; + } + else + { + xfer->out_bytes_so_far = 0; + ep->EPINTEN = USBD_EPINTEN_RXPKIEN_Msk; + } + } + + return true; +} + +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + + if (tu_edpt_number(ep_addr)) + { + USBD_EP_T *ep = ep_entry(ep_addr, false); + ep->EPRSPCTL = (ep->EPRSPCTL & 0xf7) | USBD_EPRSPCTL_HALT_Msk; + } + else + { + USBD->CEPCTL = USBD_CEPCTL_STALLEN_Msk; + } +} + +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) +{ + (void) rhport; + + if (tu_edpt_number(ep_addr)) + { + USBD_EP_T *ep = ep_entry(ep_addr, false); + ep->EPRSPCTL = USBD_EPRSPCTL_TOGGLE_Msk; + } +} + +void USBD_IRQHandler(void) +{ + uint32_t status = USBD->GINTSTS; + + /* USB interrupt */ + if (status & USBD_GINTSTS_USBIF_Msk) + { + uint32_t bus_state = USBD->BUSINTSTS; + + if (bus_state & USBD_BUSINTSTS_SOFIF_Msk) + { + /* Start-Of-Frame event */ + dcd_event_bus_signal(0, DCD_EVENT_SOF, true); + } + + if (bus_state & USBD_BUSINTSTS_RSTIF_Msk) + { + bus_reset(); + + USBD->CEPINTEN = USBD_CEPINTEN_SETUPPKIEN_Msk; + USBD->BUSINTEN = USBD_BUSINTEN_RSTIEN_Msk | USBD_BUSINTEN_RESUMEIEN_Msk | USBD_BUSINTEN_SUSPENDIEN_Msk | USBD_BUSINTEN_DMADONEIEN_Msk; + USBD->CEPINTSTS = 0x1ffc; + dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true); + } + + if (bus_state & USBD_BUSINTSTS_RESUMEIF_Msk) + { + USBD->BUSINTEN = USBD_BUSINTEN_RSTIEN_Msk | USBD_BUSINTEN_SUSPENDIEN_Msk | USBD_BUSINTEN_DMADONEIEN_Msk; + dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); + } + + if (bus_state & USBD_BUSINTSTS_SUSPENDIF_Msk) + { + USBD->BUSINTEN = USBD_BUSINTEN_RSTIEN_Msk | USBD_BUSINTEN_RESUMEIEN_Msk | USBD_BUSINTEN_DMADONEIEN_Msk; + dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true); + } + + if (bus_state & USBD_BUSINTSTS_HISPDIF_Msk) + { + USBD->CEPINTEN = USBD_CEPINTEN_SETUPPKIEN_Msk; + } + + if (bus_state & USBD_BUSINTSTS_DMADONEIF_Msk) + { + if (current_dma_xfer) + { + current_dma_xfer->dma_requested = false; + + uint16_t available_bytes = USBD->DMACNT & USBD_DMACNT_DMACNT_Msk; + + /* if the most recent DMA finishes the transfer, alert TinyUSB; otherwise, the next RXPKIF/INTKIF endpoint interrupt will prompt the next DMA */ + if ( (current_dma_xfer->total_bytes == current_dma_xfer->out_bytes_so_far) || (available_bytes < current_dma_xfer->max_packet_size) ) + { + dcd_event_xfer_complete(0, current_dma_xfer->ep_addr, current_dma_xfer->out_bytes_so_far, XFER_RESULT_SUCCESS, true); + } + + current_dma_xfer = NULL; + service_dma(); + } + } + + if (bus_state & USBD_BUSINTSTS_VBUSDETIF_Msk) + { + if (USBD->PHYCTL & USBD_PHYCTL_VBUSDET_Msk) + { + /* USB connect */ + USBD->PHYCTL |= USBD_PHYCTL_PHYEN_Msk | USBD_PHYCTL_DPPUEN_Msk; + } + else + { + /* USB disconnect */ + USBD->PHYCTL &= ~USBD_PHYCTL_DPPUEN_Msk; + } + } + + USBD->BUSINTSTS = bus_state & (USBD_BUSINTSTS_SOFIF_Msk | USBD_BUSINTSTS_RSTIF_Msk | USBD_BUSINTSTS_RESUMEIF_Msk | USBD_BUSINTSTS_SUSPENDIF_Msk | USBD_BUSINTSTS_HISPDIF_Msk | USBD_BUSINTSTS_DMADONEIF_Msk | USBD_BUSINTSTS_PHYCLKVLDIF_Msk | USBD_BUSINTSTS_VBUSDETIF_Msk); + } + + if (status & USBD_GINTSTS_CEPIF_Msk) + { + uint32_t cep_state = USBD->CEPINTSTS & USBD->CEPINTEN; + + if (cep_state & USBD_CEPINTSTS_SETUPPKIF_Msk) + { + /* get SETUP packet from USB buffer */ + uint8_t setup_packet[8]; + setup_packet[0] = (uint8_t)(USBD->SETUP1_0 >> 0); + setup_packet[1] = (uint8_t)(USBD->SETUP1_0 >> 8); + setup_packet[2] = (uint8_t)(USBD->SETUP3_2 >> 0); + setup_packet[3] = (uint8_t)(USBD->SETUP3_2 >> 8); + setup_packet[4] = (uint8_t)(USBD->SETUP5_4 >> 0); + setup_packet[5] = (uint8_t)(USBD->SETUP5_4 >> 8); + setup_packet[6] = (uint8_t)(USBD->SETUP7_6 >> 0); + setup_packet[7] = (uint8_t)(USBD->SETUP7_6 >> 8); + dcd_event_setup_received(0, setup_packet, true); + } + else if (cep_state & USBD_CEPINTSTS_INTKIF_Msk) + { + USBD->CEPINTSTS = USBD_CEPINTSTS_TXPKIF_Msk; + + if (!(cep_state & USBD_CEPINTSTS_STSDONEIF_Msk)) + { + USBD->CEPINTEN = USBD_CEPINTEN_TXPKIEN_Msk; + uint16_t bytes_now = tu_min16(ctrl_in_xfer.in_remaining_bytes, CFG_TUD_ENDPOINT0_SIZE); + for (int count = 0; count < bytes_now; count++) + USBD->CEPDAT_BYTE = *ctrl_in_xfer.data_ptr++; + ctrl_in_xfer.in_remaining_bytes -= bytes_now; + USBD_START_CEP_IN(bytes_now); + } + else + { + USBD->CEPINTEN = USBD_CEPINTEN_TXPKIEN_Msk | USBD_CEPINTEN_STSDONEIEN_Msk; + } + } + else if (cep_state & USBD_CEPINTSTS_TXPKIF_Msk) + { + USBD->CEPINTSTS = USBD_CEPINTSTS_STSDONEIF_Msk; + USBD_SET_CEP_STATE(USB_CEPCTL_NAKCLR); + + /* alert TinyUSB that the EP0 IN transfer has finished */ + if ( (0 == ctrl_in_xfer.in_remaining_bytes) || (0 == ctrl_in_xfer.total_bytes) ) + dcd_event_xfer_complete(0, 0x80, ctrl_in_xfer.total_bytes, XFER_RESULT_SUCCESS, true); + + if (ctrl_in_xfer.in_remaining_bytes) + { + USBD->CEPINTSTS = USBD_CEPINTSTS_INTKIF_Msk; + USBD->CEPINTEN = USBD_CEPINTEN_INTKIEN_Msk; + } + else + { + /* TinyUSB does its own fragmentation and ZLP for EP0; a transfer of zero means a ZLP */ + if (0 == ctrl_in_xfer.total_bytes) USBD->CEPCTL = USBD_CEPCTL_ZEROLEN_Msk; + + USBD->CEPINTSTS = USBD_CEPINTSTS_STSDONEIF_Msk; + USBD->CEPINTEN = USBD_CEPINTEN_SETUPPKIEN_Msk | USBD_CEPINTEN_STSDONEIEN_Msk; + } + } + else if (cep_state & USBD_CEPINTSTS_STSDONEIF_Msk) + { + /* given ACK from host has happened, we can now set the address (if not already done) */ + if((USBD->FADDR != assigned_address) && (USBD->FADDR == 0)) USBD->FADDR = assigned_address; + + if (configuration_changed) + { + for (enum ep_enum ep_index = PERIPH_EPA; ep_index < PERIPH_MAX_EP; ep_index++) + { + if (USBD->EP[ep_index].EPCFG & USBD_EPCFG_EPEN_Msk) USBD->EP[ep_index].EPRSPCTL = USBD_EPRSPCTL_TOGGLE_Msk; + } + configuration_changed = false; + } + + USBD->CEPINTEN = USBD_CEPINTEN_SETUPPKIEN_Msk; + } + + USBD->CEPINTSTS = cep_state; + + return; + } + + if (status & (USBD_GINTSTS_EPAIF_Msk | USBD_GINTSTS_EPBIF_Msk | USBD_GINTSTS_EPCIF_Msk | USBD_GINTSTS_EPDIF_Msk | USBD_GINTSTS_EPEIF_Msk | USBD_GINTSTS_EPFIF_Msk | USBD_GINTSTS_EPGIF_Msk | USBD_GINTSTS_EPHIF_Msk | USBD_GINTSTS_EPIIF_Msk | USBD_GINTSTS_EPJIF_Msk | USBD_GINTSTS_EPKIF_Msk | USBD_GINTSTS_EPLIF_Msk)) + { + /* service PERIPH_EPA through PERIPH_EPL */ + enum ep_enum ep_index; + uint32_t mask; + struct xfer_ctl_t *xfer; + USBD_EP_T *ep; + for (ep_index = PERIPH_EPA, mask = USBD_GINTSTS_EPAIF_Msk, xfer = &xfer_table[PERIPH_EPA], ep = &USBD->EP[PERIPH_EPA]; ep_index < PERIPH_MAX_EP; ep_index++, mask <<= 1, xfer++, ep++) + { + if(status & mask) + { + uint8_t const ep_addr = xfer->ep_addr; + bool const out_ep = !(ep_addr & TUSB_DIR_IN_MASK); + uint32_t ep_state = ep->EPINTSTS & ep->EPINTEN; + + if (out_ep) + { +#if 1 + xfer->dma_requested = true; + service_dma(); +#else + uint16_t const available_bytes = ep->EPDATCNT & USBD_EPDATCNT_DATCNT_Msk; + /* copy the data from the PC to the previously provided buffer */ + for (int count = 0; (count < available_bytes) && (xfer->out_bytes_so_far < xfer->total_bytes); count++, xfer->out_bytes_so_far++) + *xfer->data_ptr++ = ep->EPDAT_BYTE; + + /* when the transfer is finished, alert TinyUSB; otherwise, continue accepting more data */ + if ( (xfer->total_bytes == xfer->out_bytes_so_far) || (available_bytes < xfer->max_packet_size) ) + dcd_event_xfer_complete(0, ep_addr, xfer->out_bytes_so_far, XFER_RESULT_SUCCESS, true); +#endif + + } + else if (ep_state & USBD_EPINTSTS_BUFEMPTYIF_Msk) + { + /* send any remaining data */ + dcd_userEP_in_xfer(xfer, ep); + } + else if (ep_state & USBD_EPINTSTS_TXPKIF_Msk) + { + /* alert TinyUSB that we've finished */ + dcd_event_xfer_complete(0, ep_addr, xfer->total_bytes, XFER_RESULT_SUCCESS, true); + ep->EPINTEN = 0; + } + + ep->EPINTSTS = ep_state; + } + } + } +} + +void dcd_isr(uint8_t rhport) +{ + (void) rhport; + USBD_IRQHandler(); +} + +#endif diff --git a/src/tusb_option.h b/src/tusb_option.h index d39dd1046..e0aa7a627 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -79,6 +79,7 @@ #define OPT_MCU_NUC121 800 #define OPT_MCU_NUC126 801 +#define OPT_MCU_NUC505 803 /** @} */ -- 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/portable') 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 From e0cdab5bf7ab6c2f085a748f936afafbd94ca0d9 Mon Sep 17 00:00:00 2001 From: hathach Date: Sun, 8 Mar 2020 16:28:21 +0700 Subject: fix stm32 fsdev epdesc --- src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c | 30 +++++++++++++-------------- 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'src/portable') diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c index a1f40d971..5eacbe655 100644 --- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c @@ -323,29 +323,27 @@ void dcd_remote_wakeup(uint8_t rhport) remoteWakeCountdown = 4u; // required to be 1 to 15 ms, ESOF should trigger every 1ms. } -// I'm getting a weird warning about missing braces here that I don't -// know how to fix. -#if defined(__GNUC__) && (__GNUC__ >= 7) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wmissing-braces" -#endif static const tusb_desc_endpoint_t ep0OUT_desc = { - .wMaxPacketSize = CFG_TUD_ENDPOINT0_SIZE, - .bDescriptorType = TUSB_XFER_CONTROL, - .bEndpointAddress = 0x00 + .bLength = sizeof(tusb_desc_endpoint_t), + .bDescriptorType = TUSB_DESC_ENDPOINT, + + .bEndpointAddress = 0x00, + .bmAttributes = { .xfer = TUSB_XFER_CONTROL }, + .wMaxPacketSize = { .size = CFG_TUD_ENDPOINT0_SIZE }, + .bInterval = 0 }; static const tusb_desc_endpoint_t ep0IN_desc = { - .wMaxPacketSize = CFG_TUD_ENDPOINT0_SIZE, - .bDescriptorType = TUSB_XFER_CONTROL, - .bEndpointAddress = 0x80 -}; + .bLength = sizeof(tusb_desc_endpoint_t), + .bDescriptorType = TUSB_DESC_ENDPOINT, -#if defined(__GNUC__) && (__GNUC__ >= 7) -#pragma GCC diagnostic pop -#endif + .bEndpointAddress = 0x80, + .bmAttributes = { .xfer = TUSB_XFER_CONTROL }, + .wMaxPacketSize = { .size = CFG_TUD_ENDPOINT0_SIZE }, + .bInterval = 0 +}; static void dcd_handle_bus_reset(void) { -- cgit v1.3.1 From fb56c02a6f7cc03b2f9fd970a5b1bc772ccf4795 Mon Sep 17 00:00:00 2001 From: Peter Lawrence <12226419+majbthrd@users.noreply.github.com> Date: Sun, 15 Mar 2020 17:10:14 -0500 Subject: NUC505 : fix operation with net_lwip_webserver --- examples/device/net_lwip_webserver/src/tusb_config.h | 2 +- src/portable/nuvoton/nuc505/dcd_nuc505.c | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'src/portable') diff --git a/examples/device/net_lwip_webserver/src/tusb_config.h b/examples/device/net_lwip_webserver/src/tusb_config.h index 329fc80b9..0b40fefab 100644 --- a/examples/device/net_lwip_webserver/src/tusb_config.h +++ b/examples/device/net_lwip_webserver/src/tusb_config.h @@ -39,7 +39,7 @@ #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 +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || CFG_TUSB_MCU == OPT_MCU_NUC505 #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) #else #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE diff --git a/src/portable/nuvoton/nuc505/dcd_nuc505.c b/src/portable/nuvoton/nuc505/dcd_nuc505.c index 2b6a66bc2..031bfc5e4 100644 --- a/src/portable/nuvoton/nuc505/dcd_nuc505.c +++ b/src/portable/nuvoton/nuc505/dcd_nuc505.c @@ -211,6 +211,7 @@ static void bus_reset(void) current_dma_xfer = NULL; } +#if 0 /* this must only be called by the ISR; it does its best to share the single DMA engine across all user EPs (IN and OUT) */ static void service_dma(void) { @@ -243,6 +244,7 @@ static void service_dma(void) return; } } +#endif /* centralized location for USBD interrupt enable bit masks */ static const uint32_t enabled_irqs = USBD_GINTEN_USBIEN_Msk | \ @@ -366,7 +368,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to for (int count = 0; count < total_bytes; count++) *buffer++ = USBD->CEPDAT_BYTE; - usb_control_send_zlp(); + dcd_event_xfer_complete(0, ep_addr, total_bytes, XFER_RESULT_SUCCESS, true); } } else @@ -465,6 +467,7 @@ void USBD_IRQHandler(void) if (bus_state & USBD_BUSINTSTS_DMADONEIF_Msk) { +#if 0 if (current_dma_xfer) { current_dma_xfer->dma_requested = false; @@ -480,6 +483,7 @@ void USBD_IRQHandler(void) current_dma_xfer = NULL; service_dma(); } +#endif } if (bus_state & USBD_BUSINTSTS_VBUSDETIF_Msk) @@ -597,7 +601,7 @@ void USBD_IRQHandler(void) if (out_ep) { -#if 1 +#if 0 xfer->dma_requested = true; service_dma(); #else -- cgit v1.3.1 From df05440d92f0d05532476df5f544d00751f03bce Mon Sep 17 00:00:00 2001 From: Peter Lawrence <12226419+majbthrd@users.noreply.github.com> Date: Mon, 16 Mar 2020 13:20:17 -0500 Subject: NUC505 : added comment on DMA choice --- src/portable/nuvoton/nuc505/dcd_nuc505.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src/portable') diff --git a/src/portable/nuvoton/nuc505/dcd_nuc505.c b/src/portable/nuvoton/nuc505/dcd_nuc505.c index 031bfc5e4..e05e73eff 100644 --- a/src/portable/nuvoton/nuc505/dcd_nuc505.c +++ b/src/portable/nuvoton/nuc505/dcd_nuc505.c @@ -33,6 +33,12 @@ nomenclature of EPA through EPL. */ +/* + Note on OPT_MCU_NUC505_USB_DMA: the author suggests against using this option. + The DMA functionality of the USBD peripheral does not appear to succeed with + transfer lengths that are longer (> 64 bytes) and are not a multiple of 4. +*/ + #include "tusb_option.h" #if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_NUC505) @@ -211,7 +217,7 @@ static void bus_reset(void) current_dma_xfer = NULL; } -#if 0 +#ifdef OPT_MCU_NUC505_USB_DMA /* this must only be called by the ISR; it does its best to share the single DMA engine across all user EPs (IN and OUT) */ static void service_dma(void) { @@ -467,7 +473,7 @@ void USBD_IRQHandler(void) if (bus_state & USBD_BUSINTSTS_DMADONEIF_Msk) { -#if 0 +#ifdef OPT_MCU_NUC505_USB_DMA if (current_dma_xfer) { current_dma_xfer->dma_requested = false; @@ -601,7 +607,7 @@ void USBD_IRQHandler(void) if (out_ep) { -#if 0 +#ifdef OPT_MCU_NUC505_USB_DMA xfer->dma_requested = true; service_dma(); #else -- cgit v1.3.1