usb: arc: implement batched request api

rockbox-gerrit-noreply--- via rockbox-cvs <[email protected]>
Newsgroups gmane.comp.systems.archos.rockbox.cvs
Message-ID <[email protected]>
commit 03060090c9aa9d1c14341dd47ce59b5ea0c7af23
Author: mojyack <[email protected]>
Date:   Fri Dec 19 17:13:57 2025 +0900

    usb: arc: implement batched request api
    
    Change-Id: I9d677286589a336d7258cf2c9d3c7d2847243dfa

diff --git a/firmware/export/config.h b/firmware/export/config.h
index 5fb260f336..4cd178e405 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -1362,8 +1362,12 @@ Lyre prototype 1 */
 //#define USB_HAS_INTERRUPT -- seems to be broken
 #endif /* CONFIG_USBOTG */
 
+#if CONFIG_USBOTG == USBOTG_ARC
+#define USB_BATCH_SLOTS 16
+#else
 #define USB_BATCH_NON_NATIVE
 #define USB_BATCH_SLOTS 1
+#endif
 
 /* define the class drivers to enable */
 #ifdef BOOTLOADER
diff --git a/firmware/target/arm/usb-drv-arc.c b/firmware/target/arm/usb-drv-arc.c
index 1eeee010b3..09c1ade438 100644
--- a/firmware/target/arm/usb-drv-arc.c
+++ b/firmware/target/arm/usb-drv-arc.c
@@ -362,6 +362,7 @@ static const unsigned int pipe2mask[USB_NUM_ENDPOINTS*2] = {
 /*-------------------------------------------------------------------------*/
 static void transfer_completed(void);
 static void control_received(void);
+static void sof_received(void);
 static int prime_transfer(int ep_num, void* ptr, int len, bool send, bool wait);
 static void prepare_td(struct transfer_descriptor* td,
         struct transfer_descriptor* previous_td, void *ptr, int len,int pipe);
@@ -415,6 +416,14 @@ static void usb_drv_reset(void)
 #endif
 }
 
+static void td_set_buf_ptr(struct transfer_descriptor* td, const void* ptr){
+    td->buff_ptr0 = (unsigned int)ptr;
+    td->buff_ptr1 = ((unsigned int)ptr & 0xfffff000) + 0x1000;
+    td->buff_ptr2 = ((unsigned int)ptr & 0xfffff000) + 0x2000;
+    td->buff_ptr3 = ((unsigned int)ptr & 0xfffff000) + 0x3000;
+    td->buff_ptr4 = ((unsigned int)ptr & 0xfffff000) + 0x4000;
+}
+
 /* One-time driver startup init */
 void usb_drv_startup(void)
 {
@@ -544,6 +553,12 @@ void usb_drv_int(void)
     if (status & USBSTS_PORT_CHANGE) {
         REG_USBSTS = USBSTS_PORT_CHANGE;
     }
+
+    /* sof */
+    if (status & USBSTS_SOF) {
+        REG_USBSTS = USBSTS_SOF;
+        sof_received();
+    }
 }
 
 bool usb_drv_stalled(int endpoint,bool in)
@@ -664,6 +679,183 @@ void usb_drv_set_test_mode(int mode)
     REG_USBCMD |= USBCMD_RUN;
 }
 
