Просмотр исходного кода

add code for ehci interrupt handler
- interrupt source identifying
- for async list process
refractor control_xfer to use insert_qtd_to_qhd
- add test for control xfer interrupt processing
- add code for faking ehci controller async list scheduling & processing
- add prototype for void usbh_isr(pipe_handle_t pipe_hdl, uint8_t class_code);

hathach 13 лет назад
Родитель
Сommit
c1848b8114

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

@@ -43,7 +43,7 @@
 #include "hal.h"
 #include "mock_osal.h"
 #include "hcd.h"
-#include "usbh_hcd.h"
+#include "mock_usbh_hcd.h"
 #include "ehci.h"
 
 extern ehci_data_t ehci_data;

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

@@ -43,7 +43,7 @@
 #include "hal.h"
 #include "mock_osal.h"
 #include "hcd.h"
-#include "usbh_hcd.h"
+#include "mock_usbh_hcd.h"
 #include "ehci.h"
 #include "test_ehci.h"
 

+ 6 - 1
tests/test/host/ehci/test_ehci_pipe_bulk_xfer.c

@@ -43,7 +43,7 @@
 #include "hal.h"
 #include "mock_osal.h"
 #include "hcd.h"
-#include "usbh_hcd.h"
+#include "mock_usbh_hcd.h"
 #include "ehci.h"
 #include "test_ehci.h"
 
@@ -178,3 +178,8 @@ void test_bulk_xfer_double(void)
 
   //------------- list tail -------------//
 }
+
+//void test_bulk_xfer_isr(void)
+//{
+//
+//}

+ 33 - 1
tests/test/host/ehci/test_ehci_pipe_xfer_control.c

@@ -43,9 +43,10 @@
 #include "hal.h"
 #include "mock_osal.h"
 #include "hcd.h"
-#include "usbh_hcd.h"
+#include "mock_usbh_hcd.h"
 #include "ehci.h"
 #include "test_ehci.h"
+#include "ehci_controller.h"
 
 extern ehci_data_t ehci_data;
 usbh_device_info_t usbh_device_info_pool[TUSB_CFG_HOST_DEVICE_MAX+1];
@@ -199,6 +200,9 @@ void test_control_xfer_get(void)
   TEST_ASSERT_TRUE(p_status->data_toggle);
   TEST_ASSERT_EQUAL(EHCI_PID_OUT, p_status->pid);
   TEST_ASSERT_TRUE(p_status->next.terminate);
+
+  TEST_ASSERT_EQUAL_HEX(p_setup, p_qhd->p_qtd_list_head);
+  TEST_ASSERT_EQUAL_HEX(p_status, p_qhd->p_qtd_list_tail);
 }
 
 void test_control_xfer_set(void)
@@ -227,4 +231,32 @@ void test_control_xfer_set(void)
   TEST_ASSERT_TRUE(p_status->data_toggle);
   TEST_ASSERT_EQUAL(EHCI_PID_IN, p_status->pid);
   TEST_ASSERT_TRUE(p_status->next.terminate);
+
+  TEST_ASSERT_EQUAL_HEX(p_setup, p_qhd->p_qtd_list_head);
+  TEST_ASSERT_EQUAL_HEX(p_status, p_qhd->p_qtd_list_tail);
+}
+
+void test_control_xfer_isr(void)
+{
+  ehci_qhd_t * const p_qhd = &ehci_data.device[dev_addr].control.qhd;
+  hcd_pipe_control_open(dev_addr, control_max_packet_size);
+
+  hcd_pipe_control_xfer(dev_addr, &request_get_dev_desc, xfer_data);
+  ehci_controller_run(hostid);
+
+  TEST_ASSERT_EQUAL_HEX(async_head, get_operational_register(hostid)->async_list_base);
+  TEST_ASSERT_EQUAL_HEX((uint32_t) p_qhd, align32(async_head->next.address));
+  usbh_isr_Expect(((pipe_handle_t){.dev_addr = dev_addr}), 0);
+
+  //------------- Code Under TEST -------------//
+  printf("control head = %x\n", p_qhd);
+  hcd_isr(hostid);
+
+  TEST_ASSERT_NULL(p_qhd->p_qtd_list_head);
+  TEST_ASSERT_NULL(p_qhd->p_qtd_list_tail);
+
+  TEST_ASSERT_FALSE(p_setup->used);
+  TEST_ASSERT_FALSE(p_data->used);
+  TEST_ASSERT_FALSE(p_status->used);
+
 }

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

@@ -43,7 +43,7 @@
 #include "hal.h"
 #include "mock_osal.h"
 #include "hcd.h"
