Răsfoiți Sursa

add test for pipe_interrupt_xfer
implement keyboard app code
- forcefully place keyboard_report in RAM section 3
change used bit in qtd from reserved in buffer[1] to alternate link
add code for fake ehci controller runs on period interrupt
change signature of tusbh_hid_keyboard_get_report
- tusb_keyboard_report_t* to uint8_t*
implement period (interrupt) complete isr processing

hathach 13 ani în urmă
părinte
comite
7b5d9edc5a

+ 26 - 146
demos/host/host.uvopt

@@ -316,7 +316,7 @@
 
   <Group>
     <GroupName>host</GroupName>
-    <tvExp>1</tvExp>
+    <tvExp>0</tvExp>
     <tvExpOptDlg>0</tvExpOptDlg>
     <cbSel>0</cbSel>
     <RteFlg>0</RteFlg>
@@ -339,16 +339,16 @@
     <File>
       <GroupNumber>2</GroupNumber>
       <FileNumber>8</FileNumber>
-      <FileType>5</FileType>
+      <FileType>1</FileType>
       <tvExp>0</tvExp>
       <Focus>0</Focus>
-      <ColumnNumber>0</ColumnNumber>
+      <ColumnNumber>52</ColumnNumber>
       <tvExpOptDlg>0</tvExpOptDlg>
-      <TopLine>0</TopLine>
-      <CurrentLine>0</CurrentLine>
+      <TopLine>36</TopLine>
+      <CurrentLine>47</CurrentLine>
       <bDave2>0</bDave2>
-      <PathWithFileName>.\tusb_config.h</PathWithFileName>
-      <FilenameWithoutPath>tusb_config.h</FilenameWithoutPath>
+      <PathWithFileName>./keyboard_app.c</PathWithFileName>
+      <FilenameWithoutPath>keyboard_app.c</FilenameWithoutPath>
       <RteFlg>0</RteFlg>
       <bShared>0</bShared>
     </File>
@@ -380,7 +380,7 @@
 
   <Group>
     <GroupName>tinyusb_class</GroupName>
-    <tvExp>0</tvExp>
+    <tvExp>1</tvExp>
     <tvExpOptDlg>0</tvExpOptDlg>
     <cbSel>0</cbSel>
     <RteFlg>0</RteFlg>
@@ -400,33 +400,17 @@
       <RteFlg>0</RteFlg>
       <bShared>0</bShared>
     </File>
-    <File>
-      <GroupNumber>4</GroupNumber>
-      <FileNumber>11</FileNumber>
-      <FileType>1</FileType>
-      <tvExp>0</tvExp>
-      <Focus>0</Focus>
-      <ColumnNumber>0</ColumnNumber>
-      <tvExpOptDlg>0</tvExpOptDlg>
-      <TopLine>0</TopLine>
-      <CurrentLine>0</CurrentLine>
-      <bDave2>0</bDave2>
-      <PathWithFileName>../../tinyusb/class/msc_host.c</PathWithFileName>
-      <FilenameWithoutPath>msc_host.c</FilenameWithoutPath>
-      <RteFlg>0</RteFlg>
-      <bShared>0</bShared>
-    </File>
   </Group>
 
   <Group>
     <GroupName>tinyusb_common</GroupName>
-    <tvExp>0</tvExp>
+    <tvExp>1</tvExp>
     <tvExpOptDlg>0</tvExpOptDlg>
     <cbSel>0</cbSel>
     <RteFlg>0</RteFlg>
     <File>
       <GroupNumber>5</GroupNumber>
-      <FileNumber>12</FileNumber>
+      <FileNumber>11</FileNumber>
       <FileType>1</FileType>
       <tvExp>0</tvExp>
       <Focus>0</Focus>
@@ -442,7 +426,7 @@
     </File>
     <File>
       <GroupNumber>5</GroupNumber>
-      <FileNumber>13</FileNumber>
+      <FileNumber>12</FileNumber>
       <FileType>1</FileType>
       <tvExp>0</tvExp>
       <Focus>0</Focus>
@@ -460,13 +444,13 @@
 
   <Group>
     <GroupName>tinyusb_hal</GroupName>
-    <tvExp>0</tvExp>
+    <tvExp>1</tvExp>
     <tvExpOptDlg>0</tvExpOptDlg>
     <cbSel>0</cbSel>
     <RteFlg>0</RteFlg>
     <File>
       <GroupNumber>6</GroupNumber>
-      <FileNumber>14</FileNumber>
+      <FileNumber>13</FileNumber>
       <FileType>1</FileType>
       <tvExp>0</tvExp>
       <Focus>0</Focus>
@@ -482,7 +466,7 @@
     </File>
     <File>
       <GroupNumber>6</GroupNumber>
-      <FileNumber>15</FileNumber>
+      <FileNumber>14</FileNumber>
       <FileType>1</FileType>
       <tvExp>0</tvExp>
       <Focus>0</Focus>
@@ -498,7 +482,7 @@
     </File>
     <File>
       <GroupNumber>6</GroupNumber>
-      <FileNumber>16</FileNumber>
+      <FileNumber>15</FileNumber>
       <FileType>1</FileType>
       <tvExp>0</tvExp>
       <Focus>0</Focus>
@@ -516,13 +500,13 @@
 
   <Group>
     <GroupName>tinyusb_host</GroupName>
-    <tvExp>0</tvExp>
+    <tvExp>1</tvExp>
     <tvExpOptDlg>0</tvExpOptDlg>
     <cbSel>0</cbSel>
     <RteFlg>0</RteFlg>
     <File>
       <GroupNumber>7</GroupNumber>
-      <FileNumber>17</FileNumber>
+      <FileNumber>16</FileNumber>
       <FileType>1</FileType>
       <tvExp>0</tvExp>
       <Focus>0</Focus>
@@ -538,7 +522,7 @@
     </File>
     <File>
       <GroupNumber>7</GroupNumber>
-      <FileNumber>18</FileNumber>
+      <FileNumber>17</FileNumber>
       <FileType>1</FileType>
       <tvExp>0</tvExp>
       <Focus>0</Focus>
@@ -556,13 +540,13 @@
 
   <Group>
     <GroupName>tinyusb_host_ehci</GroupName>
-    <tvExp>0</tvExp>
+    <tvExp>1</tvExp>
     <tvExpOptDlg>0</tvExpOptDlg>
     <cbSel>0</cbSel>
     <RteFlg>0</RteFlg>
     <File>
       <GroupNumber>8</GroupNumber>
-      <FileNumber>19</FileNumber>
+      <FileNumber>18</FileNumber>
       <FileType>1</FileType>
       <tvExp>0</tvExp>
       <Focus>0</Focus>
@@ -580,13 +564,13 @@
 
   <Group>
     <GroupName>tinyusb_osal</GroupName>
-    <tvExp>0</tvExp>
+    <tvExp>1</tvExp>
     <tvExpOptDlg>0</tvExpOptDlg>
     <cbSel>0</cbSel>
     <RteFlg>0</RteFlg>
     <File>
       <GroupNumber>9</GroupNumber>