+/* batched request api  */
+static struct transfer_descriptor batch_td_array[USB_BATCH_SLOTS] USB_DEVBSS_ATTR __attribute__((aligned(32)));
+
+static uint8_t batch_ep = 0;
+static uint8_t batch_write_cursor;
+static bool batch_stopped;
+static usb_drv_batch_get_more batch_get_more;
+
+static int ep_to_pipe_index(uint8_t ep) {
+    const int ep_num = EP_NUM(ep);
+    const int ep_dir = EP_DIR(ep);
+    const int pipe = ep_num * 2 + (ep_dir == DIR_IN ? 1 : 0);
+    return pipe;
+}
+
+int usb_drv_batch_init(int ep, usb_drv_batch_get_more get_more) {
+    logf("batch init");
+    if(batch_ep != 0) {
+        logf("batch function not available");
+        return -1;
+    }
+    batch_ep = ep;
+    batch_get_more = get_more;
+    return 0;
+}
+
+int usb_drv_batch_deinit() {
+    logf("batch deinit");
+    usb_drv_batch_stop();
+    batch_ep = 0;
+    return 0;
+}
+
+/* returns whether priming is needed */
+static bool batch_fill_tds(void) {
+    while(!(batch_td_array[batch_write_cursor].size_ioc_sts & DTD_STATUS_ACTIVE)) {
+        /* batch_get_more may call batch_stop() through:
+        *  - batch_get_more()
+        *   - pcm_play_dma_complete_callback()
+        *    - sink_stop()
+        *     - usb_drv_batch_stop() */
+        const void* ptr;
+        size_t len;
+        batch_get_more(&ptr, &len);
+        if(len == 0 || batch_stopped) {
+            return false;
+        }
+
+        struct transfer_descriptor* td = &batch_td_array[batch_write_cursor];
+        td_set_buf_ptr(td, ptr);
+        td->size_ioc_sts = (len << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE;
+        batch_write_cursor = (batch_write_cursor + 1)  % USB_BATCH_SLOTS;
+    }
+    return true;
+}
+
+int usb_drv_batch_start(void) {
+    logf("batch start");
+
+    /* reset variables */
+    batch_write_cursor = 0;
+    batch_stopped = false;
+
+    /* chain tds */
+    memset(batch_td_array, 0, sizeof(struct transfer_descriptor) * USB_BATCH_SLOTS);
+    for(int i = 0; i < USB_BATCH_SLOTS; i += 1) {
+        struct transfer_descriptor* td = &batch_td_array[i];
+        td->next_td_ptr = (unsigned int)&batch_td_array[(i + 1) % USB_BATCH_SLOTS];
+    }
+
+    /* configure queue head */
+    const int pipe = ep_to_pipe_index(batch_ep);
+    struct queue_head* const qh = &qh_array[pipe];
+
+    qh->curr_dtd_ptr = (unsigned int)&batch_td_array[0]; /* or error check in sof_received may read random location */
+    qh->dtd.next_td_ptr = (unsigned int)&batch_td_array[0];
+    qh->dtd.size_ioc_sts = 0;
+
+    /* pull initial buffers */
+    batch_fill_tds();
+
+    /* monitor sof */
+    REG_USBINTR |= USBINTR_SOF_EN;
+    return 0;
+}
+
+int usb_drv_batch_stop(void) {
+    batch_stopped = true;
+
+    /* disable sof interrupt */
+    REG_USBINTR &= ~USBINTR_SOF_EN;
+
+    /* break the chain */
+    /* terminating qh->dtd.next_td_ptr is not reliable */
+    for(int i = 0; i < USB_BATCH_SLOTS; i += 1) {
+        struct transfer_descriptor* td = &batch_td_array[i];
+        td->next_td_ptr = DTD_NEXT_TERMINATE;
+    }
+
+    /* flush endpoint */
+    const int pipe = ep_to_pipe_index(batch_ep);
+    const unsigned int mask = pipe2mask[pipe];
+
+    REG_ENDPTFLUSH = mask;
+    while (REG_ENDPTFLUSH & mask);
+
+    return 0;
+}
+
+#if defined(LOGF_ENABLE) && defined(ROCKBOX_HAS_LOGF)
+void usb_drv_dump_regs(void) {
+    logf("==== register dump %ld ====", current_tick);
+    logf("USBSTS         0x%08X", REG_USBSTS);
+    logf("ENDPTSETUPSTAT 0x%08X", REG_ENDPTSETUPSTAT);
+    logf("ENDPTPRIME     0x%08X", REG_ENDPTPRIME);
+    logf("ENDPTFLUSH     0x%08X", REG_ENDPTFLUSH);
+    logf("ENDPTSTATUS    0x%08X", REG_ENDPTSTATUS);
+    logf("ENDPTCOMPLETE  0x%08X", REG_ENDPTCOMPLETE);
+    logf("ENDPTCTRL0     0x%08X", REG_ENDPTCTRL0);
+    logf("ENDPTCTRL1     0x%08X", REG_ENDPTCTRL1);
+    logf("ENDPTCTRL2     0x%08X", REG_ENDPTCTRL2);
+}
+
+static void dump_td_array(struct queue_head* qh, struct transfer_descriptor* tds, size_t size) {
+    void* current = (void*)qh->curr_dtd_ptr;
+    void* next = (void*)qh->dtd.next_td_ptr;
+
+    logf("==== td dump %ld n=%p c=%p ====", current_tick, next, current);
+    for(int i = 0; i < size; i += 1) {
+        int len = (tds[i].size_ioc_sts & DTD_PACKET_SIZE) >> DTD_LENGTH_BIT_POS;
+        char sts[] = {
+            &tds[i] == current ? 'c' : ' ',
+            &tds[i] == next ? 'n' : ' ',
+            i == batch_write_cursor ? 'w' : ' ',
+            '\0',
+        };
+        logf("%s td[%02d] status=0x%08X len=%d", sts, i, tds[i].size_ioc_sts, len);
+    }
+}
+
+void usb_drv_dump_tds(int ep) {
+    const int pipe = ep_to_pipe_index(ep);
+    struct queue_head* const qh = &qh_array[pipe];
+    dump_td_array(qh, &td_array[pipe * NUM_TDS_PER_EP], NUM_TDS_PER_EP);
+}
+
+void usb_drv_batch_dump_tds(void) {
+    const int pipe = ep_to_pipe_index(batch_ep);
+    struct queue_head* const qh = &qh_array[pipe];
+    dump_td_array(qh, batch_td_array, USB_BATCH_SLOTS);
+}
+#endif
+
+static void sof_received(void) {
+    if(batch_ep == 0) {
+        /* should not happen */
+        return;
+    }
+
+    /* error recovery */
+    const int pipe = ep_to_pipe_index(batch_ep);
+    const unsigned int mask = pipe2mask[pipe];
+    struct queue_head* const qh = &qh_array[pipe];
+    struct transfer_descriptor* const td = (void*)qh->curr_dtd_ptr;
+    if(td->size_ioc_sts & DTD_ERROR_MASK) {
+        logf("td error status=0x%08X", td->size_ioc_sts);
+        REG_ENDPTPRIME |= mask;
+    }
+
+    /* do refill */
+    if(batch_fill_tds()) {
+        const int pipe = ep_to_pipe_index(batch_ep);
+        const unsigned int mask = pipe2mask[pipe];
+        REG_ENDPTPRIME |= mask;
+    }
+}
+
 /*-------------------------------------------------------------------------*/
 
 /* manual: 32.14.5.2 */
@@ -854,11 +1046,7 @@ static void prepare_td(struct transfer_descriptor* td,
     td->next_td_ptr = DTD_NEXT_TERMINATE;
     td->size_ioc_sts = (len<< DTD_LENGTH_BIT_POS) |
         DTD_STATUS_ACTIVE | DTD_IOC;
-    td->buff_ptr0 = (unsigned int)ptr;
-    td->buff_ptr1 = ((unsigned int)ptr & 0xfffff000) + 0x1000;
-    td->buff_ptr2 = ((unsigned int)ptr & 0xfffff000) + 0x2000;
-    td->buff_ptr3 = ((unsigned int)ptr & 0xfffff000) + 0x3000;
-    td->buff_ptr4 = ((unsigned int)ptr & 0xfffff000) + 0x4000;
+    td_set_buf_ptr(td, ptr);
     td->reserved |= DTD_RESERVED_LENGTH_MASK & len;
     td->reserved |= DTD_RESERVED_IN_USE;
     td->reserved |= (pipe << DTD_RESERVED_PIPE_OFFSET);
-- 
rockbox-cvs mailing list
[email protected]
https://lists.haxx.se/mailman/listinfo/rockbox-cvs
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.