627 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			627 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* 
 | |
|  * The MIT License (MIT)
 | |
|  *
 | |
|  * Copyright (c) 2021 Koji Kitayama
 | |
|  *
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining a copy
 | |
|  * of this software and associated documentation files (the "Software"), to deal
 | |
|  * in the Software without restriction, including without limitation the rights
 | |
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | |
|  * copies of the Software, and to permit persons to whom the Software is
 | |
|  * furnished to do so, subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice shall be included in
 | |
|  * all copies or substantial portions of the Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | |
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | |
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | |
|  * THE SOFTWARE.
 | |
|  *
 | |
|  * This file is part of the TinyUSB stack.
 | |
|  */
 | |
| 
 | |
| #include "tusb_option.h"
 | |
| 
 | |
| #if CFG_TUH_ENABLED && ( \
 | |
|       ( CFG_TUSB_MCU == OPT_MCU_MKL25ZXX ) || ( CFG_TUSB_MCU == OPT_MCU_K32L2BXX ) \
 | |
|     )
 | |
| 
 | |
| #include "fsl_device_registers.h"
 | |
| #define KHCI        USB0
 | |
| 
 | |
| #include "host/hcd.h"
 | |
| 
 | |
| //--------------------------------------------------------------------+
 | |
| // MACRO TYPEDEF CONSTANT ENUM DECLARATION
 | |
| //--------------------------------------------------------------------+
 | |
| 
 | |
| enum {
 | |
|   TOK_PID_OUT   = 0x1u,
 | |
|   TOK_PID_IN    = 0x9u,
 | |
|   TOK_PID_SETUP = 0xDu,
 | |
|   TOK_PID_DATA0 = 0x3u,
 | |
|   TOK_PID_DATA1 = 0xbu,
 | |
|   TOK_PID_ACK   = 0x2u,
 | |
|   TOK_PID_STALL = 0xeu,
 | |
|   TOK_PID_NAK   = 0xau,
 | |
|   TOK_PID_BUSTO = 0x0u,
 | |
|   TOK_PID_ERR   = 0xfu,
 | |
| };
 | |
| 
 | |
| typedef struct TU_ATTR_PACKED
 | |
| {
 | |
|   union {
 | |
|     uint32_t head;
 | |
|     struct {
 | |
|       union {
 | |
|         struct {
 | |
|                uint16_t           :  2;
 | |
|           __IO uint16_t tok_pid   :  4;
 | |
|                uint16_t data      :  1;
 | |
|           __IO uint16_t own       :  1;
 | |
|                uint16_t           :  8;
 | |
|         };
 | |
|         struct {
 | |
|                uint16_t           :  2;
 | |
|                uint16_t bdt_stall :  1;
 | |
|                uint16_t dts       :  1;
 | |
|                uint16_t ninc      :  1;
 | |
|                uint16_t keep      :  1;
 | |
|                uint16_t           : 10;
 | |
|         };
 | |
|       };
 | |
|       __IO uint16_t bc : 10;
 | |
|            uint16_t    :  6;
 | |
|     };
 | |
|   };
 | |
|   uint8_t *addr;
 | |
| }buffer_descriptor_t;
 | |
| 
 | |
| TU_VERIFY_STATIC( sizeof(buffer_descriptor_t) == 8, "size is not correct" );
 | |
| 
 | |
| typedef struct TU_ATTR_PACKED
 | |
| {
 | |
|   union {
 | |
|     uint32_t state;
 | |
|     struct {
 | |
|       uint32_t pipenum:16;
 | |
|       uint32_t odd    : 1;
 | |
|       uint32_t        : 0;
 | |
|     };
 | |
|   };
 | |
|   uint8_t *buffer;
 | |
|   uint16_t length;
 | |
|   uint16_t remaining;
 | |
| } endpoint_state_t;
 | |
| 
 | |
| typedef struct TU_ATTR_PACKED
 | |