-      <FileNumber>20</FileNumber>
+      <FileNumber>19</FileNumber>
       <FileType>1</FileType>
       <tvExp>0</tvExp>
       <Focus>0</Focus>
@@ -603,128 +587,24 @@
   </Group>
 
   <Group>
-    <GroupName>startup</GroupName>
+    <GroupName>Startup</GroupName>
     <tvExp>1</tvExp>
     <tvExpOptDlg>0</tvExpOptDlg>
     <cbSel>0</cbSel>
     <RteFlg>0</RteFlg>
     <File>
       <GroupNumber>10</GroupNumber>
-      <FileNumber>21</FileNumber>
+      <FileNumber>20</FileNumber>
       <FileType>2</FileType>
       <tvExp>0</tvExp>
       <Focus>0</Focus>
       <ColumnNumber>0</ColumnNumber>
       <tvExpOptDlg>0</tvExpOptDlg>
-      <TopLine>146</TopLine>
-      <CurrentLine>158</CurrentLine>
-      <bDave2>0</bDave2>
-      <PathWithFileName>..\bsp\lpc43xx\startup_keil\startup_LPC43xx_arm.s</PathWithFileName>
-      <FilenameWithoutPath>startup_LPC43xx_arm.s</FilenameWithoutPath>
-      <RteFlg>0</RteFlg>
-      <bShared>0</bShared>
-    </File>
-  </Group>
-
-  <Group>
-    <GroupName>CMSISv2p10_LPC43xx_DriverLib</GroupName>
-    <tvExp>1</tvExp>
-    <tvExpOptDlg>0</tvExpOptDlg>
-    <cbSel>0</cbSel>
-    <RteFlg>0</RteFlg>
-    <File>
-      <GroupNumber>11</GroupNumber>
-      <FileNumber>22</FileNumber>
-      <FileType>1</FileType>
-      <tvExp>0</tvExp>
-      <Focus>0</Focus>
-      <ColumnNumber>0</ColumnNumber>
-      <tvExpOptDlg>0</tvExpOptDlg>
-      <TopLine>0</TopLine>
-      <CurrentLine>0</CurrentLine>
-      <bDave2>0</bDave2>
-      <PathWithFileName>..\..\..\CMSISv2p10_LPC43xx_DriverLib\src\lpc43xx_uart.c</PathWithFileName>
-      <FilenameWithoutPath>lpc43xx_uart.c</FilenameWithoutPath>
-      <RteFlg>0</RteFlg>
-      <bShared>0</bShared>
-    </File>
-    <File>
-      <GroupNumber>11</GroupNumber>
-      <FileNumber>23</FileNumber>
-      <FileType>1</FileType>
-      <tvExp>0</tvExp>
-      <Focus>0</Focus>
-      <ColumnNumber>0</ColumnNumber>
-      <tvExpOptDlg>0</tvExpOptDlg>
       <TopLine>0</TopLine>
       <CurrentLine>0</CurrentLine>
       <bDave2>0</bDave2>
-      <PathWithFileName>..\..\..\CMSISv2p10_LPC43xx_DriverLib\src\lpc43xx_cgu.c</PathWithFileName>
-      <FilenameWithoutPath>lpc43xx_cgu.c</FilenameWithoutPath>
-      <RteFlg>0</RteFlg>
-      <bShared>0</bShared>
-    </File>
-    <File>
-      <GroupNumber>11</GroupNumber>
-      <FileNumber>24</FileNumber>
-      <FileType>1</FileType>
-      <tvExp>0</tvExp>
-      <Focus>0</Focus>
-      <ColumnNumber>0</ColumnNumber>
-      <tvExpOptDlg>0</tvExpOptDlg>
-      <TopLine>0</TopLine>
-      <CurrentLine>0</CurrentLine>
-      <bDave2>0</bDave2>
-      <PathWithFileName>..\..\..\CMSISv2p10_LPC43xx_DriverLib\src\lpc43xx_gpio.c</PathWithFileName>
-      <FilenameWithoutPath>lpc43xx_gpio.c</FilenameWithoutPath>
-      <RteFlg>0</RteFlg>
-      <bShared>0</bShared>
-    </File>
-    <File>
-      <GroupNumber>11</GroupNumber>
-      <FileNumber>25</FileNumber>
-      <FileType>1</FileType>
-      <tvExp>0</tvExp>
-      <Focus>0</Focus>
-      <ColumnNumber>0</ColumnNumber>
-      <tvExpOptDlg>0</tvExpOptDlg>
-      <TopLine>0</TopLine>
-      <CurrentLine>0</CurrentLine>
-      <bDave2>0</bDave2>
-      <PathWithFileName>..\..\..\CMSISv2p10_LPC43xx_DriverLib\src\lpc43xx_nvic.c</PathWithFileName>
-      <FilenameWithoutPath>lpc43xx_nvic.c</FilenameWithoutPath>
-      <RteFlg>0</RteFlg>
-      <bShared>0</bShared>
-    </File>
-    <File>
-      <GroupNumber>11</GroupNumber>
-      <FileNumber>26</FileNumber>
-      <FileType>1</FileType>
-      <tvExp>0</tvExp>
-      <Focus>0</Focus>
-      <ColumnNumber>0</ColumnNumber>
-      <tvExpOptDlg>0</tvExpOptDlg>
-      <TopLine>0</TopLine>
-      <CurrentLine>0</CurrentLine>
-      <bDave2>0</bDave2>
-      <PathWithFileName>..\..\..\CMSISv2p10_LPC43xx_DriverLib\src\lpc43xx_scu.c</PathWithFileName>
-      <FilenameWithoutPath>lpc43xx_scu.c</FilenameWithoutPath>
-      <RteFlg>0</RteFlg>
-      <bShared>0</bShared>
-    </File>
-    <File>
-      <GroupNumber>11</GroupNumber>
-      <FileNumber>27</FileNumber>
-      <FileType>1</FileType>
-      <tvExp>0</tvExp>
-      <Focus>0</Focus>
-      <ColumnNumber>0</ColumnNumber>
-      <tvExpOptDlg>0</tvExpOptDlg>
-      <TopLine>34</TopLine>
-      <CurrentLine>68</CurrentLine>
-      <bDave2>0</bDave2>
-      <PathWithFileName>..\..\..\CMSISv2p10_LPC43xx_DriverLib\src\system_LPC43xx.c</PathWithFileName>
-      <FilenameWithoutPath>system_LPC43xx.c</FilenameWithoutPath>
+      <PathWithFileName>..\bsp\lpc43xx\startup_keil\startup_LPC43xx_arm.s</PathWithFileName>
+      <FilenameWithoutPath>startup_LPC43xx_arm.s</FilenameWithoutPath>
       <RteFlg>0</RteFlg>
       <bShared>0</bShared>
     </File>

