Guest User

Untitled

a guest
Nov 3rd, 2025
130
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 25.21 KB | None | 0 0
  1. /*
  2. * The MIT License (MIT)
  3.  *
  4.  * Copyright (c) gronnmann
  5.  * Copyright(c) N Conrad
  6.  * Copyright (c) 2019 Ha Thach (tinyusb.org)
  7.  *
  8.  * Redistribution and use in source and binary forms, with or without modification,
  9.  * are permitted provided that the following conditions are met:
  10.  *   1. Redistributions of source code must retain the above copyright notice,
  11.  *      this list of conditions and the following disclaimer.
  12.  *   2. Redistributions in binary form must reproduce the above copyright notice,
  13.  *      this list of conditions and the following disclaimer in the documentation
  14.  *      and/or other materials provided with the distribution.
  15.  *   3. Neither the name of STMicroelectronics nor the names of its contributors
  16.  *      may be used to endorse or promote products derived from this software
  17.  *      without specific prior written permission.
  18.  *
  19.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  20.  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  21.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  22.  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  23.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  24.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  25.  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  26.  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  27.  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  28.  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29.  *
  30.  * This file is part of the TinyUSB stack.
  31.  */
  32.  
  33. #include "tusb_option.h"
  34.  
  35. #if CFG_TUH_ENABLED && defined(TUP_USBIP_FSDEV)
  36. #include "host/hcd.h"
  37.  
  38. #if defined(TUP_USBIP_FSDEV_STM32)
  39. #elif defined(TUP_USBIP_FSDEV_CH32)
  40. #include "fsdev_ch32.h"
  41. #elif defined(TUP_USBIP_FSDEV_AT32)
  42. #include "fsdev_at32.h"
  43. #else
  44. #error "Unknown USB IP"
  45. #endif
  46.  
  47. #endif
  48.  
  49. #include <stdint.h>
  50. #include <stdbool.h>
  51. #include "tusb.h"
  52.  
  53. #include "stm32_helpers.h"
  54.  
  55. UsbPmaAllocator pma_allocator;
  56.  
  57. // EP allocator
  58. typedef struct {
  59.     bool valid; // is this entry valid
  60.  
  61.     uint8_t devaddr; // assigned device
  62.     uint8_t epnum; // Assigned ep number
  63.  
  64.     uint8_t chep_num;
  65.     uint8_t chep_type;
  66.     bool allocated[2]; // [0] = OUT, [1] = IN
  67.  
  68.     bool transferring[2]; // [0] = OUT, [1] = IN
  69.     bool in_setup[2];
  70.  
  71.     // Current PMA allocation size for this CHEP
  72.     uint16_t pma_allocated[2];
  73.  
  74.     // Current PMA offset for this CHEP
  75.     uint16_t pma_offset[2];
  76.  
  77.     uint8_t *buffer_ptr[2]; // tusb buffer pointer
  78.     // When transferring, we want to copy the data into the buffer, as
  79.     // the success event only takes in the length
  80.  
  81.     FsdevChepType control_type[2];
  82. } chep_alloc_t;
  83.  
  84. static chep_alloc_t chep_alloc_status[FSDEV_EP_COUNT];
  85.  
  86. void chear_chep_alloc_status(chep_alloc_t *chep) {
  87.     chep->valid = false;
  88.     chep->allocated[0] = false;
  89.     chep->allocated[1] = false;
  90.     chep->chep_num = 0xFF;
  91.     chep->chep_type = 0xFF;
  92.     chep->devaddr = 0;
  93.     chep->epnum = 0;
  94.     chep->pma_allocated[0] = 0;
  95.     chep->pma_offset[0] = 0;
  96.     chep->pma_allocated[1] = 0;
  97.     chep->pma_offset[1] = 0;
  98.     chep->transferring[0] = false;
  99.     chep->transferring[1] = false;
  100.     chep->in_setup[0] = false;
  101.     chep->in_setup[1] = false;
  102.     chep->buffer_ptr[0] = NULL;
  103.     chep->buffer_ptr[1] = NULL;
  104. }
  105.  
  106. /**
  107.  * Finds the CHEP index for given epnum and devaddr
  108.  * @param epnum the endpoint number
  109.  * @param devaddr the device address
  110.  * @return the CHEP index, or 0xFF if not found
  111.  */
  112. uint8_t find_chep(uint8_t epnum, uint8_t devaddr) {
  113.     for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
  114.         if ((chep_alloc_status[i].epnum == epnum) &&
  115.             (chep_alloc_status[i].devaddr == devaddr) && chep_alloc_status[i].valid) {
  116.             return i;
  117.         }
  118.     }
  119.     return 0xFF;
  120. }
  121.  
  122. uint8_t next_free_chep(void) {
  123.     for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
  124.         if (!chep_alloc_status[i].valid) {
  125.             return i;
  126.         }
  127.     }
  128.     return 0xFF;
  129. }
  130.  
  131. bool hcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init) {
  132.     (void) rhport;
  133.     (void) rh_init;
  134.  
  135.     // First -> reset whole register
  136.     fsdev_cntr_reset();
  137.     fsdev_istr_reset();
  138.  
  139.     // 1) Select host mode but don't clobber CNTR
  140.     USB_REG->CNTR |= USB_CNTR_HOST;
  141.  
  142.     // 2) Power up analog
  143.     USB_REG->CNTR &= ~USB_CNTR_PDWN;
  144.     HAL_Delay(10);
  145.  
  146.     // 3) Release reset
  147.     USB_REG->CNTR &= ~USB_CNTR_USBRST;
  148.     HAL_Delay(10);
  149.  
  150.     // 4) Clear/initialize BTABLE before use
  151.     for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
  152.         USB_SRAM->btable[i].txrx = 0;
  153.         USB_SRAM->btable[i].rxtx = 0;
  154.     }
  155.  
  156.     // 5) Enable interrupt masks
  157.     USB_REG->CNTR |= (USB_CNTR_CTRM | USB_CNTR_ERRM | USB_CNTR_RESETM |
  158.                       USB_CNTR_DCON | USB_CNTR_SOFM);
  159.  
  160.     usb_pma_allocator_init(&pma_allocator);
  161.  
  162.     // clear allocators
  163.     for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
  164.         chear_chep_alloc_status(&chep_alloc_status[i]);
  165.     }
  166.  
  167.     printf("Finished initialization of USB HOST MODE\r\n");
  168.     fsdev_usb_print_cntr(USB_REG->CNTR);
  169.     fsdev_usb_print_istr(USB_REG->ISTR);
  170.     fsdev_usb_print_pma(&pma_allocator);
  171.     for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
  172.         fsdev_usb_print_chep(USB_REG->CHEPnR[i].reg, i);
  173.     }
  174.     fsdev_usb_print_btable_at(USB_SRAM);
  175.     return true;
  176. }
  177.  
  178.  
  179. void hcd_int_enable(uint8_t rhport) {
  180.     // fsdev_usb_set_global_interrupt(true);
  181.     USB_REG->CNTR |= (USB_CNTR_CTRM | USB_CNTR_ERRM | USB_CNTR_RESETM |
  182.                       USB_CNTR_DCON | USB_CNTR_SOFM);
  183. }
  184.  
  185. void hcd_int_disable(uint8_t rhport) {
  186.     // fsdev_usb_set_global_interrupt(false);
  187.     USB_REG->CNTR &= ~(USB_CNTR_CTRM | USB_CNTR_ERRM | USB_CNTR_RESETM |
  188.                        USB_CNTR_DCON | USB_CNTR_SOFM);
  189. }
  190.  
  191. bool hcd_port_connect_status(uint8_t rhport) {
  192.     return (USB_REG->ISTR & USB_ISTR_DCON_STAT) != 0; // device connected
  193. }
  194.  
  195. void hcd_port_reset(uint8_t rhport) {
  196.     // printf("[hcd_port_reset] asserting reset line\r\n");
  197.     USB_REG->CNTR |= USB_CNTR_USBRST; // assert reset line
  198. }
  199.  
  200. void hcd_port_reset_end(uint8_t rhport) {
  201.     // printf("[hcd_port_reset_end] deasserting reset line\r\n");
  202.     USB_REG->CNTR &= ~USB_CNTR_USBRST; // de-assert reset line
  203. }
  204.  
  205. tusb_speed_t hcd_port_speed_get(uint8_t rhport) {
  206.     return (USB_REG->ISTR & USB_ISTR_LS_DCONN) ? TUSB_SPEED_LOW : TUSB_SPEED_FULL;
  207. }
  208.  
  209. /**
  210.  * Enqueues a OUT packet to be sent to the device.
  211.  *
  212.  * @return
  213.  */
  214. /**
  215.  * Enqueues a OUT/IN packet to be sent to/from the device.
  216.  *
  217.  * @return true if packet was successfully queued
  218.  */
  219. bool _enqueue_packet(uint8_t daddr, uint8_t ep_num, uint8_t dir, uint8_t *buffer, uint16_t buflen, bool setup) {
  220.     // printf("[_enqueue_packet] Got packet request: daddr=%u, ep_num=%u, dir=%u, buffer=%p, buflen=%u, setup=%u\r\n",
  221.     // daddr, ep_num, dir, buffer, buflen, setup);
  222.  
  223.     uint8_t chep_num = find_chep(ep_num, daddr);
  224.     if (chep_num == 0xFF) {
  225.         printf("[_enqueue_packet] CHEP for EP %u dev %u not found\r\n", ep_num, daddr);
  226.         return false;
  227.     }
  228.  
  229.     uint32_t chep_val = USB_REG->CHEPnR[chep_num].reg; // read, then write back
  230.     chep_alloc_t *chep = &chep_alloc_status[chep_num];
  231.  
  232.     if (!chep->allocated[dir]) {
  233.         printf("[_enqueue_packet] Endpoint %u for device %u dir %s not opened\r\n",
  234.                ep_num, daddr, dir ? "IN" : "OUT");
  235.         return false;
  236.     }
  237.  
  238.     if (chep->transferring[dir]) {
  239.         printf("[_enqueue_packet] Endpoint %u for device %u dir %s is already transferring\r\n",
  240.                ep_num, daddr, dir ? "IN" : "OUT");
  241.         return false;
  242.     }
  243.  
  244.     if (buflen > chep->pma_allocated[dir]) {
  245.         printf("[_enqueue_packet] Transfer size %u exceeds allocated PMA size %u\r\n",
  246.                buflen, chep->pma_allocated[dir]);
  247.         return false;
  248.     }
  249.  
  250.     FsdevChepStatus status;
  251.     if (dir == TUSB_DIR_OUT) {
  252.         status = fsdev_chep_get_tx_status(chep_val);
  253.     } else {
  254.         status = fsdev_chep_get_rx_status(chep_val);
  255.     }
  256.     if (status == FSDEV_CHEP_STATUS_VALID) {
  257.         printf("[_enqueue_packet] Endpoint %u for device %u dir %s is still VALID, cannot send new package yet \r\n",
  258.                ep_num, daddr, dir ? "IN" : "OUT");
  259.         return false;
  260.     }
  261.  
  262.  
  263.     // print chep before transfer
  264.     // printf("Before transfer: \r\n");
  265.     // fsdev_usb_print_btable_entry(tx_val, true);
  266.     // fsdev_usb_print_chep(chep_val, chep_num);
  267.  
  268.  
  269.     // ensure devaddr is always corerct
  270.     fsdev_chep_set_devaddr(&chep_val, daddr);
  271.     fsdev_chep_write_rw(&chep_val, USB_CHEP_ADDR, USB_CHEP_ADDR_Pos, ep_num);
  272.     fsdev_chep_set_type(&chep_val, chep->chep_type);
  273.  
  274.     if (dir == TUSB_DIR_OUT) {
  275.         // Out (host -> device)
  276.         if (setup) {
  277.             fsdev_chep_write_t_field(&chep_val, USB_CHEP_SETUP, USB_CHEP_SETUP_Pos, 1u);
  278.             // set EPKIND = 1
  279.             fsdev_chep_write_rw(&chep_val, USB_CHEP_KIND, USB_CHEP_KIND_Pos, 1u);
  280.  
  281.         }
  282.         fsdev_bd_tx(&USB_SRAM->btable[chep_num].txrx, chep->pma_offset[dir], buflen);
  283.         // fsdev_usb_print_btable_entry(USB_SRAM->btable[chep_num].txrx, true);
  284.  
  285.         if (buflen > 0) {
  286.             fsdev_pma_write(chep->pma_offset[dir], buffer, buflen);
  287.         }
  288.  
  289.         // Write RW fields to register FIRST
  290.         USB_REG->CHEPnR[chep_num].reg = chep_val;
  291.  
  292.         // NOW set TX status on actual register
  293.         fsdev_chep_set_tx_status(&USB_REG->CHEPnR[chep_num].reg, FSDEV_CHEP_STATUS_VALID);
  294.     } else {
  295.         // IN (device -> host)
  296.         fsdev_bd_tx(&USB_SRAM->btable[chep_num].txrx, chep->pma_offset[dir], chep->pma_allocated[dir]);
  297.  
  298.         // Write RW fields to register FIRST
  299.         USB_REG->CHEPnR[chep_num].reg = chep_val;
  300.  
  301.         // NOW set RX status on actual register
  302.         fsdev_chep_set_rx_status(&USB_REG->CHEPnR[chep_num].reg, FSDEV_CHEP_STATUS_VALID);
  303.     }
  304.  
  305.     chep->transferring[dir] = true;
  306.     chep->in_setup[dir] = setup;
  307.     chep->buffer_ptr[dir] = buffer;
  308.  
  309.     // printf("After transfer: \r\n");
  310.     // fsdev_usb_print_btable_entry(USB_SRAM->btable[chep_num].txrx, true);
  311.     // fsdev_usb_print_chep(USB_REG->CHEPnR[chep_num].reg, chep_num);
  312.  
  313.  
  314.     // printf("Successfully queued transfer on EP %u dir %s for device %u\r\n",
  315.     // ep_num, dir ? "IN" : "OUT", daddr);
  316.  
  317.     return true;
  318. }
  319.  
  320.  
  321. bool hcd_setup_send(uint8_t rhport, uint8_t daddr, uint8_t const setup_packet[8]) {
  322.     // printf("[hcd_setup_send] rhport=%u, daddr=%u\r\n", rhport, daddr);
  323.     (void) rhport;
  324.  
  325.     // Channel/endpoint 0 register
  326.     uint8_t chep_num = find_chep(0, daddr);
  327.     // lets find the first free CHEP if not found
  328.     if (chep_num == 0xFF) {
  329.         printf("[hcd_setup_send] CHEP for EP0 dev %u not found... \r\n", daddr);
  330.         return false;
  331.     }
  332.  
  333.     return _enqueue_packet(daddr, 0, TUSB_DIR_OUT, (uint8_t *) setup_packet, 8, true);
  334. }
  335.  
  336. /**
  337.  * Handle interrupt indicating connect/disconnect event
  338.  */
  339. static inline void _handle_connect_change(uint8_t rhport, bool in_isr) {
  340.     volatile uint32_t *reg = &USB_REG->ISTR;
  341.     const bool connected = (*reg & USB_ISTR_DCON_STAT) != 0;
  342.     if (connected) {
  343.         hcd_event_device_attach(rhport, in_isr);
  344.     } else {
  345.         hcd_event_device_remove(rhport, in_isr);
  346.     }
  347.     fsdev_istr_clear_irq(USB_ISTR_DCON | USB_ISTR_RESET);
  348. }
  349.  
  350. /**
  351.  * Handle interrupt indicating SOF (Start Of Frame) event
  352.  */
  353. static inline void _handle_sof() {
  354.     volatile uint32_t *reg = &USB_REG->ISTR;
  355.     // printf("[_handle_sof] USB ISTR=0x%08lx. Clearing SOF flag.\r\n", *reg);
  356.     fsdev_istr_clear_irq(USB_ISTR_SOF);
  357. }
  358.  
  359. /**
  360.  * Handle interrupt indicating error event
  361.  */
  362. static inline void _handle_error(void) {
  363.     volatile uint32_t *reg = &USB_REG->ISTR;
  364.     // printf("[_handle_error] USB ISTR=0x%08lx. Clearing error flags.\r\n", *reg);
  365.     fsdev_istr_clear_irq(USB_ISTR_ERR);
  366. }
  367.  
  368.  
  369. static inline void _handle_correct_transfer(uint8_t rhport, bool in_isr) {
  370.     volatile uint32_t *istr = &USB_REG->ISTR;
  371.     // printf("[_handle_correct_transfer] USB ISTR=0x%08lx\r\n", *istr);
  372.  
  373.     uint8_t dir = (*istr & USB_ISTR_DIR) >> USB_ISTR_DIR_Pos;
  374.     uint8_t chep_idx = fsdev_istr_read_idn(istr);
  375.     uint32_t chep_val = USB_REG->CHEPnR[chep_idx].reg;
  376.     chep_alloc_t *alloc = &chep_alloc_status[chep_idx];
  377.  
  378.     // In host mode, ALWAYS use TX descriptor for count (hardware quirk for both directions)
  379.     uint32_t btable_val = USB_SRAM->btable[chep_idx].txrx;
  380.     // printf("[_handle_correct_transfer] Received CTR on CHEP %u (EP %u for device %u) dir %s\r\n",
  381.     // chep_idx, alloc->epnum, alloc->devaddr, dir ? "IN" : "OUT");
  382.  
  383.     // fsdev_usb_print_chep(chep_val, chep_idx);
  384.     // fsdev_usb_print_btable_entry(btable_val, true);
  385.  
  386.     if (!alloc->valid) {
  387.         printf("[_handle_correct_transfer] Weird CTR received. CHEP %u not allocated\r\n", chep_idx);
  388.         fsdev_istr_clear_irq(USB_ISTR_CTR);
  389.         return;
  390.     }
  391.  
  392.     if (!alloc->transferring[dir]) {
  393.         printf("[_handle_correct_transfer] Weird CTR received. CHEP %u not transferring in dir %u\r\n",
  394.                chep_idx, dir);
  395.         fsdev_istr_clear_irq(USB_ISTR_CTR);
  396.         return;
  397.     }
  398.  
  399.     uint16_t xferred_bytes = 0;
  400.     xfer_result_t result = XFER_RESULT_FAILED;
  401.  
  402.     bool nak = false;
  403.  
  404.     // Check for error conditions first
  405.     if (chep_val & (USB_CHEP_ERRRX | USB_CHEP_ERRTX)) {
  406.         // printf("[_handle_correct_transfer] Transfer error detected\r\n");
  407.         result = XFER_RESULT_FAILED;
  408.  
  409.         // Clear error flags using helper
  410.         fsdev_chep_clear_rcw0(&chep_val, USB_CHEP_ERRRX | USB_CHEP_ERRTX);
  411.         USB_REG->CHEPnR[chep_idx].reg = chep_val;
  412.  
  413.         // Check if we have 3 consecutive errors
  414.         if (chep_val & (USB_CHEP_THREE_ERR_RX | USB_CHEP_THREE_ERR_TX)) {
  415.             const char *err_type = "unknown";
  416.             uint32_t err_code = 0;
  417.  
  418.             if (chep_val & USB_CHEP_THREE_ERR_RX) {
  419.                 err_code = (chep_val & USB_CHEP_THREE_ERR_RX) >> USB_CHEP_THREE_ERR_RX_Pos;
  420.                 switch (err_code) {
  421.                     case 0x1: err_type = "timeout";
  422.                         break;
  423.                     case 0x2: err_type = "data error (CRC)";
  424.                         break;
  425.                     case 0x3: err_type = "protocol error";
  426.                         break;
  427.                 }
  428.                 // printf("[_handle_correct_transfer] Three consecutive IN errors detected: %s (code %lu)\r\n",
  429.                 // err_type, err_code);
  430.             } else {
  431.                 err_code = (chep_val & USB_CHEP_THREE_ERR_TX) >> USB_CHEP_THREE_ERR_TX_Pos;
  432.                 switch (err_code) {
  433.                     case 0x1: err_type = "timeout";
  434.                         break;
  435.                     case 0x2: err_type = "data error (CRC)";
  436.                         break;
  437.                     case 0x3: err_type = "protocol error";
  438.                         break;
  439.                 }
  440.                 // printf("[_handle_correct_transfer] Three consecutive OUT errors detected: %s (code %lu)\r\n",
  441.                 // err_type, err_code);
  442.             }
  443.  
  444.             // Clear three error flags
  445.             fsdev_chep_clear_rcw0(&chep_val, USB_CHEP_THREE_ERR_RX | USB_CHEP_THREE_ERR_TX);
  446.             USB_REG->CHEPnR[chep_idx].reg = chep_val;
  447.         }
  448.     }
  449.     // Check for NAK condition
  450.     else if (chep_val & USB_CHEP_NAK) {
  451.         printf("[_handle_correct_transfer] Got NAK - retrying\r\n");
  452.  
  453.         // Clear NAK flag
  454.         fsdev_chep_clear_rcw0(&chep_val, USB_CHEP_NAK);
  455.         USB_REG->CHEPnR[chep_idx].reg = chep_val;
  456.  
  457.         // Re-enable channel to retry - don't complete transfer
  458.         if (dir == TUSB_DIR_OUT) {
  459.             fsdev_chep_set_tx_status(&USB_REG->CHEPnR[chep_idx].reg, FSDEV_CHEP_STATUS_VALID);
  460.         } else {
  461.             fsdev_chep_set_rx_status(&USB_REG->CHEPnR[chep_idx].reg, FSDEV_CHEP_STATUS_VALID);
  462.         }
  463.  
  464.         return;
  465.     }
  466.     // Check for STALL condition
  467.     else if ((dir && (fsdev_chep_get_rx_status(chep_val) == FSDEV_CHEP_STATUS_STALL)) ||
  468.              (!dir && (fsdev_chep_get_tx_status(chep_val) == FSDEV_CHEP_STATUS_STALL))) {
  469.         printf("[_handle_correct_transfer] Got STALL\r\n");
  470.         result = XFER_RESULT_STALLED;
  471.     } else {
  472.         // Get actual transferred bytes from COUNTn_TX
  473.         xferred_bytes = fsdev_bd_tx_get_count_ptr(&USB_SRAM->btable[chep_idx].txrx);
  474.  
  475.         // For IN transfers, copy data from USB SRAM to buffer
  476.         if (dir && alloc->buffer_ptr[dir] && xferred_bytes > 0) {
  477.             uint16_t pma_addr = fsdev_bd_tx_get_addr_ptr(&USB_SRAM->btable[chep_idx].txrx);
  478.             fsdev_pma_read(alloc->buffer_ptr[dir], pma_addr, xferred_bytes);
  479.         }
  480.  
  481.         result = XFER_RESULT_SUCCESS;
  482.         printf("[_handle_correct_transfer] Transfer successful, %u bytes\r\n", xferred_bytes);
  483.     }
  484.  
  485.     // Complete the transfer - Clear the VTRX or VTTX bit
  486.     uint32_t chep_clear = USB_REG->CHEPnR[chep_idx].reg;
  487.     if (dir == TUSB_DIR_OUT) {
  488.         fsdev_chep_clear_rcw0(&chep_clear, USB_CHEP_VTTX);
  489.     } else {
  490.         fsdev_chep_clear_rcw0(&chep_clear, USB_CHEP_VTRX);
  491.     }
  492.     USB_REG->CHEPnR[chep_idx].reg = chep_clear;
  493.     fsdev_istr_clear_irq(USB_ISTR_CTR);
  494.  
  495.     // Reset state
  496.     alloc->transferring[dir] = false;
  497.     alloc->in_setup[dir] = false;
  498.  
  499.  
  500.     // Notify TinyUSB - data already in buffer from above
  501.     // printf("[_handle_correct_transfer] Completing: dev %u EP %u dir %s, result %u, bytes %u\r\n",
  502.     // alloc->devaddr, alloc->epnum, dir ? "IN" : "OUT", result, xferred_bytes);
  503.     hcd_event_xfer_complete(alloc->devaddr,
  504.                             tu_edpt_addr(alloc->epnum, dir),
  505.                             result, xferred_bytes, in_isr);
  506. }
  507.  
  508. void hcd_int_handler(uint8_t rhport, bool in_isr) {
  509.     volatile uint32_t *reg = &USB_REG->ISTR;
  510.     // printf("interrupt\r\n");
  511.  
  512.     uint8_t dir = *reg & USB_ISTR_DIR;
  513.  
  514.     if (*reg & USB_ISTR_ERR) {
  515.         _handle_error();
  516.     }
  517.  
  518.     if (*reg & USB_ISTR_CTR) {
  519.         _handle_correct_transfer(rhport, in_isr);
  520.     }
  521.  
  522.     if (*reg & (USB_ISTR_DCON | USB_ISTR_RESET)) {
  523.         _handle_connect_change(rhport, in_isr);
  524.     }
  525.  
  526.     if (*reg & USB_ISTR_SOF) {
  527.         _handle_sof();
  528.     }
  529.  
  530.     if (*reg & USB_ISTR_PMAOVR) {
  531.         // printf("PMA overran, clearing \r\n");
  532.         fsdev_istr_clear_irq(USB_ISTR_PMAOVR);
  533.     }
  534.  
  535.  
  536.     // print afterwards to ensure everything is correctly cleared
  537.     // fsdev_usb_print_istr(*reg);
  538. }
  539.  
  540. void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
  541.     // printf("Got close request for device %u\r\n", dev_addr);
  542.     (void) rhport;
  543.  
  544.     // find all ChEP allocated to this device, and close all of them
  545.     for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
  546.         if (chep_alloc_status[i].valid && (chep_alloc_status[i].devaddr == dev_addr)) {
  547.             // printf("[hcd_device_close] Closing CHEP %u for device %u\r\n", i, dev_addr);
  548.             // TODO - free PMA
  549.             chear_chep_alloc_status(&chep_alloc_status[i]);
  550.  
  551.             // Now we need to clear the actual CHEP registers
  552.             volatile uint32_t *chep_reg = &USB_REG->CHEPnR[i].reg;
  553.             fsdev_chep_set_tx_status(chep_reg, FSDEV_CHEP_STATUS_DISABLED);
  554.             fsdev_chep_set_rx_status(chep_reg, FSDEV_CHEP_STATUS_DISABLED);
  555.         }
  556.     }
  557. }
  558.  
  559.  
  560. /**
  561.  * Queue a transfer on a given pipe
  562.  */
  563. bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen) {
  564.     uint8_t ep_num = tu_edpt_number(ep_addr);
  565.     uint8_t dir = tu_edpt_dir(ep_addr); // TUSB_DIR_OUT = 0, TUSB_DIR_IN = 1
  566.     // printf("[hcd_edpt_xfer] Got xfer request. ep_num: %u, daddr: %u, dir: %s, len: %u\r\n",
  567.     // ep_num, daddr, dir ? "IN" : "OUT", buflen);
  568.  
  569.  
  570.     return _enqueue_packet(daddr, ep_num, dir, buffer, buflen, false);
  571. }
  572.  
  573. /**
  574.  * Stop a running transfer on a given pipe.
  575.  */
  576. bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
  577.     uint8_t ep_num = tu_edpt_number(ep_addr);
  578.     uint8_t dir = tu_edpt_dir(ep_addr);
  579.  
  580.     uint8_t chep_num = find_chep(ep_num, dev_addr);
  581.     if (chep_num == 0xFF) {
  582.         printf("[hcd_edpt_abort_xfer] Tried aborting transfer, but CHEP for EP %u dev %u not found... \r\n", ep_num,
  583.                dev_addr);
  584.         return false;
  585.     }
  586.     volatile uint32_t *const reg = &USB_REG->CHEPnR[chep_num].reg;
  587.     chep_alloc_t *chep = &chep_alloc_status[chep_num];
  588.     if (!chep->allocated[dir]) {
  589.         printf("[hcd_edpt_abort_xfer] Endpoint %u for device %u dir %s not opened\r\n",
  590.                ep_num, dev_addr, dir ? "IN" : "OUT");
  591.         return false;
  592.     }
  593.     if (!chep->transferring[dir]) {
  594.         printf("[hcd_edpt_abort_xfer] Endpoint %u for device %u dir %s is not transferring\r\n",
  595.                ep_num, dev_addr, dir ? "IN" : "OUT");
  596.         return false;
  597.     }
  598.  
  599.     // set disabled
  600.     if (dir == TUSB_DIR_OUT) {
  601.         fsdev_chep_set_tx_status(reg, FSDEV_CHEP_STATUS_DISABLED);
  602.     } else {
  603.         fsdev_chep_set_rx_status(reg, FSDEV_CHEP_STATUS_DISABLED);
  604.     }
  605.     return true;
  606. }
  607.  
  608. /**
  609.  * Create a pipe for an endpoint
  610.  * This means picking channel, programming CHEPnR, allocate PMA, bind BTABLE etc.
  611.  */
  612. bool hcd_edpt_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const *ep_desc) {
  613.     (void) rhport;
  614.  
  615.     uint8_t ep_num = tu_edpt_number(ep_desc->bEndpointAddress);
  616.     uint8_t dir = tu_edpt_dir(ep_desc->bEndpointAddress);
  617.     uint16_t pma_size = tu_edpt_packet_size(ep_desc);
  618.     if (ep_num == 0 && pma_size < 64) {
  619.         pma_size = 64; // control endpoint minimum size for full speed
  620.     }
  621.  
  622.     // Find or allocate CHEP for this device endpoint
  623.     uint8_t chep_idx = find_chep(ep_num, daddr);
  624.     if (chep_idx == 0xFF) {
  625.         chep_idx = next_free_chep();
  626.         if (chep_idx == 0xFF) return false;
  627.     }
  628.  
  629.     chep_alloc_t *chep = &chep_alloc_status[chep_idx];
  630.     volatile uint32_t *chep_reg = &USB_REG->CHEPnR[chep_idx].reg;
  631.  
  632.     // Write to local copy, then write to actual CHeP
  633.     uint32_t chep_to_write = *chep_reg;
  634.  
  635.     // Allocate PMA buffer for this direction
  636.     uint16_t pma_off = usb_pma_alloc(&pma_allocator, pma_size);
  637.     if (pma_off == 0xFFFFu) return false;
  638.  
  639.     // 1) Program channel static config: type, address, endpoint
  640.     FsdevChepType ep_type;
  641.     switch (ep_desc->bmAttributes.xfer) {
  642.         case TUSB_XFER_CONTROL: ep_type = FSDEV_CHEP_TYPE_CONTROL;
  643.             break;
  644.         case TUSB_XFER_BULK: ep_type = FSDEV_CHEP_TYPE_BULK;
  645.             break;
  646.         case TUSB_XFER_INTERRUPT: ep_type = FSDEV_CHEP_TYPE_INTERRUPT;
  647.             break;
  648.         default: ep_type = FSDEV_CHEP_TYPE_BULK;
  649.             break;
  650.     }
  651.     fsdev_chep_set_type(&chep_to_write, ep_type);
  652.     fsdev_chep_set_devaddr(&chep_to_write, daddr);
  653.     fsdev_chep_write_rw(&chep_to_write, USB_CHEP_ADDR, USB_CHEP_ADDR_Pos, ep_num);
  654.  
  655.     // 2) Disable both directions before touching BD/flags (host-mode best practice)
  656.     fsdev_chep_set_tx_status(&chep_to_write, FSDEV_CHEP_STATUS_DISABLED);
  657.     fsdev_chep_set_rx_status(&chep_to_write, FSDEV_CHEP_STATUS_DISABLED);
  658.  
  659.     // 3) Clear stale events/errors on this channel
  660.     uint32_t clear_mask = USB_CHEP_RC_W0_MASK;
  661.     fsdev_chep_clear_rcw0(&chep_to_write, clear_mask);
  662.     fsdev_chep_write_t_field(&chep_to_write, USB_CHEP_DTOG_TX, USB_CHEP_DTOG_TX_Pos, 0);
  663.     fsdev_chep_write_t_field(&chep_to_write, USB_CHEP_DTOG_RX, USB_CHEP_DTOG_RX_Pos, 0);
  664.  
  665.  
  666.     // Write back to actual CHEP register
  667.     *chep_reg = chep_to_write;
  668.  
  669.     // 4) Bind BD TX to PMA address and clear count (host uses TX BD for both directions)
  670.     fsdev_bd_tx(&USB_SRAM->btable[chep_idx].txrx, pma_off, 0);
  671.  
  672.  
  673.     // Save allocation bookkeeping
  674.     chep->valid = true;
  675.     chep->devaddr = daddr;
  676.     chep->epnum = ep_num;
  677.     chep->chep_num = chep_idx;
  678.     chep->chep_type = ep_type;
  679.  
  680.     if (ep_num == 0) {
  681.         // Control endpoint needs separate PMA buffers for IN and OUT
  682.         uint16_t pma_off_in = usb_pma_alloc(&pma_allocator, pma_size);
  683.         uint16_t pma_off_out = usb_pma_alloc(&pma_allocator, pma_size);
  684.  
  685.         if (pma_off_in == 0xFFFFu || pma_off_out == 0xFFFFu) {
  686.             // Free allocated PMA if one failed
  687.             if (pma_off_in != 0xFFFFu) return false;
  688.             if (pma_off_out != 0xFFFFu) return false;
  689.             return false;
  690.         }
  691.  
  692.         chep->allocated[TUSB_DIR_IN] = true;
  693.         chep->pma_offset[TUSB_DIR_IN] = pma_off_in;
  694.         chep->pma_allocated[TUSB_DIR_IN] = pma_size;
  695.  
  696.         chep->allocated[TUSB_DIR_OUT] = true;
  697.         chep->pma_offset[TUSB_DIR_OUT] = pma_off_out;  // Different PMA!
  698.         chep->pma_allocated[TUSB_DIR_OUT] = pma_size;
  699.     } else {
  700.         // Non-control endpoints - mark the opened direction
  701.         chep->allocated[dir] = true;
  702.         chep->pma_offset[dir] = pma_off;
  703.         chep->pma_allocated[dir] = pma_size;
  704.         chep->control_type[dir] = ep_type;
  705.  
  706.         fsdev_chep_set_tx_status(chep_reg, FSDEV_CHEP_STATUS_DISABLED);
  707.         fsdev_chep_set_rx_status(chep_reg, FSDEV_CHEP_STATUS_DISABLED);
  708.     }
  709.  
  710.     // printf("[hcd_edpt_open] EP%u %s opened on CHEP %u, PMA=0x%04x size=%u\n",
  711.     // ep_num, dir ? "IN" : "OUT", chep_idx, pma_off, pma_size);
  712.     // fsdev_usb_print_btable_at(USB_SRAM);
  713.     // fsdev_usb_print_chep(*chep_reg, chep_idx);
  714.     return true;
  715. }
  716.  
Advertisement
Add Comment
Please, Sign In to add comment