| {
 | |
|   uint8_t  dev_addr;
 | |
|   uint8_t  ep_addr;
 | |
|   uint16_t max_packet_size;
 | |
|   union {
 | |
|     uint8_t flags;
 | |
|     struct {
 | |
|       uint8_t data : 1;
 | |
|       uint8_t xfer : 2;
 | |
|       uint8_t      : 0;
 | |
|     };
 | |
|   };
 | |
|   uint8_t *buffer;
 | |
|   uint16_t length;
 | |
|   uint16_t remaining;
 | |
| } pipe_state_t;
 | |
| 
 | |
| 
 | |
| typedef struct
 | |
| {
 | |
|   union {
 | |
|     /* [OUT,IN][EVEN,ODD] */
 | |
|     buffer_descriptor_t bdt[2][2];
 | |
|     uint16_t            bda[2*2];
 | |
|   };
 | |
|   endpoint_state_t endpoint[2];
 | |
|   pipe_state_t pipe[HCD_MAX_XFER * 2];
 | |
|   uint32_t     in_progress; /* Bitmap. Each bit indicates that a transfer of the corresponding pipe is in progress */
 | |
|   uint32_t     pending;     /* Bitmap. Each bit indicates that a transfer of the corresponding pipe will be resume the next frame */
 | |
|   bool         need_reset;  /* The device has not been reset after connection. */
 | |
| } hcd_data_t;
 | |
| 
 | |
| //--------------------------------------------------------------------+
 | |
| // INTERNAL OBJECT & FUNCTION DECLARATION
 | |
| //--------------------------------------------------------------------+
 | |
| // BDT(Buffer Descriptor Table) must be 256-byte aligned
 | |
| CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(512) static hcd_data_t _hcd;
 | |
| //CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t _rx_buf[1024];
 | |
| 
 | |
| int find_pipe(uint8_t dev_addr, uint8_t ep_addr)
 | |