-#include "usbh_hcd.h"
+#include "mock_usbh_hcd.h"
 #include "ehci.h"
 
 extern ehci_data_t ehci_data;

+ 78 - 0
tests/test/support/ehci_controller.c

@@ -0,0 +1,78 @@
+/*
+ * ehci_controller.c
+ *
+ *  Created on: Mar 9, 2013
+ *      Author: hathach
+ */
+
+/*
+ * Software License Agreement (BSD License)
+ * Copyright (c) 2012, hathach (tinyusb.net)
+ * 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
+//--------------------------------------------------------------------+
+#include "unity.h"
+#include "tusb_option.h"
+#include "errors.h"
+#include "binary.h"
+#include "hal.h"
+#include "ehci.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+extern ehci_data_t ehci_data;
+
+//--------------------------------------------------------------------+
+// IMPLEMENTATION
+//--------------------------------------------------------------------+
+void ehci_controller_run(uint8_t hostid)
+{
+  //------------- Async List -------------//
+  ehci_registers_t* const regs = get_operational_register(hostid);
+
+  ehci_qhd_t *p_qhd = (ehci_qhd_t*) regs->async_list_base;
+  do
+  {
+    if ( !p_qhd->qtd_overlay.halted )
+    {
+      while(!p_qhd->qtd_overlay.next.terminate)
+      {
+        ehci_qtd_t* p_qtd = (ehci_qtd_t*) align32(p_qhd->qtd_overlay.next.address);
+        p_qtd->active = 0;
+        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
+  //------------- Period List -------------//
+
+  regs->usb_sts = EHCI_INT_MASK_NXP_ASYNC;
+}

+ 66 - 0
tests/test/support/ehci_controller.h

@@ -0,0 +1,66 @@
+/*
+ * ehci_controller.h
+ *
+ *  Created on: Mar 10, 2013
+ *      Author: hathach
+ */
+
+/*
+ * Software License Agreement (BSD License)
+ * Copyright (c) 2012, hathach (tinyusb.net)
+ * 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.
+ */
+
+/** \file
+ *  \brief TBD
+ *
+ *  \note TBD
+ */
+
+/** \ingroup TBD
+ *  \defgroup TBD
+ *  \brief TBD
+ *
+ *  @{
+ */
+
+#ifndef _TUSB_EHCI_CONTROLLER_H_
+#define _TUSB_EHCI_CONTROLLER_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+void ehci_controller_run(uint8_t hostid);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_EHCI_CONTROLLER_H_ */
+
+/** @} */

+ 124 - 42
tinyusb/host/ehci/ehci.c

@@ -134,9 +134,92 @@ tusb_error_t hcd_init(void)
   return TUSB_ERROR_NONE;
 }
 
+//--------------------------------------------------------------------+
+// EHCI Interrupt Handler
+//--------------------------------------------------------------------+
+static inline uint8_t get_qhd_index(ehci_qhd_t * p_qhd) ATTR_ALWAYS_INLINE ATTR_PURE;
+static inline uint8_t get_qhd_index(ehci_qhd_t * p_qhd)
+{
+  return p_qhd - ehci_data.device[p_qhd->device_address].qhd;
+}
+
+void async_list_process_isr(ehci_qhd_t * const async_head, ehci_registers_t * const regs)
+{
+  ehci_qhd_t *p_qhd = async_head;
+  do
+  {
+    if ( p_qhd->qtd_overlay.halted )
+    {
+      // TODO invoke some error callback if not async head
+    } else
+    {
+      // 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 = get_qhd_index(p_qhd);
+          }
+          usbh_isr( pipe_hdl, p_qhd->class_code); // call USBH call back
+        }
+        p_qhd->p_qtd_list_head->used = 0; // free QTD
+
+        if (p_qhd->p_qtd_list_head == p_qhd->p_qtd_list_tail) // last TD --> make it NULL
+        {
+          p_qhd->p_qtd_list_head = p_qhd->p_qtd_list_tail = NULL;
+        }else
+        {
+          p_qhd->p_qtd_list_head = (ehci_qtd_t*) align32(p_qhd->p_qtd_list_head->next.address);
+        }
+      }
+    }
+    p_qhd = (ehci_qhd_t*) align32(p_qhd->next.address);
+  }while(p_qhd != async_head); // stop if loop around
+}
+
+//------------- Host Controller Driver's Interrupt Handler -------------//
+// TODO this isr is not properly go through TDD
 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;