+ 4 - 44
demos/host/host.uvproj

@@ -432,9 +432,9 @@
               <FilePath>./main.c</FilePath>
             </File>
             <File>
-              <FileName>tusb_config.h</FileName>
-              <FileType>5</FileType>
-              <FilePath>.\tusb_config.h</FilePath>
+              <FileName>keyboard_app.c</FileName>
+              <FileType>1</FileType>
+              <FilePath>./keyboard_app.c</FilePath>
             </File>
           </Files>
         </Group>
@@ -456,11 +456,6 @@
               <FileType>1</FileType>
               <FilePath>../../tinyusb/class/hid_host.c</FilePath>
             </File>
-            <File>
-              <FileName>msc_host.c</FileName>
-              <FileType>1</FileType>
-              <FilePath>../../tinyusb/class/msc_host.c</FilePath>
-            </File>
           </Files>
         </Group>
         <Group>
@@ -534,7 +529,7 @@
           </Files>
         </Group>
         <Group>
-          <GroupName>startup</GroupName>
+          <GroupName>Startup</GroupName>
           <Files>
             <File>
               <FileName>startup_LPC43xx_arm.s</FileName>
@@ -543,41 +538,6 @@
             </File>
           </Files>
         </Group>
-        <Group>
-          <GroupName>CMSISv2p10_LPC43xx_DriverLib</GroupName>
-          <Files>
-            <File>
-              <FileName>lpc43xx_uart.c</FileName>
-              <FileType>1</FileType>
-              <FilePath>..\..\..\CMSISv2p10_LPC43xx_DriverLib\src\lpc43xx_uart.c</FilePath>
-            </File>
-            <File>
-              <FileName>lpc43xx_cgu.c</FileName>
-              <FileType>1</FileType>
-              <FilePath>..\..\..\CMSISv2p10_LPC43xx_DriverLib\src\lpc43xx_cgu.c</FilePath>
-            </File>
-            <File>
-              <FileName>lpc43xx_gpio.c</FileName>
-              <FileType>1</FileType>
-              <FilePath>..\..\..\CMSISv2p10_LPC43xx_DriverLib\src\lpc43xx_gpio.c</FilePath>
-            </File>
-            <File>
-              <FileName>lpc43xx_nvic.c</FileName>
-              <FileType>1</FileType>
-              <FilePath>..\..\..\CMSISv2p10_LPC43xx_DriverLib\src\lpc43xx_nvic.c</FilePath>
-            </File>
-            <File>
-              <FileName>lpc43xx_scu.c</FileName>
-              <FileType>1</FileType>
-              <FilePath>..\..\..\CMSISv2p10_LPC43xx_DriverLib\src\lpc43xx_scu.c</FilePath>
-            </File>
-            <File>
-              <FileName>system_LPC43xx.c</FileName>
-              <FileType>1</FileType>
-              <FilePath>..\..\..\CMSISv2p10_LPC43xx_DriverLib\src\system_LPC43xx.c</FilePath>
-            </File>
-          </Files>
-        </Group>
       </Groups>
     </Target>
   </Targets>

+ 34 - 2
demos/host/keyboard_app.c

@@ -38,9 +38,7 @@
 //--------------------------------------------------------------------+
 // INCLUDE
 //--------------------------------------------------------------------+
-#include "tusb.h"
 #include "keyboard_app.h"
-
 //--------------------------------------------------------------------+
 // MACRO CONSTANT TYPEDEF
 //--------------------------------------------------------------------+
@@ -48,17 +46,51 @@
 //--------------------------------------------------------------------+
 // INTERNAL OBJECT & FUNCTION DECLARATION
 //--------------------------------------------------------------------+
+//TUSB_CFG_ATTR_USBRAM
+__attribute__ ((section(".data.$RAM3")))
+tusb_keyboard_report_t keyboard_report;
 
 //--------------------------------------------------------------------+
 // IMPLEMENTATION
 //--------------------------------------------------------------------+
+
+// only convert a-z (case insensitive) +  0-9
+static inline uint8_t keycode_to_ascii(uint8_t keycode) ATTR_CONST ATTR_ALWAYS_INLINE;
+static inline uint8_t keycode_to_ascii(uint8_t keycode)
+{
+  return
+      ( KEYBOARD_KEYCODE_a <= keycode && keycode <= KEYBOARD_KEYCODE_z) ? ( (keycode - KEYBOARD_KEYCODE_a) + 'a' ) :
+      ( KEYBOARD_KEYCODE_1 <= keycode && keycode < KEYBOARD_KEYCODE_0)  ? ( (keycode - KEYBOARD_KEYCODE_1) + '1' ) :
+      ( KEYBOARD_KEYCODE_0 == keycode)                                  ? '0' : 'x';
+}
+
+
 void keyboard_app_task(void)
 {
   for (uint8_t dev_addr = 1; dev_addr <= TUSB_CFG_HOST_DEVICE_MAX; dev_addr++)
   {
     if ( tusbh_hid_keyboard_is_supported(dev_addr) )
     {
+      switch (tusbh_hid_keyboard_status(dev_addr,0))
+      {
+        case TUSB_INTERFACE_STATUS_READY:
+        case TUSB_INTERFACE_STATUS_ERROR: // skip error, get next key
+          tusbh_hid_keyboard_get_report(dev_addr, 0, (uint8_t*) &keyboard_report);
+        break;
+
+        case TUSB_INTERFACE_STATUS_COMPLETE:
+          // TODO buffer in queue
+          for(uint8_t i=0; i<6; i++)
+          {
+            if ( keyboard_report.keycode[i] != 0 )
+              printf("%c", keycode_to_ascii(keyboard_report.keycode[i]));
+          }
+        break;
+
+        case TUSB_INTERFACE_STATUS_BUSY:
+        break;
 
+      }
     }
   }
 }

+ 2 - 1
demos/host/keyboard_app.h

@@ -55,7 +55,8 @@
  extern "C" {
 #endif
 
-
+#include "boards/board.h"
+#include "tusb.h"
 
 #ifdef __cplusplus
  }

+ 1 - 0
demos/host/main.c

