From d200007a2985ed585a674bccb683b1bc953b2e36 Mon Sep 17 00:00:00 2001 From: Konstantin Oblaukhov Date: Sun, 7 Oct 2018 15:04:33 +0700 Subject: Reorganize KINETIS driver files --- os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.c | 873 +++++++++++++++++++++++++ 1 file changed, 873 insertions(+) create mode 100644 os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.c (limited to 'os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.c') diff --git a/os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.c b/os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.c new file mode 100644 index 0000000..dc1fca9 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.c @@ -0,0 +1,873 @@ +/* + ChibiOS - Copyright (C) 2015 RedoX https://github.com/RedoXyde/ + (C) 2015-2016 flabbergast + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file USBHSv1/hal_usb_lld.c + * @brief KINETIS USB subsystem low level driver source. + * + * @addtogroup USB + * @{ + */ + +#include + +#include "hal.h" + +#if HAL_USE_USB || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief USB0 driver identifier.*/ +#if KINETIS_USB_USE_USB0 || defined(__DOXYGEN__) +USBDriver USBD1; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/** + * @brief IN EP0 state. + */ +USBInEndpointState ep0in; + +/** + * @brief OUT EP0 state. + */ +USBOutEndpointState ep0out; + +/** + * @brief Buffer for the EP0 setup packets. + */ +static uint8_t ep0setup_buffer[8]; + +/** + * @brief EP0 initialization structure. + */ +static const USBEndpointConfig ep0config = { + USB_EP_MODE_TYPE_CTRL, + _usb_ep0setup, + _usb_ep0in, + _usb_ep0out, + 64, + 64, + &ep0in, + &ep0out, + 1, + ep0setup_buffer +}; + +/* + * Buffer Descriptor Table (BDT) + */ + +/* + * Buffer Descriptor (BD) + * */ +typedef struct { + uint32_t desc; + uint8_t* addr; +} bd_t; + +/* + * Buffer Descriptor fields - p.889 + */ +#define BDT_OWN 0x80 +#define BDT_DATA 0x40 +#define BDT_KEEP 0x20 +#define BDT_NINC 0x10 +#define BDT_DTS 0x08 +#define BDT_STALL 0x04 + +#define BDT_DESC(bc, data) (BDT_OWN | BDT_DTS | ((data&0x1)<<6) | ((bc) << 16)) + +/* + * BDT PID - p.891 + */ +#define BDT_PID_OUT 0x01 +#define BDT_PID_IN 0x09 +#define BDT_PID_SETUP 0x0D +#define BDT_TOK_PID(n) (((n)>>2)&0xF) + +/* + * BDT index fields + */ +#define DATA0 0 +#define DATA1 1 + +#define RX 0 +#define TX 1 + +#define EVEN 0 +#define ODD 1 + +#define BDT_INDEX(endpoint, tx, odd) (((endpoint)<<2) | ((tx)<<1) | (odd)) +/* + * Get RX-ed/TX-ed bytes count from BDT entry + */ +#define BDT_BC(n) (((n)>>16)&0x3FF) + +/* The USB-FS needs 2 BDT entry per endpoint direction + * that adds to: 2*2*16 BDT entries for 16 bi-directional EP + */ +static volatile bd_t _bdt[(KINETIS_USB_ENDPOINTS)*2*2] __attribute__((aligned(512))); + +/* FIXME later with dyn alloc + * 16 EP + * 2 directions per EP + * 2 buffer per direction + * => 64 buffers + */ +static uint8_t _usbb[KINETIS_USB_ENDPOINTS*4][64] __attribute__((aligned(4))); +static volatile uint8_t _usbbn=0; +uint8_t* usb_alloc(uint8_t size) +{ + (void)size; + if(_usbbn < (KINETIS_USB_ENDPOINTS)*4) + return _usbb[_usbbn++]; + while(1); /* Should not happen, ever */ +} +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/* Called from locked ISR. */ +void usb_packet_transmit(USBDriver *usbp, usbep_t ep, size_t n) +{ + const USBEndpointConfig *epc = usbp->epc[ep]; + USBInEndpointState *isp = epc->in_state; + + bd_t *bd = (bd_t *)&_bdt[BDT_INDEX(ep, TX, isp->odd_even)]; + + if (n > (size_t)epc->in_maxsize) + n = (size_t)epc->in_maxsize; + + /* Copy from buf to _usbb[] */ + size_t i=0; + for(i=0;iaddr[i] = isp->txbuf[i]; + + /* Update the Buffer status */ + bd->desc = BDT_DESC(n, isp->data_bank); + /* Toggle the odd and data bits for next TX */ + isp->data_bank ^= DATA1; + isp->odd_even ^= ODD; +} + +/* Called from locked ISR. */ +void usb_packet_receive(USBDriver *usbp, usbep_t ep, size_t n) +{ + const USBEndpointConfig *epc = usbp->epc[ep]; + USBOutEndpointState *osp = epc->out_state; + + bd_t *bd = (bd_t *)&_bdt[BDT_INDEX(ep, RX, osp->odd_even)]; + + if (n > (size_t)epc->out_maxsize) + n = (size_t)epc->out_maxsize; + + /* Copy from _usbb[] to buf */ + size_t i=0; + for(i=0;irxbuf[i] = bd->addr[i]; + + /* Update the Buffer status + * Set current buffer to same DATA bank and then toggle. + * Since even/odd buffers are ping-pong and setup re-initialized them + * this should work correctly */ + bd->desc = BDT_DESC(epc->out_maxsize, osp->data_bank); + osp->data_bank ^= DATA1; + usb_lld_start_out(usbp, ep); +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*============================================================================*/ + +#if KINETIS_USB_USE_USB0 || defined(__DOXYGEN__) +/** + * @brief USB interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(KINETIS_USB_IRQ_VECTOR) { + USBDriver *usbp = &USBD1; + uint8_t istat = USB0->ISTAT; + + OSAL_IRQ_PROLOGUE(); + + /* 04 - Bit2 - Start Of Frame token received */ + if(istat & USBx_ISTAT_SOFTOK) { + _usb_isr_invoke_sof_cb(usbp); + USB0->ISTAT = USBx_ISTAT_SOFTOK; + } + + /* 08 - Bit3 - Token processing completed */ + while(istat & USBx_ISTAT_TOKDNE) { + uint8_t stat = USB0->STAT; + uint8_t ep = stat >> 4; + if(ep > KINETIS_USB_ENDPOINTS) { + OSAL_IRQ_EPILOGUE(); + return; + } + const USBEndpointConfig *epc = usbp->epc[ep]; + + /* Get the correct BDT entry */ + uint8_t odd_even = (stat & USBx_STAT_ODD_MASK) >> USBx_STAT_ODD_SHIFT; + uint8_t tx_rx = (stat & USBx_STAT_TX_MASK) >> USBx_STAT_TX_SHIFT; + bd_t *bd = (bd_t*)&_bdt[BDT_INDEX(ep,tx_rx,odd_even)]; + + /* Update the ODD/EVEN state for RX */ + if(tx_rx == RX && epc->out_state != NULL) + epc->out_state->odd_even = odd_even; + + switch(BDT_TOK_PID(bd->desc)) + { + case BDT_PID_SETUP: // SETUP + { + /* Clear receiving in the chibios state machine */ + (usbp)->receiving &= ~1; + /* Call SETUP function (ChibiOS core), which prepares + * for send or receive and releases the buffer + */ + _usb_isr_invoke_setup_cb(usbp, ep); + /* When a setup packet is received, tx is suspended, + * so it needs to be resumed here. + */ + USB0->CTL &= ~USBx_CTL_TXSUSPENDTOKENBUSY; + } break; + case BDT_PID_IN: // IN + { + if(epc->in_state == NULL) + break; + /* Special case for SetAddress for EP0 */ + if(ep == 0 && (((uint16_t)usbp->setup[0]<<8)|usbp->setup[1]) == 0x0500) + { + usbp->address = usbp->setup[2]; + usb_lld_set_address(usbp); + _usb_isr_invoke_event_cb(usbp, USB_EVENT_ADDRESS); + usbp->state = USB_SELECTED; + } + uint16_t txed = BDT_BC(bd->desc); + epc->in_state->txcnt += txed; + if(epc->in_state->txcnt < epc->in_state->txsize) + { + epc->in_state->txbuf += txed; + osalSysLockFromISR(); + usb_packet_transmit(usbp,ep,epc->in_state->txsize - epc->in_state->txcnt); + osalSysUnlockFromISR(); + } + else + { + if(epc->in_cb != NULL) + _usb_isr_invoke_in_cb(usbp,ep); + } + } break; + case BDT_PID_OUT: // OUT + { + if(epc->out_state == NULL) + break; + uint16_t rxed = BDT_BC(bd->desc); + + osalSysLockFromISR(); + usb_packet_receive(usbp,ep,rxed); + osalSysUnlockFromISR(); + if(rxed) + { + epc->out_state->rxbuf += rxed; + + /* Update transaction data */ + epc->out_state->rxcnt += rxed; + epc->out_state->rxsize -= rxed; + epc->out_state->rxpkts -= 1; + + /* The transaction is completed if the specified number of packets + has been received or the current packet is a short packet.*/ + if ((rxed < epc->out_maxsize) || (epc->out_state->rxpkts == 0)) + { + if(epc->out_cb != NULL) + _usb_isr_invoke_out_cb(usbp, ep); + } + } + } break; + default: + break; + } + USB0->ISTAT = USBx_ISTAT_TOKDNE; + istat = USB0->ISTAT; + } + + /* 01 - Bit0 - Valid USB Reset received */ + if(istat & USBx_ISTAT_USBRST) { + _usb_reset(usbp); + USB0->ISTAT = USBx_ISTAT_USBRST; + OSAL_IRQ_EPILOGUE(); + return; + } + + /* 80 - Bit7 - STALL handshake received */ + if(istat & USBx_ISTAT_STALL) { + USB0->ISTAT = USBx_ISTAT_STALL; + } + + /* 02 - Bit1 - ERRSTAT condition triggered */ + if(istat & USBx_ISTAT_ERROR) { + uint8_t err = USB0->ERRSTAT; + USB0->ERRSTAT = err; + USB0->ISTAT = USBx_ISTAT_ERROR; + } + + /* 10 - Bit4 - Constant IDLE on USB bus detected */ + if(istat & USBx_ISTAT_SLEEP) { + /* This seems to fire a few times before the device is + * configured - need to ignore those occurences somehow. */ + /* The other option would be to only activate INTEN_SLEEPEN + * on CONFIGURED event, but that would need to be done in + * user firmware. */ + if(usbp->state == USB_ACTIVE) { + _usb_suspend(usbp); + /* Enable interrupt on resume */ + USB0->INTEN |= USBx_INTEN_RESUMEEN; + } + + // low-power version (check!): + // enable wakeup interrupt on resume USB signaling + // (check that it was a wakeup int with USBx_USBTRC0_USB_RESUME_INT) + //? USB0->USBTRC0 |= USBx_USBTRC0_USBRESMEN + // suspend the USB module + //? USB0->USBCTRL |= USBx_USBCTRL_SUSP; + + USB0->ISTAT = USBx_ISTAT_SLEEP; + } + + /* 20 - Bit5 - Resume - Only allowed in sleep=suspend mode */ + if(istat & USBx_ISTAT_RESUME) { + /* Disable interrupt on resume (should be disabled + * during normal operation according to datasheet). */ + USB0->INTEN &= ~USBx_INTEN_RESUMEEN; + + // low power version (check!): + // desuspend the USB module + //? USB0->USBCTRL &= ~USBx_USBCTRL_SUSP; + // maybe also + //? USB0->CTL = USBx_CTL_USBENSOFEN; + _usb_wakeup(usbp); + USB0->ISTAT = USBx_ISTAT_RESUME; + } + + /* 40 - Bit6 - ATTACH - used */ + + OSAL_IRQ_EPILOGUE(); +} +#endif /* KINETIS_USB_USE_USB0 */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level USB driver initialization. + * + * @notapi + */ +void usb_lld_init(void) { + /* Driver initialization.*/ + usbObjectInit(&USBD1); + +#if KINETIS_USB_USE_USB0 + + /* Set USB clock source to MCGPLLCLK, MCGFLLCLK, USB1 PFD, or IRC48M */ + SIM->SOPT2 |= SIM_SOPT2_USBSRC; + +#if defined(K20x5) || defined(K20x7) || defined(MK66F18) + +#if KINETIS_MCG_MODE == KINETIS_MCG_MODE_FEI + + /* MCGOUTCLK is the SYSCLK frequency, so don't divide for USB clock */ + SIM->CLKDIV2 = SIM_CLKDIV2_USBDIV(0); + +#elif KINETIS_MCG_MODE == KINETIS_MCG_MODE_PEE + +#if !defined(MK66F18) + /* Note: We don't need this for MK66F18, we can use IRC48M clock for USB */ + #define KINETIS_USBCLK_FREQUENCY 48000000UL + uint32_t i,j; + for(i=0; i < 2; i++) { + for(j=0; j < 8; j++) { + if((KINETIS_PLLCLK_FREQUENCY * (i+1)) == (KINETIS_USBCLK_FREQUENCY*(j+1))) { + SIM->CLKDIV2 = i | SIM_CLKDIV2_USBDIV(j); + goto usbfrac_match_found; + } + } + } + usbfrac_match_found: + osalDbgAssert(i<2 && j <8,"USB Init error"); +#endif + +#else /* KINETIS_MCG_MODE == KINETIS_MCG_MODE_PEE */ +#error USB clock setting not implemented for this KINETIS_MCG_MODE +#endif /* KINETIS_MCG_MODE == ... */ + +#if defined(MK66F18) + /* Switch from default MCGPLLCLK to IRC48M for USB */ + SIM->CLKDIV2 = SIM_CLKDIV2_USBDIV(0); + SIM->SOPT2 |= SIM_SOPT2_PLLFLLSEL_SET(3); +#endif + +#elif defined(KL25) || defined (KL26) || defined(KL27) + + /* No extra clock dividers for USB clock */ + +#else /* defined(KL25) || defined (KL26) || defined(KL27) */ +#error USB driver not implemented for your MCU type +#endif + +#endif /* KINETIS_USB_USE_USB0 */ +} + +/** + * @brief Configures and activates the USB peripheral. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_start(USBDriver *usbp) { + if (usbp->state == USB_STOP) { +#if KINETIS_USB_USE_USB0 + if (&USBD1 == usbp) { + /* Clear BDT */ + uint8_t i; + for(i=0;iUSBPHYCTL |= SIM_USBPHYCTL_USBDISILIM; +#endif + + /* Enable Clock */ +#if KINETIS_USB0_IS_USBOTG + SIM->SCGC4 |= SIM_SCGC4_USBOTG; + +#else /* KINETIS_USB0_IS_USBOTG */ + SIM->SCGC4 |= SIM_SCGC4_USBFS; +#endif /* KINETIS_USB0_IS_USBOTG */ + +#if KINETIS_HAS_USB_CLOCK_RECOVERY + USB0->CLK_RECOVER_IRC_EN |= USBx_CLK_RECOVER_IRC_EN_IRC_EN; + USB0->CLK_RECOVER_CTRL |= USBx_CLK_RECOVER_CTRL_CLOCK_RECOVER_EN; +#endif /* KINETIS_HAS_USB_CLOCK_RECOVERY */ + + /* Reset USB module, wait for completion */ + USB0->USBTRC0 |= USBx_USBTRC0_USBRESET; + while ((USB0->USBTRC0 & USBx_USBTRC0_USBRESET)); + + /* Set BDT Address */ + USB0->BDTPAGE1 = ((uint32_t)_bdt) >> 8; + USB0->BDTPAGE2 = ((uint32_t)_bdt) >> 16; + USB0->BDTPAGE3 = ((uint32_t)_bdt) >> 24; + + /* Clear all ISR flags */ + USB0->ISTAT = 0xFF; + USB0->ERRSTAT = 0xFF; +#if KINETIS_USB0_IS_USBOTG + USB0->OTGISTAT = 0xFF; +#endif /* KINETIS_USB0_IS_USBOTG */ + USB0->USBTRC0 |= 0x40; //a hint was given that this is an undocumented interrupt bit + + /* Enable USB */ + USB0->CTL = USBx_CTL_ODDRST | USBx_CTL_USBENSOFEN; + USB0->USBCTRL = 0; + + /* Enable reset interrupt */ + USB0->INTEN |= USBx_INTEN_USBRSTEN; + + /* Enable interrupt in NVIC */ +#if KINETIS_USB0_IS_USBOTG + nvicEnableVector(USB_OTG_IRQn, KINETIS_USB_USB0_IRQ_PRIORITY); +#else /* KINETIS_USB0_IS_USBOTG */ + nvicEnableVector(USB_IRQn, KINETIS_USB_USB0_IRQ_PRIORITY); +#endif /* KINETIS_USB0_IS_USBOTG */ + } +#endif /* KINETIS_USB_USE_USB0 */ + } +} + +/** + * @brief Deactivates the USB peripheral. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_stop(USBDriver *usbp) { + /* TODO: If in ready state then disables the USB clock.*/ + if (usbp->state == USB_STOP) { +#if KINETIS_USB_USE_USB0 + if (&USBD1 == usbp) { +#if KINETIS_USB0_IS_USBOTG + nvicDisableVector(USB_OTG_IRQn); +#else /* KINETIS_USB0_IS_USBOTG */ + nvicDisableVector(USB_IRQn); +#endif /* KINETIS_USB0_IS_USBOTG */ + } +#endif /* KINETIS_USB_USE_USB0 */ + } +} + +/** + * @brief USB low level reset routine. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_reset(USBDriver *usbp) { + // FIXME, dyn alloc + _usbbn = 0; + +#if KINETIS_USB_USE_USB0 + + /* Reset BDT ODD/EVEN bits */ + USB0->CTL = USBx_CTL_ODDRST; + + /* EP0 initialization.*/ + usbp->epc[0] = &ep0config; + usb_lld_init_endpoint(usbp, 0); + + /* Clear all pending interrupts */ + USB0->ERRSTAT = 0xFF; + USB0->ISTAT = 0xFF; + + /* Set the address to zero during enumeration */ + usbp->address = 0; + USB0->ADDR = 0; + + /* Enable other interrupts */ + USB0->ERREN = 0xFF; + USB0->INTEN = USBx_INTEN_TOKDNEEN | + USBx_INTEN_SOFTOKEN | + USBx_INTEN_STALLEN | + USBx_INTEN_ERROREN | + USBx_INTEN_USBRSTEN | + USBx_INTEN_SLEEPEN; + + /* "is this necessary?", Paul from PJRC */ + USB0->CTL = USBx_CTL_USBENSOFEN; +#endif /* KINETIS_USB_USE_USB0 */ +} + +/** + * @brief Sets the USB address. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_set_address(USBDriver *usbp) { + +#if KINETIS_USB_USE_USB0 + USB0->ADDR = usbp->address&0x7F; +#endif /* KINETIS_USB_USE_USB0 */ +} + +/** + * @brief Enables an endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep) { + + if(ep > KINETIS_USB_ENDPOINTS) + return; + + const USBEndpointConfig *epc = usbp->epc[ep]; + uint8_t mask=0; + + if(epc->out_state != NULL) + { + /* OUT Endpoint */ + epc->out_state->odd_even = EVEN; + epc->out_state->data_bank = DATA0; + /* RXe */ + _bdt[BDT_INDEX(ep, RX, EVEN)].desc = BDT_DESC(epc->out_maxsize, DATA0); + _bdt[BDT_INDEX(ep, RX, EVEN)].addr = usb_alloc(epc->out_maxsize); + /* RXo */ + _bdt[BDT_INDEX(ep, RX, ODD)].desc = BDT_DESC(epc->out_maxsize, DATA1); + _bdt[BDT_INDEX(ep, RX, ODD)].addr = usb_alloc(epc->out_maxsize); + /* Enable OUT direction */ + mask |= USBx_ENDPTn_EPRXEN; + } + if(epc->in_state != NULL) + { + /* IN Endpoint */ + epc->in_state->odd_even = EVEN; + epc->in_state->data_bank = DATA0; + /* TXe, not used yet */ + _bdt[BDT_INDEX(ep, TX, EVEN)].desc = 0; + _bdt[BDT_INDEX(ep, TX, EVEN)].addr = usb_alloc(epc->in_maxsize); + /* TXo, not used yet */ + _bdt[BDT_INDEX(ep, TX, ODD)].desc = 0; + _bdt[BDT_INDEX(ep, TX, ODD)].addr = usb_alloc(epc->in_maxsize); + /* Enable IN direction */ + mask |= USBx_ENDPTn_EPTXEN; + } + + /* EPHSHK should be set for CTRL, BULK, INTR not for ISOC*/ + if((epc->ep_mode & USB_EP_MODE_TYPE) != USB_EP_MODE_TYPE_ISOC) + mask |= USBx_ENDPTn_EPHSHK; + /* Endpoint is not a CTRL endpoint, disable SETUP transfers */ + if((epc->ep_mode & USB_EP_MODE_TYPE) != USB_EP_MODE_TYPE_CTRL) + mask |= USBx_ENDPTn_EPCTLDIS; + +#if KINETIS_USB_USE_USB0 + USB0->ENDPT[ep].V = mask; +#endif /* KINETIS_USB_USE_USB0 */ +} + +/** + * @brief Disables all the active endpoints except the endpoint zero. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_disable_endpoints(USBDriver *usbp) { + (void)usbp; + uint8_t i; +#if KINETIS_USB_USE_USB0 + for(i=1;iENDPT[i].V = 0; +#endif /* KINETIS_USB_USE_USB0 */ +} + +/** + * @brief Returns the status of an OUT endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * @return The endpoint status. + * @retval EP_STATUS_DISABLED The endpoint is not active. + * @retval EP_STATUS_STALLED The endpoint is stalled. + * @retval EP_STATUS_ACTIVE The endpoint is active. + * + * @notapi + */ +usbepstatus_t usb_lld_get_status_out(USBDriver *usbp, usbep_t ep) { + (void)usbp; +#if KINETIS_USB_USE_USB0 + if(ep > USB_MAX_ENDPOINTS) + return EP_STATUS_DISABLED; + if(!(USB0->ENDPT[ep].V & (USBx_ENDPTn_EPRXEN))) + return EP_STATUS_DISABLED; + else if(USB0->ENDPT[ep].V & USBx_ENDPTn_EPSTALL) + return EP_STATUS_STALLED; + return EP_STATUS_ACTIVE; +#endif /* KINETIS_USB_USE_USB0 */ +} + +/** + * @brief Returns the status of an IN endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * @return The endpoint status. + * @retval EP_STATUS_DISABLED The endpoint is not active. + * @retval EP_STATUS_STALLED The endpoint is stalled. + * @retval EP_STATUS_ACTIVE The endpoint is active. + * + * @notapi + */ +usbepstatus_t usb_lld_get_status_in(USBDriver *usbp, usbep_t ep) { + (void)usbp; + if(ep > USB_MAX_ENDPOINTS) + return EP_STATUS_DISABLED; +#if KINETIS_USB_USE_USB0 + if(!(USB0->ENDPT[ep].V & (USBx_ENDPTn_EPTXEN))) + return EP_STATUS_DISABLED; + else if(USB0->ENDPT[ep].V & USBx_ENDPTn_EPSTALL) + return EP_STATUS_STALLED; + return EP_STATUS_ACTIVE; +#endif /* KINETIS_USB_USE_USB0 */ +} + +/** + * @brief Reads a setup packet from the dedicated packet buffer. + * @details This function must be invoked in the context of the @p setup_cb + * callback in order to read the received setup packet. + * @pre In order to use this function the endpoint must have been + * initialized as a control endpoint. + * @post The endpoint is ready to accept another packet. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * @param[out] buf buffer where to copy the packet data + * + * @notapi + */ +void usb_lld_read_setup(USBDriver *usbp, usbep_t ep, uint8_t *buf) { + /* Get the BDT entry */ + USBOutEndpointState *os = usbp->epc[ep]->out_state; + bd_t *bd = (bd_t*)&_bdt[BDT_INDEX(ep, RX, os->odd_even)]; + /* Copy the 8 bytes of data */ + uint8_t n; + for (n = 0; n < 8; n++) { + buf[n] = bd->addr[n]; + } + /* Release the buffer + * Setup packet is always DATA0 + * Release the current DATA0 buffer + */ + bd->desc = BDT_DESC(usbp->epc[ep]->out_maxsize,DATA0); + /* If DATA1 was expected, then the states are out of sync. + * So reset the other buffer too, and set it as DATA1. + * This should not happen in normal cases, but is possible in + * error situations. NOTE: it's possible that this is too late + * and the next packet has already been received and dropped, but + * there's nothing that we can do about that anymore at this point. + */ + if (os->data_bank == DATA1) + { + bd_t *bd_next = (bd_t*)&_bdt[BDT_INDEX(ep, RX, os->odd_even^ODD)]; + bd_next->desc = BDT_DESC(usbp->epc[ep]->out_maxsize,DATA1); + } + /* After a SETUP, both in and out are always DATA1 */ + usbp->epc[ep]->in_state->data_bank = DATA1; + os->data_bank = DATA1; +} + +/** + * @brief Starts a receive operation on an OUT endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_start_out(USBDriver *usbp, usbep_t ep) { + USBOutEndpointState *osp = usbp->epc[ep]->out_state; + /* Transfer initialization.*/ + if (osp->rxsize == 0) /* Special case for zero sized packets.*/ + osp->rxpkts = 1; + else + osp->rxpkts = (uint16_t)((osp->rxsize + usbp->epc[ep]->out_maxsize - 1) / + usbp->epc[ep]->out_maxsize); +} + +/** + * @brief Starts a transmit operation on an IN endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @note Called from ISR and locked zone. + * @notapi + */ +void usb_lld_start_in(USBDriver *usbp, usbep_t ep) { + if (ep == 0 && usbp->ep0state == USB_EP0_IN_SENDING_STS) { + /* When a status packet is about to be sent on endpoint 0 the + * next packet will be a setup packet, which means that the + * buffer we expect after this should be DATA0, and the following + * DATA1. Since no out packets should be in flight at this time + * it's safe to initialize the buffers according to the expectations + * here. + */ + const USBEndpointConfig* epc = usbp->epc[ep]; + bd_t * bd = (bd_t*)&_bdt[BDT_INDEX(ep, RX, epc->out_state->odd_even)]; + bd_t *bd_next = (bd_t*)&_bdt[BDT_INDEX(ep, RX, epc->out_state->odd_even^ODD)]; + + bd->desc = BDT_DESC(usbp->epc[ep]->out_maxsize,DATA1); + bd_next->desc = BDT_DESC(usbp->epc[ep]->out_maxsize,DATA0); + epc->out_state->data_bank = DATA0; + } + usb_packet_transmit(usbp,ep,usbp->epc[ep]->in_state->txsize); +} + +/** + * @brief Brings an OUT endpoint in the stalled state. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_stall_out(USBDriver *usbp, usbep_t ep) { + (void)usbp; +#if KINETIS_USB_USE_USB0 + USB0->ENDPT[ep].V |= USBx_ENDPTn_EPSTALL; +#endif /* KINETIS_USB_USE_USB0 */ +} + +/** + * @brief Brings an IN endpoint in the stalled state. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_stall_in(USBDriver *usbp, usbep_t ep) { + (void)usbp; +#if KINETIS_USB_USE_USB0 + USB0->ENDPT[ep].V |= USBx_ENDPTn_EPSTALL; +#endif /* KINETIS_USB_USE_USB0 */ +} + +/** + * @brief Brings an OUT endpoint in the active state. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_clear_out(USBDriver *usbp, usbep_t ep) { + (void)usbp; +#if KINETIS_USB_USE_USB0 + USB0->ENDPT[ep].V &= ~USBx_ENDPTn_EPSTALL; +#endif /* KINETIS_USB_USE_USB0 */ +} + +/** + * @brief Brings an IN endpoint in the active state. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_clear_in(USBDriver *usbp, usbep_t ep) { + (void)usbp; +#if KINETIS_USB_USE_USB0 + USB0->ENDPT[ep].V &= ~USBx_ENDPTn_EPSTALL; +#endif /* KINETIS_USB_USE_USB0 */ +} + +#endif /* HAL_USE_USB */ + +/** @} */ -- cgit v1.2.3