+
+  if (int_status == 0)
+    return;
+
+  if (int_status & EHCI_INT_MASK_ERROR)
+  {
+    // TODO something going wrong
+  }
+
+  //------------- 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), regs);
+  }
+
+  if (int_status & EHCI_INT_MASK_NXP_PERIODIC)
+  {
+
+  }
+
+  if (int_status & EHCI_INT_MASK_PORT_CHANGE)
+  {
+
+  }
 
+  if (int_status & EHCI_INT_MASK_ASYNC_ADVANCE)
+  {
+
+  }
+
+  regs->usb_sts |= regs->usb_sts; // Acknowledge interrupt & clear it
 }
 
 //--------------------------------------------------------------------+
@@ -151,9 +234,10 @@ tusb_error_t hcd_controller_init(uint8_t hostid)
   regs->usb_int_enable = 0;                 // 1. disable all the interrupt
   regs->usb_sts        = EHCI_INT_MASK_ALL; // 2. clear all status
   regs->usb_int_enable =
-      /*EHCI_INT_MASK_USB |*/ EHCI_INT_MASK_ERROR | EHCI_INT_MASK_PORT_CHANGE | EHCI_INT_MASK_ASYNC_ADVANCE
-#if 1 // TODO enable usbint olny
-      | EHCI_INT_MASK_NXP_ASYNC | EHCI_INT_MASK_NXP_PERIODIC
+      EHCI_INT_MASK_ERROR | EHCI_INT_MASK_PORT_CHANGE
+      | EHCI_INT_MASK_ASYNC_ADVANCE | EHCI_INT_MASK_NXP_ASYNC
+#if EHCI_PERIODIC_LIST
+      | EHCI_INT_MASK_NXP_PERIODIC
 #endif
       ;
 
@@ -256,7 +340,27 @@ static inline ehci_qtd_t* get_control_qtds(uint8_t dev_addr) ATTR_ALWAYS_INLINE
 //--------------------------------------------------------------------+
 // CONTROL PIPE API
 //--------------------------------------------------------------------+
+// TODO subject to pure function
+static void init_qtd(ehci_qtd_t* p_qtd, uint32_t data_ptr, uint16_t total_bytes)
+{
+  memclr_(p_qtd, sizeof(ehci_qtd_t));
+
+  p_qtd->used                = 1;
 
+  p_qtd->next.terminate      = 1; // init to null
+  p_qtd->alternate.terminate = 1; // not used, always set to terminated
+  p_qtd->active              = 1;
+  p_qtd->cerr                = 3; // TODO 3 consecutive errors tolerance
+  p_qtd->data_toggle         = 0;
+  p_qtd->total_bytes         = total_bytes;
+
+  p_qtd->buffer[0]           = data_ptr;
+
+  for(uint8_t i=1; i<5; i++)
+  {
+    p_qtd->buffer[i] |= align4k( p_qtd->buffer[i-1] ) + 4096;
+  }
+}
 
 static inline void list_insert(ehci_link_t *current, ehci_link_t *new, uint8_t new_type) ATTR_ALWAYS_INLINE;
 static inline void list_insert(ehci_link_t *current, ehci_link_t *new, uint8_t new_type)
@@ -266,6 +370,20 @@ static inline void list_insert(ehci_link_t *current, ehci_link_t *new, uint8_t n
   current->type = new_type;
 }
 
+static inline void insert_qtd_to_qhd(ehci_qhd_t *p_qhd, ehci_qtd_t *p_qtd_new) ATTR_ALWAYS_INLINE;
+static inline void insert_qtd_to_qhd(ehci_qhd_t *p_qhd, ehci_qtd_t *p_qtd_new)
+{
+  if (p_qhd->p_qtd_list_head == NULL) // empty list
+  {
+    p_qhd->p_qtd_list_head               = p_qhd->p_qtd_list_tail = p_qtd_new;
+    p_qhd->qtd_overlay.next.address      = (uint32_t) p_qhd->p_qtd_list_head;
+  }else
+  {
+    p_qhd->p_qtd_list_tail->next.address = (uint32_t) p_qtd_new;
+    p_qhd->p_qtd_list_tail               = p_qtd_new;
+  }
+}
+
 tusb_error_t  hcd_pipe_control_open(uint8_t dev_addr, uint8_t max_packet_size)
 {
   ehci_qhd_t * const p_qhd = get_control_qhd(dev_addr);
@@ -283,28 +401,6 @@ tusb_error_t  hcd_pipe_control_open(uint8_t dev_addr, uint8_t max_packet_size)
   return TUSB_ERROR_NONE;
 }
 