@@ -25,6 +25,7 @@ int main(void)
   while (1)
   {
     tusb_task_runner();
+    keyboard_app_task();
 
     if (current_tick + 10*CFG_TICKS_PER_SECOND < system_ticks)
     {

+ 1 - 1
tests/test/host/ehci/test_ehci_structure.c

@@ -113,6 +113,7 @@ void test_qtd_structure(void)
 {
   TEST_ASSERT_EQUAL( 0, offsetof(ehci_qtd_t, next));
   TEST_ASSERT_EQUAL( 4, offsetof(ehci_qtd_t, alternate));
+  TEST_ASSERT_EQUAL( 5, BITFIELD_OFFSET_OF_UINT32(ehci_qtd_t, 1, used));
 
   //------------- Word 2 -------------//
   TEST_ASSERT_EQUAL( 0, BITFIELD_OFFSET_OF_UINT32(ehci_qtd_t, 2, pingstate_err) );
@@ -131,7 +132,6 @@ void test_qtd_structure(void)
   TEST_ASSERT_EQUAL( 31, BITFIELD_OFFSET_OF_UINT32(ehci_qtd_t, 2, data_toggle) );
 
   TEST_ASSERT_EQUAL( 12, offsetof(ehci_qtd_t, buffer));
-  TEST_ASSERT_EQUAL( 16, offsetof(ehci_qtd_t, used));
 }
 
 void test_qhd_structure(void)

+ 0 - 1
tests/test/host/ehci/test_pipe_bulk_xfer.c

@@ -49,7 +49,6 @@
 
 usbh_device_info_t usbh_devices[TUSB_CFG_HOST_DEVICE_MAX+1];
 
-uint8_t const control_max_packet_size = 64;
 uint8_t const hub_addr = 2;
 uint8_t const hub_port = 2;
 uint8_t dev_addr;

+ 208 - 0
tests/test/host/ehci/test_pipe_interrupt_xfer.c

@@ -0,0 +1,208 @@
+/*
+ * test_ehci_pipe_bulk_xfer.c
+ *
+ *  Created on: Feb 27, 2013
+ *      Author: hathach
+ */
+
+/*
+ * Software License Agreement (BSD License)
+ * Copyright (c) 2012, hathach (tinyusb.org)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the tiny usb stack.
+ */
+
+#include "unity.h"
+#include "tusb_option.h"
+#include "errors.h"
+#include "binary.h"
+
+#include "hal.h"
+#include "mock_osal.h"
+#include "hcd.h"
+#include "mock_usbh_hcd.h"
+#include "ehci.h"
+#include "ehci_controller.h"
+
+usbh_device_info_t usbh_devices[TUSB_CFG_HOST_DEVICE_MAX+1];
+
+uint8_t const hub_addr = 2;
+uint8_t const hub_port = 2;
+uint8_t dev_addr;
+uint8_t hostid;
+uint8_t xfer_data [100];
+uint8_t data2[100];
+
+ehci_qhd_t *period_head;
+ehci_qhd_t *p_qhd_interrupt;
+pipe_handle_t pipe_hdl_interrupt;
+
+tusb_descriptor_endpoint_t const desc_ept_interrupt_in =
+{
+    .bLength          = sizeof(tusb_descriptor_endpoint_t),
+    .bDescriptorType  = TUSB_DESC_ENDPOINT,
+    .bEndpointAddress = 0x81,
+    .bmAttributes     = { .xfer = TUSB_XFER_INTERRUPT },
+    .wMaxPacketSize   = 8,
+    .bInterval        = 0
+};
+
+tusb_descriptor_endpoint_t const desc_ept_interupt_out =
+{
+    .bLength          = sizeof(tusb_descriptor_endpoint_t),
+    .bDescriptorType  = TUSB_DESC_ENDPOINT,
+    .bEndpointAddress = 0x01,
+    .bmAttributes     = { .xfer = TUSB_XFER_INTERRUPT },
+    .wMaxPacketSize   = 64,
+    .bInterval        = 0
+};
+
+//--------------------------------------------------------------------+
+// Setup/Teardown + helper declare
+//--------------------------------------------------------------------+
+void setUp(void)
+{
+  memclr_(&lpc_usb0, sizeof(LPC_USB0_Type));
+  memclr_(&lpc_usb1, sizeof(LPC_USB1_Type));
+
+  memclr_(usbh_devices, sizeof(usbh_device_info_t)*(TUSB_CFG_HOST_DEVICE_MAX+1));
+  memclr_(xfer_data, sizeof(xfer_data));
+
+  hcd_init();
+
+  dev_addr = 1;
+
+  hostid = RANDOM(CONTROLLER_HOST_NUMBER) + TEST_CONTROLLER_HOST_START_INDEX;
+  for (uint8_t i=0; i<TUSB_CFG_HOST_DEVICE_MAX+1; i++)
+  {
+    usbh_devices[i].core_id  = hostid;
+    usbh_devices[i].hub_addr = hub_addr;
+    usbh_devices[i].hub_port = hub_port;
+    usbh_devices[i].speed    = TUSB_SPEED_HIGH;
+  }
+
+  period_head =  get_period_head( hostid );
+  pipe_hdl_interrupt = hcd_pipe_open(dev_addr, &desc_ept_interrupt_in, TUSB_CLASS_HID);
+
+  TEST_ASSERT_EQUAL(dev_addr, pipe_hdl_interrupt.dev_addr);
+  TEST_ASSERT_EQUAL(TUSB_XFER_INTERRUPT, pipe_hdl_interrupt.xfer_type);
+
+  p_qhd_interrupt = &ehci_data.device[ dev_addr -1].qhd[ pipe_hdl_interrupt.index ];
+}
+
+void tearDown(void)
+{
+}
+//--------------------------------------------------------------------+
+// BULK TRANSFER
+//--------------------------------------------------------------------+
+void verify_qtd(ehci_qtd_t *p_qtd, uint8_t p_data[], uint16_t length)
+{
+  TEST_ASSERT_TRUE(p_qtd->alternate.terminate); // not used, always invalid
+
+  //------------- status -------------//
+  TEST_ASSERT_FALSE(p_qtd->pingstate_err);
+  TEST_ASSERT_FALSE(p_qtd->non_hs_split_state);
+  TEST_ASSERT_FALSE(p_qtd->non_hs_period_missed_uframe);
+  TEST_ASSERT_FALSE(p_qtd->xact_err);
+  TEST_ASSERT_FALSE(p_qtd->babble_err);
+  TEST_ASSERT_FALSE(p_qtd->buffer_err);
+  TEST_ASSERT_FALSE(p_qtd->halted);
+  TEST_ASSERT_TRUE(p_qtd->active);
+
+  TEST_ASSERT_FALSE(p_qtd->data_toggle);
+  TEST_ASSERT_EQUAL(3, p_qtd->cerr);
+  TEST_ASSERT_EQUAL(0, p_qtd->current_page);
+  TEST_ASSERT_EQUAL(length, p_qtd->total_bytes);
+  TEST_ASSERT_TRUE(p_qtd->used);
+
+  TEST_ASSERT_EQUAL_HEX( p_data, p_qtd->buffer[0] );
+  for(uint8_t i=1; i<5; i++)
+  {
+    TEST_ASSERT_EQUAL_HEX( align4k((uint32_t) (p_data+4096*i)), align4k(p_qtd->buffer[i]) );
+  }
+}
+
+void test_interrupt_xfer(void)
+{
+  //------------- Code Under Test -------------//
+  hcd_pipe_xfer(pipe_hdl_interrupt, xfer_data, sizeof(xfer_data), true);
+
+  ehci_qtd_t* p_qtd = p_qhd_interrupt->p_qtd_list_head;
+  TEST_ASSERT_NOT_NULL(p_qtd);
+
+  verify_qtd( p_qtd, xfer_data, sizeof(xfer_data));
+  TEST_ASSERT_EQUAL_HEX(p_qhd_interrupt->qtd_overlay.next.address, p_qtd);
+  TEST_ASSERT_TRUE(p_qtd->next.terminate);
+  TEST_ASSERT_EQUAL(EHCI_PID_IN, p_qtd->pid);
+  TEST_ASSERT_TRUE(p_qtd->int_on_complete);
+}
+
+void test_interrupt_xfer_double(void)
+{
+  //------------- Code Under Test -------------//
+  hcd_pipe_xfer(pipe_hdl_interrupt, xfer_data, sizeof(xfer_data), false);
+  hcd_pipe_xfer(pipe_hdl_interrupt, data2, sizeof(data2), true);
+
+  ehci_qtd_t* p_head = p_qhd_interrupt->p_qtd_list_head;
+  ehci_qtd_t* p_tail = p_qhd_interrupt->p_qtd_list_tail;
+
+  //------------- list head -------------//
+  TEST_ASSERT_NOT_NULL(p_head);
+  verify_qtd(p_head, xfer_data, sizeof(xfer_data));
+  TEST_ASSERT_EQUAL_HEX(p_qhd_interrupt->qtd_overlay.next.address, p_head);
+  TEST_ASSERT_EQUAL(EHCI_PID_IN, p_head->pid);
+  TEST_ASSERT_FALSE(p_head->next.terminate);
+  TEST_ASSERT_FALSE(p_head->int_on_complete);
+
+  //------------- list tail -------------//
+  TEST_ASSERT_NOT_NULL(p_tail);
+  verify_qtd(p_tail, data2, sizeof(data2));
+  TEST_ASSERT_EQUAL_HEX( align32(p_head->next.address), p_tail);
+  TEST_ASSERT_EQUAL(EHCI_PID_IN, p_tail->pid);
+  TEST_ASSERT_TRUE(p_tail->next.terminate);
+  TEST_ASSERT_TRUE(p_tail->int_on_complete);
+}
+
+void test_interrupt_xfer_complete_isr(void)
+{
+  hcd_pipe_xfer(pipe_hdl_interrupt, xfer_data, sizeof(xfer_data), false);
+  hcd_pipe_xfer(pipe_hdl_interrupt, data2, sizeof(data2), true);
+
+  ehci_qtd_t* p_head = p_qhd_interrupt->p_qtd_list_head;
+  ehci_qtd_t* p_tail = p_qhd_interrupt->p_qtd_list_tail;
+
+  usbh_isr_Expect(pipe_hdl_interrupt, TUSB_CLASS_HID, BUS_EVENT_XFER_COMPLETE);
+
+  //------------- Code Under Test -------------//
+  ehci_controller_run(hostid);
+
+  TEST_ASSERT_TRUE(p_qhd_interrupt->qtd_overlay.next.terminate);
+  TEST_ASSERT_FALSE(p_head->used);
+  TEST_ASSERT_FALSE(p_tail->used);
+  TEST_ASSERT_NULL(p_qhd_interrupt->p_qtd_list_head);
+  TEST_ASSERT_NULL(p_qhd_interrupt->p_qtd_list_tail);
+}

+ 23 - 7
tests/test/support/ehci_controller.c

@@ -55,12 +55,10 @@ LPC_USB1_Type lpc_usb1;
 //--------------------------------------------------------------------+
 // IMPLEMENTATION
 //--------------------------------------------------------------------+
-void ehci_controller_run(uint8_t hostid)
+bool complete_all_qtd_in_list(ehci_qhd_t *head)
 {
-  //------------- Async List -------------//
-  ehci_registers_t* const regs = get_operational_register(hostid);
+  ehci_qhd_t *p_qhd = head;
 
-  ehci_qhd_t *p_qhd = (ehci_qhd_t*) regs->async_list_base;
   do
   {
     if ( !p_qhd->qtd_overlay.halted )
@@ -72,11 +70,29 @@ void ehci_controller_run(uint8_t hostid)
         p_qhd->qtd_overlay = *p_qtd;
       }
     }
-    p_qhd = (ehci_qhd_t*) align32(p_qhd->next.address);
-  }while(p_qhd != get_async_head(hostid)); // stop if loop around
+    if (!p_qhd->next.terminate)
+    {
+      p_qhd = (ehci_qhd_t*) align32(p_qhd->next.address);
+    }
+    else
+    {
+      break;
+    }
+  }while(p_qhd != head); // stop if loop around
+
+  return true;
+}
+
+void ehci_controller_run(uint8_t hostid)
+{
+  //------------- Async List -------------//
+  ehci_registers_t* const regs = get_operational_register(hostid);
+  complete_all_qtd_in_list((ehci_qhd_t*) regs->async_list_base);
+
   //------------- Period List -------------//
+ complete_all_qtd_in_list( get_period_head(hostid) );
+  regs->usb_sts = EHCI_INT_MASK_NXP_ASYNC | EHCI_INT_MASK_NXP_PERIODIC;
 
-  regs->usb_sts = EHCI_INT_MASK_NXP_ASYNC;
   hcd_isr(hostid);
 }
 