| {
 | |
|   /* Find the target pipe */
 | |
|   int num;
 | |
|   for (num = 0; num < HCD_MAX_XFER * 2; ++num) {
 | |
|     pipe_state_t *p = &_hcd.pipe[num];
 | |
|     if ((p->dev_addr == dev_addr) && (p->ep_addr == ep_addr))
 | |
|       return num;
 | |
|   }
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| static int prepare_packets(int pipenum)
 | |
| {
 | |
|   pipe_state_t *pipe      = &_hcd.pipe[pipenum];
 | |
|   unsigned const dir_tx   = tu_edpt_dir(pipe->ep_addr) ? 0 : 1;
 | |
|   endpoint_state_t *ep    = &_hcd.endpoint[dir_tx];
 | |
|   unsigned const odd      = ep->odd;
 | |
|   buffer_descriptor_t *bd = _hcd.bdt[dir_tx];
 | |
|   TU_ASSERT(0 == bd[odd].own, -1);
 | |
| 
 | |
|   // TU_LOG1("  %p dir %d odd %d data %d\n", &bd[odd], dir_tx, odd, pipe->data);
 | |
| 
 | |
|   ep->pipenum = pipenum;
 | |
| 
 | |
|   bd[odd    ].data      = pipe->data;
 | |
|   bd[odd ^ 1].data      = pipe->data ^ 1;
 | |
|   bd[odd ^ 1].own       = 0;
 | |
|   /* reset values for a next transfer */
 | |
| 
 | |
|   int num_tokens = 0; /* The number of prepared packets */
 | |
|   unsigned const mps = pipe->max_packet_size;
 | |
|   unsigned const rem = pipe->remaining;
 | |
|   if (rem > mps) {
 | |
|     /* When total_bytes is greater than the max packet size,
 | |
|      * it prepares to the next transfer to avoid NAK in advance. */
 | |
|     bd[odd ^ 1].bc   = rem >= 2 * mps ? mps: rem - mps;
 | |
|     bd[odd ^ 1].addr = pipe->buffer + mps;
 | |
|     bd[odd ^ 1].own  = 1;
 | |
|     if (dir_tx) ++num_tokens;
 | |
|   }
 | |
|   bd[odd].bc   = rem >= mps ? mps: rem;
 | |
|   bd[odd].addr = pipe->buffer;
 | |
|   __DSB();
 | |
|   bd[odd].own  = 1; /* This bit must be set last */
 | |
|   ++num_tokens;
 | |
|   return num_tokens;
 | |
| }
 | |
| 
 | |
| static int select_next_pipenum(int pipenum)
 | |
| {
 | |
|   unsigned wip  = _hcd.in_progress & ~_hcd.pending;
 | |
|   if (!wip) return -1;
 | |
|   unsigned msk  = TU_GENMASK(31, pipenum);
 | |
|   int      next = __builtin_ctz(wip & msk);
 | |
|   if (next) return next;
 | |
|   msk  = TU_GENMASK(pipenum, 0);
 | |
|   next = __builtin_ctz(wip & msk);
 | |
|   return next;
 | |
| }
 | |
| 
 | |
| /* When transfer is completed, return true. */
 | |
| static bool continue_transfer(int pipenum, buffer_descriptor_t *bd)
 | |
| {
 | |
|   pipe_state_t *pipe = &_hcd.pipe[pipenum];
 | |
|   unsigned const bc  = bd->bc;
 | |
|   unsigned const rem = pipe->remaining - bc;
 | |
| 
 | |
|   pipe->remaining = rem;
 | |
|   if (rem && bc == pipe->max_packet_size) {
 | |
|     int const next_rem = rem - pipe->max_packet_size;
 | |
|     if (next_rem > 0) {
 | |
|       /* Prepare to the after next transfer */
 | |
|       bd->addr += pipe->max_packet_size * 2;
 | |
|       bd->bc    = next_rem > pipe->max_packet_size ? pipe->max_packet_size: next_rem;
 | |
|       __DSB();
 | |
|       bd->own   = 1; /* This bit must be set last */
 | |
|       while (KHCI->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK) ;
 | |
|       KHCI->TOKEN = KHCI->TOKEN; /* Queue the same token as the last */
 | |
|     } else if (TUSB_DIR_IN == tu_edpt_dir(pipe->ep_addr)) { /* IN */
 | |
|       while (KHCI->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK) ;
 | |
|       KHCI->TOKEN = KHCI->TOKEN;
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
|   pipe->data = bd->data ^ 1;
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| static bool resume_transfer(int pipenum)
 | |
| {
 | |
|   int num_tokens = prepare_packets(pipenum);
 | |
|   TU_ASSERT(0 <= num_tokens);
 | |
| 
 | |
|   const unsigned ie = NVIC_GetEnableIRQ(USB0_IRQn);
 | |
|   NVIC_DisableIRQ(USB0_IRQn);
 | |
|   pipe_state_t *pipe = &_hcd.pipe[pipenum];
 | |
| 
 | |
|   unsigned flags = KHCI->ENDPOINT[0].ENDPT & USB_ENDPT_HOSTWOHUB_MASK;
 | |
|   flags |= USB_ENDPT_EPRXEN_MASK | USB_ENDPT_EPTXEN_MASK;
 | |
|   switch (pipe->xfer) {
 | |
|   case TUSB_XFER_CONTROL:
 | |
|     flags |= USB_ENDPT_EPHSHK_MASK;
 | |
|     break;
 | |
|   case TUSB_XFER_ISOCHRONOUS:
 | |
|     flags |= USB_ENDPT_EPCTLDIS_MASK | USB_ENDPT_RETRYDIS_MASK;
 | |
|     break;
 | |
|   default:
 | |
|     flags |= USB_ENDPT_EPHSHK_MASK | USB_ENDPT_EPCTLDIS_MASK | USB_ENDPT_RETRYDIS_MASK;
 | |
|     break;
 | |
|   }
 | |
|   // TU_LOG1("  resume pipenum %d flags %x\n", pipenum, flags);
 | |
| 
 | |
|   KHCI->ENDPOINT[0].ENDPT = flags;
 | |
|   KHCI->ADDR  = (KHCI->ADDR & USB_ADDR_LSEN_MASK) | pipe->dev_addr;
 | |
| 
 | |
|   unsigned const token = tu_edpt_number(pipe->ep_addr) |
 | |
|     ((tu_edpt_dir(pipe->ep_addr) ? TOK_PID_IN: TOK_PID_OUT) << USB_TOKEN_TOKENPID_SHIFT);
 | |
|   do {
 | |
|     while (KHCI->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK) ;
 | |
|     KHCI->TOKEN = token;
 | |
|   } while (--num_tokens);
 | |
|   if (ie) NVIC_EnableIRQ(USB0_IRQn);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| static void suspend_transfer(int pipenum, buffer_descriptor_t *bd)
 | |
| {
 | |
|   pipe_state_t *pipe = &_hcd.pipe[pipenum];
 | |
|   pipe->buffer  = bd->addr;
 | |
|   pipe->data    = bd->data ^ 1;
 | |
|   if ((TUSB_XFER_INTERRUPT == pipe->xfer) ||
 | |
|       (TUSB_XFER_BULK == pipe->xfer)) {
 | |
|     _hcd.pending |= TU_BIT(pipenum);
 | |
|     KHCI->INTEN |= USB_ISTAT_SOFTOK_MASK;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void process_tokdne(uint8_t rhport)
 | |
| {
 | |
|   (void)rhport;
 | |
|   const unsigned s = KHCI->STAT;
 | |
|   KHCI->ISTAT = USB_ISTAT_TOKDNE_MASK; /* fetch the next token if received */
 | |
|   uint8_t const dir_in = (s & USB_STAT_TX_MASK) ? TUSB_DIR_OUT: TUSB_DIR_IN;
 | |
|   unsigned const odd   = (s & USB_STAT_ODD_MASK) ? 1 : 0;
 | |
| 
 | |
|   buffer_descriptor_t *bd = (buffer_descriptor_t *)&_hcd.bda[s];
 | |
|   endpoint_state_t    *ep = &_hcd.endpoint[s >> 3];
 | |
| 
 | |
|   /* fetch status before discarded by the next steps */
 | |
|   const unsigned pid = bd->tok_pid;
 | |
| 
 | |
|   /* reset values for a next transfer */
 | |
|   bd->bdt_stall = 0;
 | |
|   bd->dts       = 1;
 | |
|   bd->ninc      = 0;
 | |
|   bd->keep      = 0;
 | |
|   /* Update the odd variable to prepare for the next transfer */
 | |
|   ep->odd       = odd ^ 1;
 | |
| 
 | |
|   int pipenum = ep->pipenum;
 | |
|   int next_pipenum;
 | |
|   // TU_LOG1("TOKDNE %x PID %x pipe %d\n", s, pid, pipenum);
 | |
| 
 | |
|   xfer_result_t result;
 | |
|   switch (pid) {
 | |
|     default:
 | |
|       if (continue_transfer(pipenum, bd))
 | |
|         return;
 | |
|       result = XFER_RESULT_SUCCESS;
 | |
|       break;
 | |
|     case TOK_PID_NAK:
 | |
|       suspend_transfer(pipenum, bd);
 | |
|       next_pipenum = select_next_pipenum(pipenum);
 | |
|       if (0 <= next_pipenum)
 | |
|         resume_transfer(next_pipenum);
 | |
|       return;
 | |
|     case TOK_PID_STALL:
 | |
|       result = XFER_RESULT_STALLED;
 | |
|       break;
 | |
|     case TOK_PID_ERR: /* mismatch toggle bit */
 | |
|     case TOK_PID_BUSTO:
 | |
|       result = XFER_RESULT_FAILED;
 | |
|       break;
 | |
|   }
 | |
|   _hcd.in_progress  &= ~TU_BIT(pipenum);
 | |
|   pipe_state_t *pipe = &_hcd.pipe[ep->pipenum];
 | |
|   hcd_event_xfer_complete(pipe->dev_addr,
 | |
|                           tu_edpt_addr(KHCI->TOKEN & USB_TOKEN_TOKENENDPT_MASK, dir_in),
 | |
|                           pipe->length - pipe->remaining,
 | |
|                           result, true);
 | |
|   next_pipenum = select_next_pipenum(pipenum);
 | |
|   if (0 <= next_pipenum)
 | |
|     resume_transfer(next_pipenum);
 | |
| }
 | |
| 
 | |
| static void process_attach(uint8_t rhport)
 | |
| {
 | |
|   unsigned ctl = KHCI->CTL;
 | |
|   if (!(ctl & USB_CTL_JSTATE_MASK)) {
 | |
|     /* The attached device is a low speed device. */
 | |
|     KHCI->ADDR = USB_ADDR_LSEN_MASK;
 | |
|     KHCI->ENDPOINT[0].ENDPT = USB_ENDPT_HOSTWOHUB_MASK;
 | |
|   }
 | |
|   hcd_event_device_attach(rhport, true);
 | |
| }
 | |
| 
 | |
| static void process_bus_reset(uint8_t rhport)
 | |
| {
 | |
|   KHCI->ISTAT    = USB_ISTAT_TOKDNE_MASK;
 | |
|   KHCI->USBCTRL &= ~USB_USBCTRL_SUSP_MASK;
 | |
|   KHCI->CTL     &= ~USB_CTL_USBENSOFEN_MASK;
 | |
|   KHCI->ADDR     = 0;
 | |
|   KHCI->ENDPOINT[0].ENDPT = 0;
 | |
| 
 | |
|   hcd_event_device_remove(rhport, true);
 | |
| 
 | |
|   _hcd.in_progress = 0;
 | |
|   _hcd.pending     = 0;
 | |
|   buffer_descriptor_t *bd = &_hcd.bdt[0][0];
 | |
|   for (unsigned i = 0; i < 2; ++i, ++bd) {
 | |
|     bd->head = 0;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /*------------------------------------------------------------------*/
 | |
| /* Host API
 | |
|  *------------------------------------------------------------------*/
 | |
| bool hcd_init(uint8_t rhport)
 | |
| {
 | |
|   (void)rhport;
 | |
| 
 | |
|   KHCI->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
 | |
|   while (KHCI->USBTRC0 & USB_USBTRC0_USBRESET_MASK);
 | |
| 
 | |
|   tu_memclr(&_hcd, sizeof(_hcd));
 | |
|   KHCI->USBTRC0 |= TU_BIT(6); /* software must set this bit to 1 */
 | |
|   KHCI->BDTPAGE1 = (uint8_t)((uintptr_t)_hcd.bdt >>  8);
 | |
|   KHCI->BDTPAGE2 = (uint8_t)((uintptr_t)_hcd.bdt >> 16);
 | |
|   KHCI->BDTPAGE3 = (uint8_t)((uintptr_t)_hcd.bdt >> 24);
 | |
| 
 | |
|   KHCI->USBCTRL &= ~USB_USBCTRL_SUSP_MASK;
 | |
|   KHCI->CTL     |= USB_CTL_ODDRST_MASK;
 | |
|   for (unsigned i = 0; i < 16; ++i) {
 | |
|     KHCI->ENDPOINT[i].ENDPT = 0;
 | |
|   }
 | |
|   KHCI->CTL &= ~USB_CTL_ODDRST_MASK;
 | |
| 
 | |
|   KHCI->SOFTHLD = 74; /* for 64-byte packets */
 | |
|   // KHCI->SOFTHLD = 144; /* for low speed 8-byte packets */
 | |
|   KHCI->CTL     = USB_CTL_HOSTMODEEN_MASK | USB_CTL_SE0_MASK;
 | |
|   KHCI->USBCTRL = USB_USBCTRL_PDE_MASK;
 | |
| 
 | |
|   NVIC_ClearPendingIRQ(USB0_IRQn);
 | |
|   KHCI->INTEN = USB_INTEN_ATTACHEN_MASK | USB_INTEN_TOKDNEEN_MASK |
 | |
|     USB_INTEN_USBRSTEN_MASK | USB_INTEN_ERROREN_MASK | USB_INTEN_STALLEN_MASK;
 | |
|   KHCI->ERREN = 0xff;
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void hcd_int_enable(uint8_t rhport)
 | |
| {
 | |
|   (void)rhport;
 | |
|   NVIC_EnableIRQ(USB0_IRQn);
 | |
| }
 | |
| 
 | |
| void hcd_int_disable(uint8_t rhport)
 | |
| {
 | |
|   (void)rhport;
 | |
|   NVIC_DisableIRQ(USB0_IRQn);
 | |
| }
 | |
| 
 | |
| uint32_t hcd_frame_number(uint8_t rhport)
 | |
| {
 | |
|   (void)rhport;
 | |
|   /* The device must be reset at least once after connection 
 | |
|    * in order to start the frame counter. */
 | |
|   if (_hcd.need_reset) hcd_port_reset(rhport);
 | |
|   uint32_t frmnum = KHCI->FRMNUML;
 | |
|   frmnum |= KHCI->FRMNUMH << 8u;
 | |
|    return frmnum;
 | |
| }
 | |
| 
 | |
| /*--------------------------------------------------------------------+
 | |
|  * Port API
 | |
|  *--------------------------------------------------------------------+ */
 | |
| bool hcd_port_connect_status(uint8_t rhport)
 | |
| {
 | |
|   (void)rhport;
 | |
|   if (KHCI->ISTAT & USB_ISTAT_ATTACH_MASK)
 | |
|     return true;
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| void hcd_port_reset(uint8_t rhport)
 | |
| {
 | |
|   (void)rhport;
 | |
|   KHCI->CTL &= ~USB_CTL_USBENSOFEN_MASK;
 | |
|   KHCI->CTL |= USB_CTL_RESET_MASK;
 | |
|   unsigned cnt = SystemCoreClock / 100;
 | |
|   while (cnt--) __NOP();
 | |
|   KHCI->CTL &= ~USB_CTL_RESET_MASK;
 | |
|   KHCI->CTL |= USB_CTL_USBENSOFEN_MASK;
 | |
|   _hcd.need_reset = false;
 | |
| }
 | |
| 
 | |
| tusb_speed_t hcd_port_speed_get(uint8_t rhport)
 | |
| {
 | |
|   (void)rhport;
 | |
|   tusb_speed_t speed = TUSB_SPEED_FULL;
 | |
|   const unsigned ie = NVIC_GetEnableIRQ(USB0_IRQn);
 | |
|   NVIC_DisableIRQ(USB0_IRQn);
 | |
|   if (KHCI->ADDR & USB_ADDR_LSEN_MASK)
 | |
|     speed = TUSB_SPEED_LOW;
 | |
|   if (ie) NVIC_EnableIRQ(USB0_IRQn);
 | |
|   return speed;
 | |
| }
 | |
| 
 | |
| void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
 | |
| {
 | |
|   (void)rhport;
 | |
|   const unsigned ie = NVIC_GetEnableIRQ(USB0_IRQn);
 | |
|   NVIC_DisableIRQ(USB0_IRQn);
 | |
|   pipe_state_t *p   = &_hcd.pipe[0];
 | |
|   pipe_state_t *end = &_hcd.pipe[HCD_MAX_XFER * 2];
 | |
|   for (;p != end; ++p) {
 | |
|     if (p->dev_addr == dev_addr)
 | |
|       tu_memclr(p, sizeof(*p));
 | |
|   }
 | |
|   if (ie) NVIC_EnableIRQ(USB0_IRQn);
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------------------+
 | |
| // Endpoints API
 | |
| //--------------------------------------------------------------------+
 | |
| bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
 | |
| {
 | |
|   (void)rhport;
 | |
|   // TU_LOG1("SETUP %u\n", dev_addr);
 | |
|   TU_ASSERT(0 == (_hcd.in_progress & TU_BIT(0)));
 | |
| 
 | |
|   int pipenum = find_pipe(dev_addr, 0);
 | |
|   if (pipenum < 0) return false;
 | |
| 
 | |
|   pipe_state_t *pipe = &_hcd.pipe[pipenum];
 | |
|   pipe[0].data       = 0;
 | |
|   pipe[0].buffer     = (uint8_t*)(uintptr_t)setup_packet;
 | |
|   pipe[0].length     = 8;
 | |
|   pipe[0].remaining  = 8;
 | |
|   pipe[1].data       = 1;
 | |
| 
 | |
|   if (1 != prepare_packets(pipenum))
 | |
|     return false;
 | |
| 
 | |
|   _hcd.in_progress |= TU_BIT(pipenum);
 | |
| 
 | |
|   unsigned hostwohub = KHCI->ENDPOINT[0].ENDPT & USB_ENDPT_HOSTWOHUB_MASK;
 | |
|   KHCI->ENDPOINT[0].ENDPT = hostwohub |
 | |
|     USB_ENDPT_EPHSHK_MASK | USB_ENDPT_EPRXEN_MASK | USB_ENDPT_EPTXEN_MASK;
 | |
|   KHCI->ADDR  = (KHCI->ADDR & USB_ADDR_LSEN_MASK) | dev_addr;
 | |
|   while (KHCI->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK) ;
 | |
|   KHCI->TOKEN = (TOK_PID_SETUP << USB_TOKEN_TOKENPID_SHIFT);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
 | |
| {
 | |
|   (void)rhport;
 | |
|   uint8_t const ep_addr = ep_desc->bEndpointAddress;
 | |
|   // TU_LOG1("O %u %x\n", dev_addr, ep_addr);
 | |
|   /* Find a free pipe */
 | |
|   pipe_state_t *p = &_hcd.pipe[0];
 | |
|   pipe_state_t *end = &_hcd.pipe[HCD_MAX_XFER * 2];
 | |
|   if (dev_addr || ep_addr) {
 | |
|     p += 2;
 | |
|     for (; p < end && (p->dev_addr || p->ep_addr); ++p) ;
 | |
|     if (p == end) return false;
 | |
|   }
 | |
|   p->dev_addr        = dev_addr;
 | |
|   p->ep_addr         = ep_addr;
 | |
|   p->max_packet_size = ep_desc->wMaxPacketSize;
 | |
|   p->xfer            = ep_desc->bmAttributes.xfer;
 | |
|   p->data            = 0;
 | |
|   if (!ep_addr) {
 | |
|     /* Open one more pipe for Control IN transfer */
 | |
|     TU_ASSERT(TUSB_XFER_CONTROL == p->xfer);
 | |
|     pipe_state_t *q = p + 1;
 | |
|     TU_ASSERT(!q->dev_addr && !q->ep_addr);
 | |
|     q->dev_addr        = dev_addr;
 | |
|     q->ep_addr         = tu_edpt_addr(0, TUSB_DIR_IN);
 | |
|     q->max_packet_size = ep_desc->wMaxPacketSize;
 | |
|     q->xfer            = ep_desc->bmAttributes.xfer;
 | |
|     q->data            = 1;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| /* The address of buffer must be aligned to 4 byte boundary. And it must be at least 4 bytes long.
 | |
|  * DMA writes data in 4 byte unit */
 | |
| bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen)
 | |
| {
 | |
|   (void)rhport;
 | |
|   // TU_LOG1("X %u %x %x %d\n", dev_addr, ep_addr, (uintptr_t)buffer, buflen);
 | |
| 
 | |
|   int pipenum = find_pipe(dev_addr, ep_addr);
 | |
|   TU_ASSERT(0 <= pipenum);
 | |
| 
 | |
|   TU_ASSERT(0 == (_hcd.in_progress & TU_BIT(pipenum)));
 | |
|   unsigned const ie  = NVIC_GetEnableIRQ(USB0_IRQn);
 | |
|   NVIC_DisableIRQ(USB0_IRQn);
 | |
|   pipe_state_t *pipe = &_hcd.pipe[pipenum];
 | |
|   pipe->buffer       = buffer;
 | |
|   pipe->length       = buflen;
 | |
|   pipe->remaining    = buflen;
 | |
|   _hcd.in_progress  |= TU_BIT(pipenum);
 | |
|   _hcd.pending      |= TU_BIT(pipenum); /* Send at the next Frame */
 | |
|   KHCI->INTEN |= USB_ISTAT_SOFTOK_MASK;
 | |
|   if (ie) NVIC_EnableIRQ(USB0_IRQn);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
 | |
| {
 | |
|   if (!tu_edpt_number(ep_addr)) return true;
 | |
|   int num = find_pipe(dev_addr, ep_addr);
 | |
|   if (num < 0) return false;
 | |
|   pipe_state_t *p = &_hcd.pipe[num];
 | |
|   p->data = 0; /* Reset data toggle */
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| /*--------------------------------------------------------------------+
 | |
|  * ISR
 | |
|  *--------------------------------------------------------------------+*/
 | |
| void hcd_int_handler(uint8_t rhport)
 | |
| {
 | |
|   uint32_t is  = KHCI->ISTAT;
 | |
|   uint32_t msk = KHCI->INTEN;
 | |
| 
 | |
|   // TU_LOG1("S %lx\n", is);
 | |
| 
 | |
|   /* clear disabled interrupts */
 | |
|   KHCI->ISTAT = (is & ~msk & ~USB_ISTAT_TOKDNE_MASK) | USB_ISTAT_SOFTOK_MASK;
 | |
|   is &= msk;
 | |
| 
 | |
|   if (is & USB_ISTAT_ERROR_MASK) {
 | |
|     unsigned err = KHCI->ERRSTAT;
 | |
|     if (err) {
 | |
|       TU_LOG1(" ERR %x\n", err);
 | |
|       KHCI->ERRSTAT = err;
 | |
|     } else {
 | |
|       KHCI->INTEN &= ~USB_ISTAT_ERROR_MASK;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (is & USB_ISTAT_USBRST_MASK) {
 | |
|     KHCI->INTEN = (msk & ~USB_INTEN_USBRSTEN_MASK) | USB_INTEN_ATTACHEN_MASK;
 | |
|     process_bus_reset(rhport);
 | |
|     return;
 | |
|   }
 | |
|   if (is & USB_ISTAT_ATTACH_MASK) {
 | |
|     KHCI->INTEN = (msk & ~USB_INTEN_ATTACHEN_MASK) | USB_INTEN_USBRSTEN_MASK;
 | |
|     _hcd.need_reset = true;
 | |
|     process_attach(rhport);
 | |
|     return;
 | |
|   }
 | |
|   if (is & USB_ISTAT_STALL_MASK) {
 | |
|     KHCI->ISTAT = USB_ISTAT_STALL_MASK;
 | |
|   }
 | |
|   if (is & USB_ISTAT_SOFTOK_MASK) {
 | |
|     msk &= ~USB_ISTAT_SOFTOK_MASK;
 | |
|     KHCI->INTEN = msk;
 | |
|     if (_hcd.pending) {
 | |
|       int pipenum = __builtin_ctz(_hcd.pending);
 | |
|       _hcd.pending = 0;
 | |
|       if (!(is & USB_ISTAT_TOKDNE_MASK))
 | |
|         resume_transfer(pipenum);
 | |
|     }
 | |
|   }
 | |
|   if (is & USB_ISTAT_TOKDNE_MASK) {
 | |
|     process_tokdne(rhport);
 | |
|   }
 | |
| }
 | |
| 
 | |
| #endif
 | 