-// TODO subject to pure function
-static void init_qtd(ehci_qtd_t* p_qtd, uint32_t data_ptr, uint16_t total_bytes)
-{
-  memclr_(p_qtd, sizeof(ehci_qtd_t));
-
-  p_qtd->used                = 1;
-
-  p_qtd->next.terminate      = 1; // init to null
-  p_qtd->alternate.terminate = 1; // not used, always set to terminated
-  p_qtd->active              = 1;
-  p_qtd->cerr                = 3; // TODO 3 consecutive errors tolerance
-  p_qtd->data_toggle         = 0;
-  p_qtd->total_bytes         = total_bytes;
-
-  p_qtd->buffer[0]           = data_ptr;
-
-  for(uint8_t i=1; i<5; i++)
-  {
-    p_qtd->buffer[i] |= align4k( p_qtd->buffer[i-1] ) + 4096;
-  }
-}
-
 tusb_error_t  hcd_pipe_control_xfer(uint8_t dev_addr, tusb_std_request_t const * p_request, uint8_t data[])
 {
   ehci_qhd_t * const p_qhd = get_control_qhd(dev_addr);
@@ -317,6 +413,7 @@ tusb_error_t  hcd_pipe_control_xfer(uint8_t dev_addr, tusb_std_request_t const *
   init_qtd(p_setup, (uint32_t) p_request, 8);
   p_setup->pid          = EHCI_PID_SETUP;
   p_setup->next.address = (uint32_t) p_data;
+  insert_qtd_to_qhd(p_qhd, p_setup);
 
   //------------- DATA Phase -------------//
   if (p_request->wLength > 0)
@@ -324,6 +421,7 @@ tusb_error_t  hcd_pipe_control_xfer(uint8_t dev_addr, tusb_std_request_t const *
     init_qtd(p_data, (uint32_t) data, p_request->wLength);
     p_data->data_toggle = 1;
     p_data->pid         = p_request->bmRequestType.direction ? EHCI_PID_IN : EHCI_PID_OUT;
+    insert_qtd_to_qhd(p_qhd, p_data);
   }else
   {
     p_data = p_setup;
@@ -337,10 +435,7 @@ tusb_error_t  hcd_pipe_control_xfer(uint8_t dev_addr, tusb_std_request_t const *
   p_status->data_toggle     = 1;
   p_status->pid             = p_request->bmRequestType.direction ? EHCI_PID_OUT : EHCI_PID_IN; // reverse direction of data phase
   p_status->next.terminate  = 1;
-
-  //------------- hook TD List to Queue Head -------------//
-  p_qhd->p_qtd_list_head          = p_setup;
-  p_qhd->qtd_overlay.next.address = (uint32_t) p_setup;
+  insert_qtd_to_qhd(p_qhd, p_status);
 
   return TUSB_ERROR_NONE;
 }
@@ -398,19 +493,6 @@ pipe_handle_t hcd_pipe_open(uint8_t dev_addr, tusb_descriptor_endpoint_t const *
   return (pipe_handle_t) { .dev_addr = dev_addr, .xfer_type = p_endpoint_desc->bmAttributes.xfer, .index = index};
 }
 
-static inline void insert_qtd_to_qhd(ehci_qhd_t *p_qhd, ehci_qtd_t *p_qtd_new) ATTR_ALWAYS_INLINE;
-static inline void insert_qtd_to_qhd(ehci_qhd_t *p_qhd, ehci_qtd_t *p_qtd_new)
-{
-  if (p_qhd->p_qtd_list_head == NULL) // empty list
-  {
-    p_qhd->p_qtd_list_head               = p_qhd->p_qtd_list_tail = p_qtd_new;
-    p_qhd->qtd_overlay.next.address      = (uint32_t) p_qhd->p_qtd_list_head;
-  }else
-  {
-    p_qhd->p_qtd_list_tail->next.address = (uint32_t) p_qtd_new;
-    p_qhd->p_qtd_list_tail               = p_qtd_new;
-  }
-}
 
 
 tusb_error_t  hcd_pipe_xfer(pipe_handle_t pipe_hdl, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete)

+ 7 - 0
tinyusb/host/usbh_hcd.h

@@ -60,6 +60,11 @@
 //--------------------------------------------------------------------+
 #include "common/common.h"
 
+#ifdef _TEST_
+#include "hcd.h"
+#include "osal.h"
+#endif
+
 //--------------------------------------------------------------------+
 // USBH
 //--------------------------------------------------------------------+
@@ -103,6 +108,8 @@ typedef struct { // TODO internal structure, re-order members
 
 extern usbh_device_info_t usbh_device_info_pool[TUSB_CFG_HOST_DEVICE_MAX+1]; // including zero-address
 
+void usbh_isr(pipe_handle_t pipe_hdl, uint8_t class_code);
+
 #ifdef __cplusplus
  }
 #endif