+ 2 - 2
tinyusb/class/hid.h

@@ -102,12 +102,12 @@ typedef ATTR_PREPACKED struct
  *
  *  Type define for a standard Boot Protocol Keyboard report
  */
-typedef ATTR_PREPACKED struct
+typedef ATTR_PACKED_STRUCT(struct)
 {
   uint8_t modifier; /**< Keyboard modifier byte, indicating pressed modifier keys (a combination of HID_KEYBOARD_MODIFER_* masks). */
   uint8_t reserved; /**< Reserved for OEM use, always set to 0. */
   uint8_t keycode[6]; /**< Key codes of the currently pressed keys. */
-} ATTR_PACKED tusb_keyboard_report_t;
+} tusb_keyboard_report_t;
 
 /**
  * \brief buttons codes for HID mouse

+ 2 - 2
tinyusb/class/hid_host.c

@@ -72,7 +72,7 @@ bool  tusbh_hid_keyboard_is_supported(uint8_t dev_addr)
   return tusbh_device_is_configured(dev_addr) && pipehandle_is_valid(keyboard_data[dev_addr-1].pipe_hdl);
 }
 
-tusb_error_t tusbh_hid_keyboard_get_report(uint8_t dev_addr, uint8_t instance_num, tusb_keyboard_report_t * const report)
+tusb_error_t tusbh_hid_keyboard_get_report(uint8_t dev_addr, uint8_t instance_num, uint8_t * const report)
 {
   //------------- parameters validation -------------//
   ASSERT_INT(TUSB_DEVICE_STATE_CONFIGURED, tusbh_device_get_state(dev_addr), TUSB_ERROR_DEVICE_NOT_READY);
@@ -85,7 +85,7 @@ tusb_error_t tusbh_hid_keyboard_get_report(uint8_t dev_addr, uint8_t instance_nu
   ASSERT(TUSB_INTERFACE_STATUS_BUSY != p_keyboard->status, TUSB_ERROR_INTERFACE_IS_BUSY);
 
   // TODO abstract to use hidh service
-  ASSERT_STATUS( hcd_pipe_xfer(p_keyboard->pipe_hdl, (uint8_t*) report, p_keyboard->report_size, true) ) ;
+  ASSERT_STATUS( hcd_pipe_xfer(p_keyboard->pipe_hdl, report, p_keyboard->report_size, true) ) ;
 
   p_keyboard->status = TUSB_INTERFACE_STATUS_BUSY;
 

+ 1 - 1
tinyusb/class/hid_host.h

@@ -69,7 +69,7 @@ typedef struct {
 }hidh_keyboard_info_t;
 
 bool          tusbh_hid_keyboard_is_supported(uint8_t dev_addr) ATTR_PURE ATTR_WARN_UNUSED_RESULT;
-tusb_error_t  tusbh_hid_keyboard_get_report(uint8_t dev_addr, uint8_t instance_num, tusb_keyboard_report_t * const report) ATTR_WARN_UNUSED_RESULT;
+tusb_error_t  tusbh_hid_keyboard_get_report(uint8_t dev_addr, uint8_t instance_num, uint8_t * const report) ATTR_WARN_UNUSED_RESULT;
 tusb_interface_status_t tusbh_hid_keyboard_status(uint8_t dev_addr, uint8_t instance_num) ATTR_WARN_UNUSED_RESULT;
 
 //--------------------------------------------------------------------+

+ 229 - 184
tinyusb/host/ehci/ehci.c

@@ -107,9 +107,6 @@ static tusb_error_t hcd_controller_stop(uint8_t hostid) ATTR_WARN_UNUSED_RESULT;
 //--------------------------------------------------------------------+
 tusb_error_t hcd_init(void)
 {
-  // oops, ehci_qtd_t:used must be at Reserved places in EHCI specs
-  ASSERT(offsetof(ehci_qtd_t, used) == 16, TUSB_ERROR_HCD_FAILED); // TODO can be removed after an THOROUGH checked
-
   //------------- Data Structure init -------------//
   memclr_(&ehci_data, sizeof(ehci_data_t));
 
@@ -146,182 +143,6 @@ bool hcd_port_connect_status(uint8_t hostid)
   return get_operational_register(hostid)->portsc_bit.current_connect_status;
 }
 
-//--------------------------------------------------------------------+
-// EHCI Interrupt Handler
-//--------------------------------------------------------------------+
-void async_advance_isr(ehci_qhd_t * const async_head)
-{
-  // TODO do we need to close addr0
-  if(async_head->is_removing) // closing control pipe of addr0
-  {
-    async_head->is_removing = 0;
-    async_head->p_qtd_list_head = async_head->p_qtd_list_tail = NULL;
-    async_head->qtd_overlay.halted = 1;
-  }
-
-  for(uint8_t relative_dev_addr=0; relative_dev_addr < TUSB_CFG_HOST_DEVICE_MAX; relative_dev_addr++)
-  {
-    // check if control endpoint is removing
-    ehci_qhd_t *p_control_qhd = &ehci_data.device[relative_dev_addr].control.qhd;
-    if( p_control_qhd->is_removing )
-    {
-      p_control_qhd->is_removing     = 0;
-      p_control_qhd->used            = 0;
-
-      // Host Controller has cleaned up its cached data for this device, set state to unplug
-      usbh_devices[relative_dev_addr+1].state = TUSB_DEVICE_STATE_UNPLUG;
-
-      for (uint8_t i=0; i<EHCI_MAX_QHD; i++) // free all qhd
-      {
-        ehci_data.device[relative_dev_addr].qhd[i].used = 0;
-        ehci_data.device[relative_dev_addr].qhd[i].is_removing = 0;
-      }
-      for (uint8_t i=0; i<EHCI_MAX_QTD; i++) // free all qtd
-      {
-        ehci_data.device[relative_dev_addr].qtd[i].used = 0;
-      }
-      // TODO free all itd & sitd
-    }
-
-//    // check if any other endpoints in pool is removing
-//    for (uint8_t i=0; i<EHCI_MAX_QHD; i++)
-//    {
-//      ehci_qhd_t *p_qhd = &ehci_data.device[relative_dev_addr].qhd[i];
-//      if (p_qhd->is_removing)
-//      {
-//        p_qhd->used        = 0;
-//        p_qhd->is_removing = 0;
-//
-//        while(p_qhd->p_qtd_list_head != NULL) // remove all TDs
-//        {
-//          p_qhd->p_qtd_list_head->used = 0; // free QTD
-//          qtd_remove_1st_from_qhd(p_qhd);
-//        }
-//      }
-//    }// end qhd list loop
-  } // end for device[] loop
-}
-
-void port_connect_status_change_isr(uint8_t hostid)
-{
-  ehci_registers_t* const regs = get_operational_register(hostid);
-
-  if (regs->portsc_bit.current_connect_status) // device plugged
-  {
-    hcd_port_reset(hostid);
-    usbh_device_plugged_isr(hostid, regs->portsc_bit.nxp_port_speed); // NXP specific port speed
-  }else // device unplugged
-  {
-    usbh_device_unplugged_isr(hostid);
-    regs->usb_cmd_bit.advacne_async = 1; // Async doorbell check EHCI 4.8.2 for operational details
-  }
-
-}
-
-void async_list_process_isr(ehci_qhd_t * const async_head)
-{
-  ehci_qhd_t *p_qhd = async_head;
-  do
-  {
-    if ( !p_qhd->qtd_overlay.halted )
-    {
-      // 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)
-      {
-        // TODO check halted TD
-        if (p_qhd->p_qtd_list_head->int_on_complete) // end of request
-        {
-          pipe_handle_t pipe_hdl = { .dev_addr = p_qhd->device_address };
-          if (p_qhd->endpoint_number) // if not Control, can only be Bulk
-          {
-            pipe_hdl.xfer_type = TUSB_XFER_BULK;
-            pipe_hdl.index = qhd_get_index(p_qhd);
-          }
-          usbh_isr( pipe_hdl, p_qhd->class_code, BUS_EVENT_XFER_COMPLETE); // call USBH callback
-        }
-
-        p_qhd->p_qtd_list_head->used = 0; // free QTD
-        qtd_remove_1st_from_qhd(p_qhd);
-      }
-    }
-    p_qhd = (ehci_qhd_t*) align32(p_qhd->next.address);
-  }while(p_qhd != async_head); // async list traversal, stop if loop around
-}
-
-void xfer_error_isr(uint8_t hostid)
-{
-  //------------- async list -------------//
-  ehci_qhd_t * const async_head = get_async_head(hostid);
-  ehci_qhd_t *p_qhd = async_head;
-  do
-  {
-    // current qhd has error in transaction
-    if (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
-        (p_qhd->device_address != 0 && p_qhd->qtd_overlay.halted) ) // addr0 cannot be protocol STALL
-    {
-      pipe_handle_t pipe_hdl = { .dev_addr = p_qhd->device_address };
-      if (p_qhd->endpoint_number) // if not Control, can only be Bulk
-      {
-        pipe_hdl.xfer_type = TUSB_XFER_BULK;
-        pipe_hdl.index = qhd_get_index(p_qhd);
-      }
-      usbh_isr( pipe_hdl, p_qhd->class_code, BUS_EVENT_XFER_ERROR); // call USBH callback
-    }
-
-    p_qhd = (ehci_qhd_t*) align32(p_qhd->next.address);
-  }while(p_qhd != async_head); // async list traversal, stop if loop around
-
-  //------------- TODO period list -------------//
-}
-
-//------------- Host Controller Driver's Interrupt Handler -------------//
-void hcd_isr(uint8_t hostid)
-{
-  ehci_registers_t* const regs = get_operational_register(hostid);
-
-  uint32_t int_status = regs->usb_sts & regs->usb_int_enable;
-  regs->usb_sts |= int_status; // Acknowledge handled interrupt
-  
-  if (int_status == 0)
-    return;
-
-  if (int_status & EHCI_INT_MASK_ERROR)
-  {
-    // TODO handle Queue Head halted
-    hal_debugger_breakpoint();
-    xfer_error_isr(hostid);
-  }
-
-  //------------- some QTD/SITD/ITD with IOC set is completed -------------//
-  if (int_status & EHCI_INT_MASK_NXP_ASYNC)
-  {
-    async_list_process_isr(get_async_head(hostid));
-  }
-
-  if (int_status & EHCI_INT_MASK_NXP_PERIODIC)
-  {
-
-  }
-
-  if (int_status & EHCI_INT_MASK_PORT_CHANGE)
-  {
-    uint32_t port_status = regs->portsc & EHCI_PORTSC_MASK_ALL;
-
-    if (regs->portsc_bit.connect_status_change)
-    {
-      port_connect_status_change_isr(hostid);
-    }
-
-    regs->portsc |= port_status; // Acknowledge change bits in portsc
-  }
-
-  if (int_status & EHCI_INT_MASK_ASYNC_ADVANCE) // need to place after EHCI_INT_MASK_NXP_ASYNC
-  {
-    async_advance_isr( get_async_head(hostid) );
-  }
-}
-
 //--------------------------------------------------------------------+
 // Controller API
 //--------------------------------------------------------------------+
@@ -409,11 +230,6 @@ tusb_error_t hcd_controller_reset(uint8_t hostid)
   timeout_timer_t timeout;
 
 // NXP chip powered with non-host mode --> sts bit is not correctly reflected
-//  if (regs->usb_sts_bit.hc_halted == 0) // need to stop before reset
-//  {
-//    ASSERT_STATUS( hcd_controller_stop(hostid) );
-//  }
-
   regs->usb_cmd_bit.reset = 1;
 
   timeout_set(&timeout, 2); // should not take longer the time to stop controller
@@ -586,6 +402,235 @@ tusb_error_t  hcd_pipe_close(pipe_handle_t pipe_hdl)
   return TUSB_ERROR_NONE;
 }
 
+
+//--------------------------------------------------------------------+
+// EHCI Interrupt Handler
+//--------------------------------------------------------------------+
+void async_advance_isr(ehci_qhd_t * const async_head)
+{
+  // TODO do we need to close addr0
+  if(async_head->is_removing) // closing control pipe of addr0
+  {
+    async_head->is_removing = 0;
+    async_head->p_qtd_list_head = async_head->p_qtd_list_tail = NULL;
+    async_head->qtd_overlay.halted = 1;
+  }
+
+  for(uint8_t relative_dev_addr=0; relative_dev_addr < TUSB_CFG_HOST_DEVICE_MAX; relative_dev_addr++)
+  {
+    // check if control endpoint is removing
+    ehci_qhd_t *p_control_qhd = &ehci_data.device[relative_dev_addr].control.qhd;
+    if( p_control_qhd->is_removing )
+    {
+      p_control_qhd->is_removing     = 0;
+      p_control_qhd->used            = 0;
+
+      // Host Controller has cleaned up its cached data for this device, set state to unplug
+      usbh_devices[relative_dev_addr+1].state = TUSB_DEVICE_STATE_UNPLUG;
+
+      for (uint8_t i=0; i<EHCI_MAX_QHD; i++) // free all qhd
+      {
+        ehci_data.device[relative_dev_addr].qhd[i].used = 0;
+        ehci_data.device[relative_dev_addr].qhd[i].is_removing = 0;
+      }
+      for (uint8_t i=0; i<EHCI_MAX_QTD; i++) // free all qtd
+      {
+        ehci_data.device[relative_dev_addr].qtd[i].used = 0;
+      }
+      // TODO free all itd & sitd
+    }
+
+//    // check if any other endpoints in pool is removing
+//    for (uint8_t i=0; i<EHCI_MAX_QHD; i++)
+//    {
+//      ehci_qhd_t *p_qhd = &ehci_data.device[relative_dev_addr].qhd[i];
+//      if (p_qhd->is_removing)
+//      {
+//        p_qhd->used        = 0;
+//        p_qhd->is_removing = 0;
+//
+//        while(p_qhd->p_qtd_list_head != NULL) // remove all TDs
+//        {
+//          p_qhd->p_qtd_list_head->used = 0; // free QTD
+//          qtd_remove_1st_from_qhd(p_qhd);
+//        }
+//      }
+//    }// end qhd list loop
+  } // end for device[] loop
+}
+
+void port_connect_status_change_isr(uint8_t hostid)
+{
+  ehci_registers_t* const regs = get_operational_register(hostid);
+
+  if (regs->portsc_bit.current_connect_status) // device plugged
+  {
+    hcd_port_reset(hostid);
+    usbh_device_plugged_isr(hostid, regs->portsc_bit.nxp_port_speed); // NXP specific port speed
+  }else // device unplugged
+  {
+    usbh_device_unplugged_isr(hostid);
+    regs->usb_cmd_bit.advacne_async = 1; // Async doorbell check EHCI 4.8.2 for operational details
+  }
+}
+
+void async_list_process_isr(ehci_qhd_t * const async_head)
+{
+  uint8_t max_loop = 0;
+  ehci_qhd_t *p_qhd = async_head;
+  do
+  {
+    if ( !p_qhd->qtd_overlay.halted )
+    {
+      // 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)
+      {
+        // TODO check halted TD
+        if (p_qhd->p_qtd_list_head->int_on_complete) // end of request
+        {
+          pipe_handle_t pipe_hdl = { .dev_addr = p_qhd->device_address };
+          if (p_qhd->endpoint_number) // if not Control, can only be Bulk
+          {
+            pipe_hdl.xfer_type = TUSB_XFER_BULK;
+            pipe_hdl.index = qhd_get_index(p_qhd);
+          }
+          usbh_isr( pipe_hdl, p_qhd->class_code, BUS_EVENT_XFER_COMPLETE); // call USBH callback
+        }
+
+        p_qhd->p_qtd_list_head->used = 0; // free QTD
+        qtd_remove_1st_from_qhd(p_qhd);
+      }
+    }
+    p_qhd = (ehci_qhd_t*) align32(p_qhd->next.address);
+    max_loop++;
+  }while(p_qhd != async_head && max_loop <= EHCI_MAX_QHD); // async list traversal, stop if loop around
+  // TODO abstract max loop guard for async
+}
+
+void period_list_process_isr(ehci_qhd_t const * const period_head)
+{
+  uint8_t max_loop = 0;
+  ehci_link_t next_item = period_head->next;
+
+  // TODO abstract max loop guard for period
+  while( !next_item.terminate && max_loop < (EHCI_MAX_QHD + EHCI_MAX_ITD + EHCI_MAX_SITD))
+  {
+    switch ( next_item.type )
+    {
+      case EHCI_QUEUE_ELEMENT_QHD:
+      {
+        ehci_qhd_t *p_qhd_int = (ehci_qhd_t *) align32(next_item.address);
+        if ( !p_qhd_int->qtd_overlay.halted )
+        {
+          // free all TDs from the head td to the first active TD
+          while(p_qhd_int->p_qtd_list_head != NULL && !p_qhd_int->p_qtd_list_head->active)
+          {
+            // TODO check halted TD
+            if (p_qhd_int->p_qtd_list_head->int_on_complete) // end of request
+            {
+              pipe_handle_t pipe_hdl = { .dev_addr = p_qhd_int->device_address };
+              if (p_qhd_int->endpoint_number) // if not Control, can only be Bulk
+              {
+                pipe_hdl.xfer_type = TUSB_XFER_INTERRUPT;
+                pipe_hdl.index = qhd_get_index(p_qhd_int);
+              }
+              usbh_isr( pipe_hdl, p_qhd_int->class_code, BUS_EVENT_XFER_COMPLETE); // call USBH callback
+            }
+
+            p_qhd_int->p_qtd_list_head->used = 0; // free QTD
+            qtd_remove_1st_from_qhd(p_qhd_int);
+          }
+        }
+        next_item = p_qhd_int->next;
+      }
+      break;
+
+      case EHCI_QUEUE_ELEMENT_ITD:
+      case EHCI_QUEUE_ELEMENT_SITD:
+      case EHCI_QUEUE_ELEMENT_FSTN:
+      default:
+        ASSERT (false, (void) 0); // TODO support hs/fs ISO
+      break;
+    }
+    max_loop++;
+  }
+
+}
+
+void xfer_error_isr(uint8_t hostid)
+{
+  //------------- async list -------------//
+  ehci_qhd_t * const async_head = get_async_head(hostid);
+  ehci_qhd_t *p_qhd = async_head;
+  do
+  {
+    // current qhd has error in transaction
+    if (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
+        (p_qhd->device_address != 0 && p_qhd->qtd_overlay.halted) ) // addr0 cannot be protocol STALL
+    {
+      pipe_handle_t pipe_hdl = { .dev_addr = p_qhd->device_address };
+      if (p_qhd->endpoint_number) // if not Control, can only be Bulk
+      {
+        pipe_hdl.xfer_type = TUSB_XFER_BULK;
+        pipe_hdl.index = qhd_get_index(p_qhd);
+      }
+      usbh_isr( pipe_hdl, p_qhd->class_code, BUS_EVENT_XFER_ERROR); // call USBH callback
+    }
+
+    p_qhd = (ehci_qhd_t*) align32(p_qhd->next.address);
+  }while(p_qhd != async_head); // async list traversal, stop if loop around
+
+  //------------- TODO period list -------------//
+}
+
+//------------- Host Controller Driver's Interrupt Handler -------------//
+void hcd_isr(uint8_t hostid)
+{
+  ehci_registers_t* const regs = get_operational_register(hostid);
+
+  uint32_t int_status = regs->usb_sts & regs->usb_int_enable;
+  regs->usb_sts |= int_status; // Acknowledge handled interrupt
+
+  if (int_status == 0)
+    return;
+
+  if (int_status & EHCI_INT_MASK_ERROR)
+  {
+    // TODO handle Queue Head halted
+    hal_debugger_breakpoint();
+    xfer_error_isr(hostid);
+  }
+
+  //------------- some QTD/SITD/ITD with IOC set is completed -------------//
+  if (int_status & EHCI_INT_MASK_NXP_ASYNC)
+  {
+    async_list_process_isr(get_async_head(hostid));
+  }
+
+  if (int_status & EHCI_INT_MASK_NXP_PERIODIC)
+  {
+    period_list_process_isr( get_period_head(hostid) );
+  }
+
+  if (int_status & EHCI_INT_MASK_PORT_CHANGE)
+  {
+    uint32_t port_status = regs->portsc & EHCI_PORTSC_MASK_ALL;
+
+    if (regs->portsc_bit.connect_status_change)
+    {
+      port_connect_status_change_isr(hostid);
+    }
+
+    regs->portsc |= port_status; // Acknowledge change bits in portsc
+  }
+
+  if (int_status & EHCI_INT_MASK_ASYNC_ADVANCE) // need to place after EHCI_INT_MASK_NXP_ASYNC
+  {
+    async_advance_isr( get_async_head(hostid) );
+  }
+}
+
 //--------------------------------------------------------------------+
 // HELPER
 //--------------------------------------------------------------------+

+ 8 - 10
tinyusb/host/ehci/ehci.h

@@ -122,7 +122,13 @@ typedef struct {
 	ehci_link_t next;
 
 	/// Word 1: Alternate Next QTD Pointer (not used)
-	ehci_link_t alternate;
+	union{
+	  ehci_link_t alternate;
+	  struct {
+	    uint32_t      : 5;
+	    uint32_t used : 1;
+	  };
+	};
 
 	/// Word 2: qTQ Token
 	volatile uint32_t pingstate_err               : 1  ; ///< If the QH.EPSfield indicates a High-speed device and the PID_Codeindicates an OUT endpoint, then this is the state bit for the Ping protocol. 0b=OUT 1b=PING
@@ -145,15 +151,7 @@ typedef struct {
 	// End of Word 2
 
 	/// 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
-	union {
-	  uint32_t buffer[5];
-	  struct {
-	    uint32_t reserve_1;
-	    uint8_t  used; // used is the LSB of buffer[1]
-	    uint8_t  reserved_2[3];
-	    uint32_t reserved_3[3];
-	  };
-	};
+	uint32_t buffer[5];
 } ehci_qtd_t; // XXX qtd is used to declare overlay in ehci_qhd_t -> cannot be declared with ATTR_ALIGNED(32)
 
 /// Queue Head (section 3.6)