Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * The MIT License (MIT)
- *
- * Copyright (c) gronnmann
- * Copyright(c) N Conrad
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. Neither the name of STMicroelectronics nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * This file is part of the TinyUSB stack.
- */
- #include "tusb_option.h"
- #if CFG_TUH_ENABLED && defined(TUP_USBIP_FSDEV)
- #include "host/hcd.h"
- #if defined(TUP_USBIP_FSDEV_STM32)
- #elif defined(TUP_USBIP_FSDEV_CH32)
- #include "fsdev_ch32.h"
- #elif defined(TUP_USBIP_FSDEV_AT32)
- #include "fsdev_at32.h"
- #else
- #error "Unknown USB IP"
- #endif
- #endif
- #include <stdint.h>
- #include <stdbool.h>
- #include "tusb.h"
- #include "stm32_helpers.h"
- UsbPmaAllocator pma_allocator;
- // EP allocator
- typedef struct {
- bool valid; // is this entry valid
- uint8_t devaddr; // assigned device
- uint8_t epnum; // Assigned ep number
- uint8_t chep_num;
- uint8_t chep_type;
- bool allocated[2]; // [0] = OUT, [1] = IN
- bool transferring[2]; // [0] = OUT, [1] = IN
- bool in_setup[2];
- // Current PMA allocation size for this CHEP
- uint16_t pma_allocated[2];
- // Current PMA offset for this CHEP
- uint16_t pma_offset[2];
- uint8_t *buffer_ptr[2]; // tusb buffer pointer
- // When transferring, we want to copy the data into the buffer, as
- // the success event only takes in the length
- FsdevChepType control_type[2];
- } chep_alloc_t;
- static chep_alloc_t chep_alloc_status[FSDEV_EP_COUNT];
- void chear_chep_alloc_status(chep_alloc_t *chep) {
- chep->valid = false;
- chep->allocated[0] = false;
- chep->allocated[1] = false;
- chep->chep_num = 0xFF;
- chep->chep_type = 0xFF;
- chep->devaddr = 0;
- chep->epnum = 0;
- chep->pma_allocated[0] = 0;
- chep->pma_offset[0] = 0;
- chep->pma_allocated[1] = 0;
- chep->pma_offset[1] = 0;
- chep->transferring[0] = false;
- chep->transferring[1] = false;
- chep->in_setup[0] = false;
- chep->in_setup[1] = false;
- chep->buffer_ptr[0] = NULL;
- chep->buffer_ptr[1] = NULL;
- }
- /**
- * Finds the CHEP index for given epnum and devaddr
- * @param epnum the endpoint number
- * @param devaddr the device address
- * @return the CHEP index, or 0xFF if not found
- */
- uint8_t find_chep(uint8_t epnum, uint8_t devaddr) {
- for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
- if ((chep_alloc_status[i].epnum == epnum) &&
- (chep_alloc_status[i].devaddr == devaddr) && chep_alloc_status[i].valid) {
- return i;
- }
- }
- return 0xFF;
- }
- uint8_t next_free_chep(void) {
- for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
- if (!chep_alloc_status[i].valid) {
- return i;
- }
- }
- return 0xFF;
- }
- bool hcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init) {
- (void) rhport;
- (void) rh_init;
- // First -> reset whole register
- fsdev_cntr_reset();
- fsdev_istr_reset();
- // 1) Select host mode but don't clobber CNTR
- USB_REG->CNTR |= USB_CNTR_HOST;
- // 2) Power up analog
- USB_REG->CNTR &= ~USB_CNTR_PDWN;
- HAL_Delay(10);
- // 3) Release reset
- USB_REG->CNTR &= ~USB_CNTR_USBRST;
- HAL_Delay(10);
- // 4) Clear/initialize BTABLE before use
- for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
- USB_SRAM->btable[i].txrx = 0;
- USB_SRAM->btable[i].rxtx = 0;
- }
- // 5) Enable interrupt masks
- USB_REG->CNTR |= (USB_CNTR_CTRM | USB_CNTR_ERRM | USB_CNTR_RESETM |
- USB_CNTR_DCON | USB_CNTR_SOFM);
- usb_pma_allocator_init(&pma_allocator);
- // clear allocators
- for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
- chear_chep_alloc_status(&chep_alloc_status[i]);
- }
- printf("Finished initialization of USB HOST MODE\r\n");
- fsdev_usb_print_cntr(USB_REG->CNTR);
- fsdev_usb_print_istr(USB_REG->ISTR);
- fsdev_usb_print_pma(&pma_allocator);
- for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
- fsdev_usb_print_chep(USB_REG->CHEPnR[i].reg, i);
- }
- fsdev_usb_print_btable_at(USB_SRAM);
- return true;
- }
- void hcd_int_enable(uint8_t rhport) {
- // fsdev_usb_set_global_interrupt(true);
- USB_REG->CNTR |= (USB_CNTR_CTRM | USB_CNTR_ERRM | USB_CNTR_RESETM |
- USB_CNTR_DCON | USB_CNTR_SOFM);
- }
- void hcd_int_disable(uint8_t rhport) {
- // fsdev_usb_set_global_interrupt(false);
- USB_REG->CNTR &= ~(USB_CNTR_CTRM | USB_CNTR_ERRM | USB_CNTR_RESETM |
- USB_CNTR_DCON | USB_CNTR_SOFM);
- }
- bool hcd_port_connect_status(uint8_t rhport) {
- return (USB_REG->ISTR & USB_ISTR_DCON_STAT) != 0; // device connected
- }
- void hcd_port_reset(uint8_t rhport) {
- // printf("[hcd_port_reset] asserting reset line\r\n");
- USB_REG->CNTR |= USB_CNTR_USBRST; // assert reset line
- }
- void hcd_port_reset_end(uint8_t rhport) {
- // printf("[hcd_port_reset_end] deasserting reset line\r\n");
- USB_REG->CNTR &= ~USB_CNTR_USBRST; // de-assert reset line
- }
- tusb_speed_t hcd_port_speed_get(uint8_t rhport) {
- return (USB_REG->ISTR & USB_ISTR_LS_DCONN) ? TUSB_SPEED_LOW : TUSB_SPEED_FULL;
- }
- /**
- * Enqueues a OUT packet to be sent to the device.
- *
- * @return
- */
- /**
- * Enqueues a OUT/IN packet to be sent to/from the device.
- *
- * @return true if packet was successfully queued
- */
- bool _enqueue_packet(uint8_t daddr, uint8_t ep_num, uint8_t dir, uint8_t *buffer, uint16_t buflen, bool setup) {
- // printf("[_enqueue_packet] Got packet request: daddr=%u, ep_num=%u, dir=%u, buffer=%p, buflen=%u, setup=%u\r\n",
- // daddr, ep_num, dir, buffer, buflen, setup);
- uint8_t chep_num = find_chep(ep_num, daddr);
- if (chep_num == 0xFF) {
- printf("[_enqueue_packet] CHEP for EP %u dev %u not found\r\n", ep_num, daddr);
- return false;
- }
- uint32_t chep_val = USB_REG->CHEPnR[chep_num].reg; // read, then write back
- chep_alloc_t *chep = &chep_alloc_status[chep_num];
- if (!chep->allocated[dir]) {
- printf("[_enqueue_packet] Endpoint %u for device %u dir %s not opened\r\n",
- ep_num, daddr, dir ? "IN" : "OUT");
- return false;
- }
- if (chep->transferring[dir]) {
- printf("[_enqueue_packet] Endpoint %u for device %u dir %s is already transferring\r\n",
- ep_num, daddr, dir ? "IN" : "OUT");
- return false;
- }
- if (buflen > chep->pma_allocated[dir]) {
- printf("[_enqueue_packet] Transfer size %u exceeds allocated PMA size %u\r\n",
- buflen, chep->pma_allocated[dir]);
- return false;
- }
- FsdevChepStatus status;
- if (dir == TUSB_DIR_OUT) {
- status = fsdev_chep_get_tx_status(chep_val);
- } else {
- status = fsdev_chep_get_rx_status(chep_val);
- }
- if (status == FSDEV_CHEP_STATUS_VALID) {
- printf("[_enqueue_packet] Endpoint %u for device %u dir %s is still VALID, cannot send new package yet \r\n",
- ep_num, daddr, dir ? "IN" : "OUT");
- return false;
- }
- // print chep before transfer
- // printf("Before transfer: \r\n");
- // fsdev_usb_print_btable_entry(tx_val, true);
- // fsdev_usb_print_chep(chep_val, chep_num);
- // ensure devaddr is always corerct
- fsdev_chep_set_devaddr(&chep_val, daddr);
- fsdev_chep_write_rw(&chep_val, USB_CHEP_ADDR, USB_CHEP_ADDR_Pos, ep_num);
- fsdev_chep_set_type(&chep_val, chep->chep_type);
- if (dir == TUSB_DIR_OUT) {
- // Out (host -> device)
- if (setup) {
- fsdev_chep_write_t_field(&chep_val, USB_CHEP_SETUP, USB_CHEP_SETUP_Pos, 1u);
- // set EPKIND = 1
- fsdev_chep_write_rw(&chep_val, USB_CHEP_KIND, USB_CHEP_KIND_Pos, 1u);
- }
- fsdev_bd_tx(&USB_SRAM->btable[chep_num].txrx, chep->pma_offset[dir], buflen);
- // fsdev_usb_print_btable_entry(USB_SRAM->btable[chep_num].txrx, true);
- if (buflen > 0) {
- fsdev_pma_write(chep->pma_offset[dir], buffer, buflen);
- }
- // Write RW fields to register FIRST
- USB_REG->CHEPnR[chep_num].reg = chep_val;
- // NOW set TX status on actual register
- fsdev_chep_set_tx_status(&USB_REG->CHEPnR[chep_num].reg, FSDEV_CHEP_STATUS_VALID);
- } else {
- // IN (device -> host)
- fsdev_bd_tx(&USB_SRAM->btable[chep_num].txrx, chep->pma_offset[dir], chep->pma_allocated[dir]);
- // Write RW fields to register FIRST
- USB_REG->CHEPnR[chep_num].reg = chep_val;
- // NOW set RX status on actual register
- fsdev_chep_set_rx_status(&USB_REG->CHEPnR[chep_num].reg, FSDEV_CHEP_STATUS_VALID);
- }
- chep->transferring[dir] = true;
- chep->in_setup[dir] = setup;
- chep->buffer_ptr[dir] = buffer;
- // printf("After transfer: \r\n");
- // fsdev_usb_print_btable_entry(USB_SRAM->btable[chep_num].txrx, true);
- // fsdev_usb_print_chep(USB_REG->CHEPnR[chep_num].reg, chep_num);
- // printf("Successfully queued transfer on EP %u dir %s for device %u\r\n",
- // ep_num, dir ? "IN" : "OUT", daddr);
- return true;
- }
- bool hcd_setup_send(uint8_t rhport, uint8_t daddr, uint8_t const setup_packet[8]) {
- // printf("[hcd_setup_send] rhport=%u, daddr=%u\r\n", rhport, daddr);
- (void) rhport;
- // Channel/endpoint 0 register
- uint8_t chep_num = find_chep(0, daddr);
- // lets find the first free CHEP if not found
- if (chep_num == 0xFF) {
- printf("[hcd_setup_send] CHEP for EP0 dev %u not found... \r\n", daddr);
- return false;
- }
- return _enqueue_packet(daddr, 0, TUSB_DIR_OUT, (uint8_t *) setup_packet, 8, true);
- }
- /**
- * Handle interrupt indicating connect/disconnect event
- */
- static inline void _handle_connect_change(uint8_t rhport, bool in_isr) {
- volatile uint32_t *reg = &USB_REG->ISTR;
- const bool connected = (*reg & USB_ISTR_DCON_STAT) != 0;
- if (connected) {
- hcd_event_device_attach(rhport, in_isr);
- } else {
- hcd_event_device_remove(rhport, in_isr);
- }
- fsdev_istr_clear_irq(USB_ISTR_DCON | USB_ISTR_RESET);
- }
- /**
- * Handle interrupt indicating SOF (Start Of Frame) event
- */
- static inline void _handle_sof() {
- volatile uint32_t *reg = &USB_REG->ISTR;
- // printf("[_handle_sof] USB ISTR=0x%08lx. Clearing SOF flag.\r\n", *reg);
- fsdev_istr_clear_irq(USB_ISTR_SOF);
- }
- /**
- * Handle interrupt indicating error event
- */
- static inline void _handle_error(void) {
- volatile uint32_t *reg = &USB_REG->ISTR;
- // printf("[_handle_error] USB ISTR=0x%08lx. Clearing error flags.\r\n", *reg);
- fsdev_istr_clear_irq(USB_ISTR_ERR);
- }
- static inline void _handle_correct_transfer(uint8_t rhport, bool in_isr) {
- volatile uint32_t *istr = &USB_REG->ISTR;
- // printf("[_handle_correct_transfer] USB ISTR=0x%08lx\r\n", *istr);
- uint8_t dir = (*istr & USB_ISTR_DIR) >> USB_ISTR_DIR_Pos;
- uint8_t chep_idx = fsdev_istr_read_idn(istr);
- uint32_t chep_val = USB_REG->CHEPnR[chep_idx].reg;
- chep_alloc_t *alloc = &chep_alloc_status[chep_idx];
- // In host mode, ALWAYS use TX descriptor for count (hardware quirk for both directions)
- uint32_t btable_val = USB_SRAM->btable[chep_idx].txrx;
- // printf("[_handle_correct_transfer] Received CTR on CHEP %u (EP %u for device %u) dir %s\r\n",
- // chep_idx, alloc->epnum, alloc->devaddr, dir ? "IN" : "OUT");
- // fsdev_usb_print_chep(chep_val, chep_idx);
- // fsdev_usb_print_btable_entry(btable_val, true);
- if (!alloc->valid) {
- printf("[_handle_correct_transfer] Weird CTR received. CHEP %u not allocated\r\n", chep_idx);
- fsdev_istr_clear_irq(USB_ISTR_CTR);
- return;
- }
- if (!alloc->transferring[dir]) {
- printf("[_handle_correct_transfer] Weird CTR received. CHEP %u not transferring in dir %u\r\n",
- chep_idx, dir);
- fsdev_istr_clear_irq(USB_ISTR_CTR);
- return;
- }
- uint16_t xferred_bytes = 0;
- xfer_result_t result = XFER_RESULT_FAILED;
- bool nak = false;
- // Check for error conditions first
- if (chep_val & (USB_CHEP_ERRRX | USB_CHEP_ERRTX)) {
- // printf("[_handle_correct_transfer] Transfer error detected\r\n");
- result = XFER_RESULT_FAILED;
- // Clear error flags using helper
- fsdev_chep_clear_rcw0(&chep_val, USB_CHEP_ERRRX | USB_CHEP_ERRTX);
- USB_REG->CHEPnR[chep_idx].reg = chep_val;
- // Check if we have 3 consecutive errors
- if (chep_val & (USB_CHEP_THREE_ERR_RX | USB_CHEP_THREE_ERR_TX)) {
- const char *err_type = "unknown";
- uint32_t err_code = 0;
- if (chep_val & USB_CHEP_THREE_ERR_RX) {
- err_code = (chep_val & USB_CHEP_THREE_ERR_RX) >> USB_CHEP_THREE_ERR_RX_Pos;
- switch (err_code) {
- case 0x1: err_type = "timeout";
- break;
- case 0x2: err_type = "data error (CRC)";
- break;
- case 0x3: err_type = "protocol error";
- break;
- }
- // printf("[_handle_correct_transfer] Three consecutive IN errors detected: %s (code %lu)\r\n",
- // err_type, err_code);
- } else {
- err_code = (chep_val & USB_CHEP_THREE_ERR_TX) >> USB_CHEP_THREE_ERR_TX_Pos;
- switch (err_code) {
- case 0x1: err_type = "timeout";
- break;
- case 0x2: err_type = "data error (CRC)";
- break;
- case 0x3: err_type = "protocol error";
- break;
- }
- // printf("[_handle_correct_transfer] Three consecutive OUT errors detected: %s (code %lu)\r\n",
- // err_type, err_code);
- }
- // Clear three error flags
- fsdev_chep_clear_rcw0(&chep_val, USB_CHEP_THREE_ERR_RX | USB_CHEP_THREE_ERR_TX);
- USB_REG->CHEPnR[chep_idx].reg = chep_val;
- }
- }
- // Check for NAK condition
- else if (chep_val & USB_CHEP_NAK) {
- printf("[_handle_correct_transfer] Got NAK - retrying\r\n");
- // Clear NAK flag
- fsdev_chep_clear_rcw0(&chep_val, USB_CHEP_NAK);
- USB_REG->CHEPnR[chep_idx].reg = chep_val;
- // Re-enable channel to retry - don't complete transfer
- if (dir == TUSB_DIR_OUT) {
- fsdev_chep_set_tx_status(&USB_REG->CHEPnR[chep_idx].reg, FSDEV_CHEP_STATUS_VALID);
- } else {
- fsdev_chep_set_rx_status(&USB_REG->CHEPnR[chep_idx].reg, FSDEV_CHEP_STATUS_VALID);
- }
- return;
- }
- // Check for STALL condition
- else if ((dir && (fsdev_chep_get_rx_status(chep_val) == FSDEV_CHEP_STATUS_STALL)) ||
- (!dir && (fsdev_chep_get_tx_status(chep_val) == FSDEV_CHEP_STATUS_STALL))) {
- printf("[_handle_correct_transfer] Got STALL\r\n");
- result = XFER_RESULT_STALLED;
- } else {
- // Get actual transferred bytes from COUNTn_TX
- xferred_bytes = fsdev_bd_tx_get_count_ptr(&USB_SRAM->btable[chep_idx].txrx);
- // For IN transfers, copy data from USB SRAM to buffer
- if (dir && alloc->buffer_ptr[dir] && xferred_bytes > 0) {
- uint16_t pma_addr = fsdev_bd_tx_get_addr_ptr(&USB_SRAM->btable[chep_idx].txrx);
- fsdev_pma_read(alloc->buffer_ptr[dir], pma_addr, xferred_bytes);
- }
- result = XFER_RESULT_SUCCESS;
- printf("[_handle_correct_transfer] Transfer successful, %u bytes\r\n", xferred_bytes);
- }
- // Complete the transfer - Clear the VTRX or VTTX bit
- uint32_t chep_clear = USB_REG->CHEPnR[chep_idx].reg;
- if (dir == TUSB_DIR_OUT) {
- fsdev_chep_clear_rcw0(&chep_clear, USB_CHEP_VTTX);
- } else {
- fsdev_chep_clear_rcw0(&chep_clear, USB_CHEP_VTRX);
- }
- USB_REG->CHEPnR[chep_idx].reg = chep_clear;
- fsdev_istr_clear_irq(USB_ISTR_CTR);
- // Reset state
- alloc->transferring[dir] = false;
- alloc->in_setup[dir] = false;
- // Notify TinyUSB - data already in buffer from above
- // printf("[_handle_correct_transfer] Completing: dev %u EP %u dir %s, result %u, bytes %u\r\n",
- // alloc->devaddr, alloc->epnum, dir ? "IN" : "OUT", result, xferred_bytes);
- hcd_event_xfer_complete(alloc->devaddr,
- tu_edpt_addr(alloc->epnum, dir),
- result, xferred_bytes, in_isr);
- }
- void hcd_int_handler(uint8_t rhport, bool in_isr) {
- volatile uint32_t *reg = &USB_REG->ISTR;
- // printf("interrupt\r\n");
- uint8_t dir = *reg & USB_ISTR_DIR;
- if (*reg & USB_ISTR_ERR) {
- _handle_error();
- }
- if (*reg & USB_ISTR_CTR) {
- _handle_correct_transfer(rhport, in_isr);
- }
- if (*reg & (USB_ISTR_DCON | USB_ISTR_RESET)) {
- _handle_connect_change(rhport, in_isr);
- }
- if (*reg & USB_ISTR_SOF) {
- _handle_sof();
- }
- if (*reg & USB_ISTR_PMAOVR) {
- // printf("PMA overran, clearing \r\n");
- fsdev_istr_clear_irq(USB_ISTR_PMAOVR);
- }
- // print afterwards to ensure everything is correctly cleared
- // fsdev_usb_print_istr(*reg);
- }
- void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
- // printf("Got close request for device %u\r\n", dev_addr);
- (void) rhport;
- // find all ChEP allocated to this device, and close all of them
- for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
- if (chep_alloc_status[i].valid && (chep_alloc_status[i].devaddr == dev_addr)) {
- // printf("[hcd_device_close] Closing CHEP %u for device %u\r\n", i, dev_addr);
- // TODO - free PMA
- chear_chep_alloc_status(&chep_alloc_status[i]);
- // Now we need to clear the actual CHEP registers
- volatile uint32_t *chep_reg = &USB_REG->CHEPnR[i].reg;
- fsdev_chep_set_tx_status(chep_reg, FSDEV_CHEP_STATUS_DISABLED);
- fsdev_chep_set_rx_status(chep_reg, FSDEV_CHEP_STATUS_DISABLED);
- }
- }
- }
- /**
- * Queue a transfer on a given pipe
- */
- bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen) {
- uint8_t ep_num = tu_edpt_number(ep_addr);
- uint8_t dir = tu_edpt_dir(ep_addr); // TUSB_DIR_OUT = 0, TUSB_DIR_IN = 1
- // printf("[hcd_edpt_xfer] Got xfer request. ep_num: %u, daddr: %u, dir: %s, len: %u\r\n",
- // ep_num, daddr, dir ? "IN" : "OUT", buflen);
- return _enqueue_packet(daddr, ep_num, dir, buffer, buflen, false);
- }
- /**
- * Stop a running transfer on a given pipe.
- */
- bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
- uint8_t ep_num = tu_edpt_number(ep_addr);
- uint8_t dir = tu_edpt_dir(ep_addr);
- uint8_t chep_num = find_chep(ep_num, dev_addr);
- if (chep_num == 0xFF) {
- printf("[hcd_edpt_abort_xfer] Tried aborting transfer, but CHEP for EP %u dev %u not found... \r\n", ep_num,
- dev_addr);
- return false;
- }
- volatile uint32_t *const reg = &USB_REG->CHEPnR[chep_num].reg;
- chep_alloc_t *chep = &chep_alloc_status[chep_num];
- if (!chep->allocated[dir]) {
- printf("[hcd_edpt_abort_xfer] Endpoint %u for device %u dir %s not opened\r\n",
- ep_num, dev_addr, dir ? "IN" : "OUT");
- return false;
- }
- if (!chep->transferring[dir]) {
- printf("[hcd_edpt_abort_xfer] Endpoint %u for device %u dir %s is not transferring\r\n",
- ep_num, dev_addr, dir ? "IN" : "OUT");
- return false;
- }
- // set disabled
- if (dir == TUSB_DIR_OUT) {
- fsdev_chep_set_tx_status(reg, FSDEV_CHEP_STATUS_DISABLED);
- } else {
- fsdev_chep_set_rx_status(reg, FSDEV_CHEP_STATUS_DISABLED);
- }
- return true;
- }
- /**
- * Create a pipe for an endpoint
- * This means picking channel, programming CHEPnR, allocate PMA, bind BTABLE etc.
- */
- bool hcd_edpt_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const *ep_desc) {
- (void) rhport;
- uint8_t ep_num = tu_edpt_number(ep_desc->bEndpointAddress);
- uint8_t dir = tu_edpt_dir(ep_desc->bEndpointAddress);
- uint16_t pma_size = tu_edpt_packet_size(ep_desc);
- if (ep_num == 0 && pma_size < 64) {
- pma_size = 64; // control endpoint minimum size for full speed
- }
- // Find or allocate CHEP for this device endpoint
- uint8_t chep_idx = find_chep(ep_num, daddr);
- if (chep_idx == 0xFF) {
- chep_idx = next_free_chep();
- if (chep_idx == 0xFF) return false;
- }
- chep_alloc_t *chep = &chep_alloc_status[chep_idx];
- volatile uint32_t *chep_reg = &USB_REG->CHEPnR[chep_idx].reg;
- // Write to local copy, then write to actual CHeP
- uint32_t chep_to_write = *chep_reg;
- // Allocate PMA buffer for this direction
- uint16_t pma_off = usb_pma_alloc(&pma_allocator, pma_size);
- if (pma_off == 0xFFFFu) return false;
- // 1) Program channel static config: type, address, endpoint
- FsdevChepType ep_type;
- switch (ep_desc->bmAttributes.xfer) {
- case TUSB_XFER_CONTROL: ep_type = FSDEV_CHEP_TYPE_CONTROL;
- break;
- case TUSB_XFER_BULK: ep_type = FSDEV_CHEP_TYPE_BULK;
- break;
- case TUSB_XFER_INTERRUPT: ep_type = FSDEV_CHEP_TYPE_INTERRUPT;
- break;
- default: ep_type = FSDEV_CHEP_TYPE_BULK;
- break;
- }
- fsdev_chep_set_type(&chep_to_write, ep_type);
- fsdev_chep_set_devaddr(&chep_to_write, daddr);
- fsdev_chep_write_rw(&chep_to_write, USB_CHEP_ADDR, USB_CHEP_ADDR_Pos, ep_num);
- // 2) Disable both directions before touching BD/flags (host-mode best practice)
- fsdev_chep_set_tx_status(&chep_to_write, FSDEV_CHEP_STATUS_DISABLED);
- fsdev_chep_set_rx_status(&chep_to_write, FSDEV_CHEP_STATUS_DISABLED);
- // 3) Clear stale events/errors on this channel
- uint32_t clear_mask = USB_CHEP_RC_W0_MASK;
- fsdev_chep_clear_rcw0(&chep_to_write, clear_mask);
- fsdev_chep_write_t_field(&chep_to_write, USB_CHEP_DTOG_TX, USB_CHEP_DTOG_TX_Pos, 0);
- fsdev_chep_write_t_field(&chep_to_write, USB_CHEP_DTOG_RX, USB_CHEP_DTOG_RX_Pos, 0);
- // Write back to actual CHEP register
- *chep_reg = chep_to_write;
- // 4) Bind BD TX to PMA address and clear count (host uses TX BD for both directions)
- fsdev_bd_tx(&USB_SRAM->btable[chep_idx].txrx, pma_off, 0);
- // Save allocation bookkeeping
- chep->valid = true;
- chep->devaddr = daddr;
- chep->epnum = ep_num;
- chep->chep_num = chep_idx;
- chep->chep_type = ep_type;
- if (ep_num == 0) {
- // Control endpoint needs separate PMA buffers for IN and OUT
- uint16_t pma_off_in = usb_pma_alloc(&pma_allocator, pma_size);
- uint16_t pma_off_out = usb_pma_alloc(&pma_allocator, pma_size);
- if (pma_off_in == 0xFFFFu || pma_off_out == 0xFFFFu) {
- // Free allocated PMA if one failed
- if (pma_off_in != 0xFFFFu) return false;
- if (pma_off_out != 0xFFFFu) return false;
- return false;
- }
- chep->allocated[TUSB_DIR_IN] = true;
- chep->pma_offset[TUSB_DIR_IN] = pma_off_in;
- chep->pma_allocated[TUSB_DIR_IN] = pma_size;
- chep->allocated[TUSB_DIR_OUT] = true;
- chep->pma_offset[TUSB_DIR_OUT] = pma_off_out; // Different PMA!
- chep->pma_allocated[TUSB_DIR_OUT] = pma_size;
- } else {
- // Non-control endpoints - mark the opened direction
- chep->allocated[dir] = true;
- chep->pma_offset[dir] = pma_off;
- chep->pma_allocated[dir] = pma_size;
- chep->control_type[dir] = ep_type;
- fsdev_chep_set_tx_status(chep_reg, FSDEV_CHEP_STATUS_DISABLED);
- fsdev_chep_set_rx_status(chep_reg, FSDEV_CHEP_STATUS_DISABLED);
- }
- // printf("[hcd_edpt_open] EP%u %s opened on CHEP %u, PMA=0x%04x size=%u\n",
- // ep_num, dir ? "IN" : "OUT", chep_idx, pma_off, pma_size);
- // fsdev_usb_print_btable_at(USB_SRAM);
- // fsdev_usb_print_chep(*chep_reg, chep_idx);
- return true;
- }
Advertisement
Add Comment
Please, Sign In to add comment