/***************************************************************************//** * @brief * Reset stall state on a stalled (halted) endpoint. * * @param[in] epAddr * The address of the endpoint to un-stall. * * @return * @ref USB_STATUS_OK on success, else an appropriate error code. ******************************************************************************/ int USBD_UnStallEp( int epAddr ) { USB_Status_TypeDef retVal; USBD_Ep_TypeDef *ep = USBD_GetEpFromAddr( epAddr ); if ( ep == NULL ) { DEBUG_USB_API_PUTS( "\nUSBD_UnStallEp(), Illegal request" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } if ( ep->num == 0 ) { DEBUG_USB_API_PUTS( "\nUSBD_UnStallEp(), Illegal endpoint" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } INT_Disable(); retVal = USBDHAL_UnStallEp( ep ); INT_Enable(); if ( retVal != USB_STATUS_OK ) { retVal = USB_STATUS_ILLEGAL; } return retVal; }
/***************************************************************************//** * @brief * Reset stall state on a stalled (halted) endpoint. * * @param[in] epAddr * The address of the endpoint to un-stall. * * @return * @ref USB_STATUS_OK on success, else an appropriate error code. ******************************************************************************/ int USBD_UnStallEp( int epAddr ) { USB_Status_TypeDef retVal; CORE_DECLARE_IRQ_STATE; USBD_Ep_TypeDef *ep = USBD_GetEpFromAddr( epAddr ); if ( ep == NULL ) { DEBUG_USB_API_PUTS( "\nUSBD_UnStallEp(), Illegal request" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } if ( ep->num == 0 ) { DEBUG_USB_API_PUTS( "\nUSBD_UnStallEp(), Illegal endpoint" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } CORE_ENTER_CRITICAL(); retVal = USBDHAL_UnStallEp( ep ); CORE_EXIT_CRITICAL(); if ( retVal != USB_STATUS_OK ) { retVal = USB_STATUS_ILLEGAL; } return retVal; }
/***************************************************************************//** * @brief * Abort a pending transfer on a specific endpoint. * * @param[in] epAddr * The address of the endpoint to abort. ******************************************************************************/ int USBD_AbortTransfer( int epAddr ) { USB_XferCompleteCb_TypeDef callback; USBD_Ep_TypeDef *ep = USBD_GetEpFromAddr( epAddr ); CORE_DECLARE_IRQ_STATE; if ( ep == NULL ) { DEBUG_USB_API_PUTS( "\nUSBD_AbortTransfer(), Illegal endpoint" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } if ( ep->num == 0 ) { DEBUG_USB_API_PUTS( "\nUSBD_AbortTransfer(), Illegal endpoint" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } CORE_ENTER_CRITICAL(); if ( ep->state == D_EP_IDLE ) { CORE_EXIT_CRITICAL(); return USB_STATUS_OK; } USBD_AbortEp( ep ); ep->state = D_EP_IDLE; if ( ep->xferCompleteCb ) { callback = ep->xferCompleteCb; ep->xferCompleteCb = NULL; if ( ( dev->lastState == USBD_STATE_CONFIGURED ) && ( dev->state == USBD_STATE_ADDRESSED ) ) { USBDHAL_DeactivateEp( ep ); } DEBUG_TRACE_ABORT( USB_STATUS_EP_ABORTED ); callback( USB_STATUS_EP_ABORTED, ep->xferred, ep->remaining ); } CORE_EXIT_CRITICAL(); return USB_STATUS_OK; }
/**************************************************************************//** * @brief Initialize MSD device. * * @param[in] activityLedPort * Specify a GPIO port for a LED activity indicator (i.e. enum gpioPortX) * Pass -1 if no indicator LED is available. * * @param[in] activityLedPin * Pin number on activityLedPort for the LED activity indicator. *****************************************************************************/ void MSDD_Init(int activityLedPort, uint32_t activityLedPin) { if ( ( sizeof(MSDSCSI_Read10_TypeDef) != SCSI_READ10_LEN ) || ( sizeof(MSDSCSI_Write10_TypeDef) != SCSI_WRITE10_LEN ) || ( sizeof(MSDSCSI_Verify10_TypeDef) != SCSI_VERIFY10_LEN ) || ( sizeof(MSDSCSI_RequestSense_TypeDef) != SCSI_REQUESTSENSE_LEN ) || ( sizeof(InquiryData) != SCSI_INQUIRYDATA_LEN ) || ( sizeof(NoSenseData) != SCSI_REQUESTSENSEDATA_LEN ) || ( sizeof(IllegalSenseData) != SCSI_REQUESTSENSEDATA_LEN ) || ( sizeof(MSDSCSI_ReadCapacity_TypeDef) != SCSI_READCAPACITY_LEN ) || ( sizeof(MSDSCSI_ReadCapacityData_TypeDef) != SCSI_READCAPACITYDATA_LEN ) || ( sizeof(MSDSCSI_StartStopUnit_TypeDef) != SCSI_STARTSTOPUNIT_LEN ) ) { DEBUG_USB_API_PUTS("\nMSDD_Init(), typedef size error"); EFM_ASSERT(false); return; } if ( ( activityLedPort >= gpioPortA ) && ( activityLedPort <= gpioPortF ) ) ledPort = activityLedPort; else ledPort = -1; ledPin = activityLedPin; msdState = MSDD_IDLE; pSenseData = (MSDSCSI_RequestSenseData_TypeDef*) &NoSenseData; if ( ledPort != -1 ) { CMU_ClockEnable(cmuClock_GPIO, true); GPIO_PinModeSet((GPIO_Port_TypeDef)ledPort, ledPin, gpioModePushPull, 0); } }
/***************************************************************************//** * @brief * Check if an endpoint is busy doing a transfer. * * @param[in] epAddr * The address of the endpoint to check. * * @return * True if endpoint is busy, false otherwise. ******************************************************************************/ bool USBD_EpIsBusy( int epAddr ) { USBD_Ep_TypeDef *ep = USBD_GetEpFromAddr( epAddr ); if ( ep == NULL ) { DEBUG_USB_API_PUTS( "\nUSBD_EpIsBusy(), Illegal endpoint" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } if ( ep->state == D_EP_IDLE ) return false; return true; }
/***************************************************************************//** * @brief * Perform a remote wakeup signalling sequence. * * @note * It is the responsibility of the application to ensure that remote wakeup * is not attempted before the device has been suspended for at least 5 * miliseconds. This function should not be called from within an interrupt * handler. * * @return * @ref USB_STATUS_OK on success, else an appropriate error code. ******************************************************************************/ int USBD_RemoteWakeup( void ) { INT_Disable(); if ( ( dev->state != USBD_STATE_SUSPENDED ) || ( dev->remoteWakeupEnabled == false ) ) { INT_Enable(); DEBUG_USB_API_PUTS( "\nUSBD_RemoteWakeup(), Illegal remote wakeup" ); return USB_STATUS_ILLEGAL; } USBDHAL_SetRemoteWakeup(); INT_Enable(); USBTIMER_DelayMs( 10 ); INT_Disable(); USBDHAL_ClearRemoteWakeup(); INT_Enable(); return USB_STATUS_OK; }
/***************************************************************************//** * @brief * MSDSCSI module initialization. * * @param[in] out * Pointer to an MSD bulk OUT endpoint structure. * * @param[in] in * Pointer to an MSD bulk IN endpoint structure. * * @return * Returns true on success, false otherwise. ******************************************************************************/ bool MSDSCSI_Init(USBH_Ep_TypeDef *out, USBH_Ep_TypeDef *in) { /* Check if all typedef's are properly packed. */ if ((sizeof(MSDSCSI_Read10_TypeDef) != SCSI_READ10_LEN) || (sizeof(MSDSCSI_Write10_TypeDef) != SCSI_WRITE10_LEN) || (sizeof(MSDSCSI_InquiryData_TypeDef) != SCSI_INQUIRYDATA_LEN) || (sizeof(MSDSCSI_RequestSenseData_TypeDef) != SCSI_REQUESTSENSEDATA_LEN) || (sizeof(MSDSCSI_ReadCapacityData_TypeDef) != SCSI_READCAPACITYDATA_LEN)) { DEBUG_USB_API_PUTS("\nMSDSCSI_Init(), typedef size error"); EFM_ASSERT(false); return false; } /* Initialize the Bulk-Only-Transport (BOT) module. */ if (MSDBOT_Init(out, in) != MSDBOT_STATUS_OK) return false; return true; }
/**************************************************************************//** * @brief Initialize MSD device. * * @param[in] activityLedPort * Specify a GPIO port for a LED activity indicator (i.e. enum gpioPortX) * Pass -1 if no indicator LED is available. * * @param[in] activityLedPin * Pin number on activityLedPort for the LED activity indicator. *****************************************************************************/ void MSDD_Init(int activityLedPort, uint32_t activityLedPin) { if ((sizeof(MSDSCSI_Read10_TypeDef) != SCSI_READ10_LEN) || (sizeof(MSDSCSI_Write10_TypeDef) != SCSI_WRITE10_LEN) || (sizeof(MSDSCSI_RequestSense_TypeDef) != SCSI_REQUESTSENSE_LEN) || (sizeof(InquiryData) != SCSI_INQUIRYDATA_LEN) || (sizeof(NoSenseData) != SCSI_REQUESTSENSEDATA_LEN) || (sizeof(IllegalSenseData) != SCSI_REQUESTSENSEDATA_LEN) || (sizeof(MSDSCSI_ReadCapacity_TypeDef) != SCSI_READCAPACITY_LEN) || (sizeof(MSDSCSI_ReadCapacityData_TypeDef) != SCSI_READCAPACITYDATA_LEN)) { DEBUG_USB_API_PUTS("\nMSDD_Init(), typedef size error"); EFM_ASSERT(false); return; } if ( ( activityLedPort >= gpioPortA ) && ( activityLedPort <= gpioPortF ) ) ledPort = activityLedPort; else ledPort = -1; ledPin = activityLedPin; msdState = MSDD_IDLE; pSenseData = (MSDSCSI_RequestSenseData_TypeDef*) &NoSenseData; USBD_Init(&initstruct); /* Start USB. */ if ( ledPort != -1 ) { CMU_ClockEnable(cmuClock_GPIO, true); GPIO_PinModeSet((GPIO_Port_TypeDef)ledPort, ledPin, gpioModePushPull, 0); } /* * When using a debugger it is pratical to uncomment the following three * lines to force host to re-enumerate the device. */ /* USBD_Disconnect(); */ /* USBTIMER_DelayMs( 1000 ); */ /* USBD_Connect(); */ }
/***************************************************************************//** * @brief * MSDBOT module initialization. * * @param[in] out * Pointer to an MSD bulk OUT endpoint structure. * * @param[in] in * Pointer to an MSD bulk IN endpoint structure. * * @return * @ref MSDBOT_STATUS_OK on success, else @ref MSDBOT_INIT_ERROR. ******************************************************************************/ int MSDBOT_Init(USBH_Ep_TypeDef *out, USBH_Ep_TypeDef *in) { /* Check if typedef's are properly packed. */ if ((sizeof(MSDBOT_CBW_TypeDef) != CBW_LEN) || (sizeof(MSDBOT_CSW_TypeDef) != CSW_LEN)) { DEBUG_USB_API_PUTS("\nMSDBOT_Init(), typedef size error"); EFM_ASSERT(false); return MSDBOT_INIT_ERROR; } /* Keep a local copy of bulk IN/OUT endpoint handles. */ epOut = out; epIn = in; /* Transfer timeouts are scaled according to device bus speed. */ if (epIn->parentDevice->speed == PORT_FULL_SPEED) timeoutFactor = 512; else timeoutFactor = 64; return MSDBOT_STATUS_OK; }
/***************************************************************************//** * @brief * Perform a remote wakeup signalling sequence. * * @note * It is the responsibility of the application to ensure that remote wakeup * is not attempted before the device has been suspended for at least 5 * miliseconds. This function should not be called from within an interrupt * handler. * * @return * @ref USB_STATUS_OK on success, else an appropriate error code. ******************************************************************************/ int USBD_RemoteWakeup( void ) { CORE_DECLARE_IRQ_STATE; CORE_ENTER_CRITICAL(); if ( ( dev->state != USBD_STATE_SUSPENDED ) || ( dev->remoteWakeupEnabled == false ) ) { CORE_EXIT_CRITICAL(); DEBUG_USB_API_PUTS( "\nUSBD_RemoteWakeup(), Illegal remote wakeup" ); return USB_STATUS_ILLEGAL; } USBDHAL_SetRemoteWakeup(); CORE_EXIT_CRITICAL(); USBTIMER_DelayMs( 10 ); CORE_ENTER_CRITICAL(); USBDHAL_ClearRemoteWakeup(); CORE_EXIT_CRITICAL(); return USB_STATUS_OK; }
/***************************************************************************//** * @brief * Add a new endpoint * * @param[in] epAddr * Endpoint address * * @param[in] transferType * Endpoint type, one of @ref USB_EPTYPE_BULK, @ref USB_EPTYPE_INTR or * @ref USB_EPTYPE_ISOC. * * @param[in] maxPacketSize * Maximum packet size of the new endpoint, in bytes * * @param[in] bufferMult * FIFO buffer size multiplier * * @return * @ref USB_STATUS_OK on success, else an appropriate error code. ******************************************************************************/ int USBD_AddEndpoint(int epAddr, int transferType, int maxPacketSize, int bufferMult) { CORE_DECLARE_IRQ_STATE; USBD_Ep_TypeDef *ep; numEps++; ep = &dev->ep[ numEps ]; ep->in = ( epAddr & USB_SETUP_DIR_MASK ) != 0; ep->buf = NULL; ep->addr = epAddr; ep->num = ep->addr & USB_EPNUM_MASK; ep->mask = 1 << ep->num; ep->type = transferType; ep->packetSize = maxPacketSize; ep->remaining = 0; ep->xferred = 0; ep->state = D_EP_IDLE; ep->xferCompleteCb = NULL; if ( ep->in ) { ep->txFifoNum = txFifoNum++; ep->fifoSize = ( ( ep->packetSize + 3 ) / 4 ) * bufferMult; dev->inEpAddr2EpIndex[ ep->num ] = numEps; totalTxFifoSize += ep->fifoSize; if ( ep->num > MAX_NUM_IN_EPS ) { DEBUG_USB_API_PUTS( "\nUSBD_AddEndpoint(), Illegal IN EP address" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } } else { ep->fifoSize = ( ( ( ep->packetSize + 3 ) / 4 ) + 1 ) * bufferMult; dev->outEpAddr2EpIndex[ ep->num ] = numEps; totalRxFifoSize += ep->fifoSize; if ( ep->num > MAX_NUM_OUT_EPS ) { DEBUG_USB_API_PUTS( "\nUSBD_AddEndpoint(), Illegal OUT EP address" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } } USB_PRINTF("USBD: Added endpoint %d to slot %d, in %d, addr 0x%x, type %d, ps %d, fifo %ld (total tx %ld, rx %ld)\n", ep->num, numEps, ep->in, ep->addr, ep->type, ep->packetSize, ep->fifoSize, totalTxFifoSize, totalRxFifoSize); CORE_ENTER_CRITICAL(); #if defined( CMU_OSCENCMD_USHFRCOEN ) /* Happy Gecko workaround: disable LEM GATE mode if using ISOC endpoints. */ if ( transferType == USB_EPTYPE_ISOC ) { USB->CTRL = (USB->CTRL & ~_USB_CTRL_LEMOSCCTRL_MASK) | USB_CTRL_LEMOSCCTRL_NONE; } #endif int ret = USBDHAL_ReconfigureFifos(totalRxFifoSize, totalTxFifoSize); CORE_EXIT_CRITICAL(); if( ret != USB_STATUS_OK ) { return ret; } USBDHAL_ActivateEp(ep, false); return USB_STATUS_OK; }
/***************************************************************************//** * @brief * Start a write (IN) transfer on an endpoint. * * @param[in] epAddr * Endpoint address. * * @param[in] data * Pointer to transfer data buffer. This buffer must be WORD (4 byte) aligned. * * @param[in] byteCount * Transfer length. * * @param[in] callback * Function to be called on transfer completion. Supply NULL if no callback * is needed. See @ref USB_XferCompleteCb_TypeDef. * * @return * @ref USB_STATUS_OK on success, else an appropriate error code. ******************************************************************************/ int USBD_Write( int epAddr, void *data, int byteCount, USB_XferCompleteCb_TypeDef callback ) { USBD_Ep_TypeDef *ep = USBD_GetEpFromAddr( epAddr ); CORE_DECLARE_IRQ_STATE; USB_PRINTF("USBD: Write addr %x, data %p, size %d, cb 0x%lx\n", epAddr, data, byteCount, (uint32_t)callback); if ( ep == NULL ) { DEBUG_USB_API_PUTS( "\nUSBD_Write(), Illegal endpoint" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } if ( ( byteCount > MAX_XFER_LEN ) || ( ( byteCount / ep->packetSize ) > MAX_PACKETS_PR_XFER ) ) { DEBUG_USB_API_PUTS( "\nUSBD_Write(), Illegal transfer size" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } if ( (uint32_t)data & 3 ) { DEBUG_USB_API_PUTS( "\nUSBD_Write(), Misaligned data buffer" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } CORE_ENTER_CRITICAL(); if ( USBDHAL_EpIsStalled( ep ) ) { CORE_EXIT_CRITICAL(); DEBUG_USB_API_PUTS( "\nUSBD_Write(), Endpoint is halted" ); return USB_STATUS_EP_STALLED; } if ( ep->state != D_EP_IDLE ) { CORE_EXIT_CRITICAL(); DEBUG_USB_API_PUTS( "\nUSBD_Write(), Endpoint is busy" ); return USB_STATUS_EP_BUSY; } if ( ( ep->num > 0 ) && ( USBD_GetUsbState() != USBD_STATE_CONFIGURED ) ) { CORE_EXIT_CRITICAL(); DEBUG_USB_API_PUTS( "\nUSBD_Write(), Device not configured" ); return USB_STATUS_DEVICE_UNCONFIGURED; } ep->buf = (uint8_t*)data; ep->remaining = byteCount; ep->xferred = 0; if ( ep->num == 0 ) { ep->in = true; } else if ( ep->in != true ) { CORE_EXIT_CRITICAL(); DEBUG_USB_API_PUTS( "\nUSBD_Write(), Illegal EP direction" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } ep->state = D_EP_TRANSMITTING; ep->xferCompleteCb = callback; USBD_ArmEp( ep ); CORE_EXIT_CRITICAL(); return USB_STATUS_OK; }
/***************************************************************************//** * @brief * Initializes USB device hardware and internal protocol stack data structures, * then connects the data-line (D+ or D-) pullup resistor to signal host that * enumeration can begin. * * @note * You may later use @ref USBD_Disconnect() and @ref USBD_Connect() to force * reenumeration. * * @param[in] p * Pointer to device initialization struct. See @ref USBD_Init_TypeDef. * * @return * @ref USB_STATUS_OK on success, else an appropriate error code. ******************************************************************************/ int USBD_Init( const USBD_Init_TypeDef *p ) { USBD_Ep_TypeDef *ep; CORE_DECLARE_IRQ_STATE; #if !defined( USB_CORECLK_HFRCO ) || !defined( CMU_OSCENCMD_USHFRCOEN ) /* Devices supporting crystal-less USB can use HFRCO or HFXO as core clock. */ /* All other devices must use HFXO as core clock. */ if ( CMU_ClockSelectGet( cmuClock_HF ) != cmuSelect_HFXO ) { CMU_ClockSelectSet( cmuClock_HF, cmuSelect_HFXO ); } #endif #if !defined( CMU_OSCENCMD_USHFRCOEN ) #if ( USB_USBC_32kHz_CLK == USB_USBC_32kHz_CLK_LFXO ) CMU_OscillatorEnable(cmuOsc_LFXO, true, false); #else CMU_OscillatorEnable(cmuOsc_LFRCO, true, false); #endif #else CMU_ClockEnable(cmuClock_CORELE, true); /* LFC clock is needed to detect USB suspend when LEMIDLE is activated. */ #if ( USB_USBC_32kHz_CLK == USB_USBC_32kHz_CLK_LFXO ) CMU_ClockSelectSet(cmuClock_LFC, cmuSelect_LFXO); #else CMU_ClockSelectSet(cmuClock_LFC, cmuSelect_LFRCO); #endif CMU_ClockEnable(cmuClock_USBLE, true); #endif USBTIMER_Init(); memset( dev, 0, sizeof( USBD_Device_TypeDef ) ); dev->setup = dev->setupPkt; dev->state = USBD_STATE_LASTMARKER; dev->savedState = USBD_STATE_NONE; dev->lastState = USBD_STATE_NONE; dev->callbacks = p->callbacks; dev->remoteWakeupEnabled = false; /* Initialize EP0 */ ep = &dev->ep[ 0 ]; ep->in = false; ep->buf = NULL; ep->num = 0; ep->mask = 1; ep->addr = 0; ep->type = USB_EPTYPE_CTRL; ep->txFifoNum = 0; /* FIXME! */ ep->packetSize = 64; dev->ep0MpsCode = _USB_DOEP0CTL_MPS_64B; ep->remaining = 0; ep->xferred = 0; ep->state = D_EP_IDLE; ep->xferCompleteCb = NULL; ep->fifoSize = ep->packetSize / 4; totalTxFifoSize = ep->fifoSize * p->bufferingMultiplier[ 0 ]; totalRxFifoSize = (ep->fifoSize + 1) * p->bufferingMultiplier[ 0 ]; /* Rx-FIFO size: SETUP packets : 4*n + 6 n=#CTRL EP's * GOTNAK : 1 * Status info : 2*n n=#OUT EP's (EP0 included) in HW */ totalRxFifoSize += 10 + 1 + ( 2 * (MAX_NUM_OUT_EPS + 1) ); CORE_ENTER_CRITICAL(); /* Enable USB clock */ CMU->HFCORECLKEN0 |= CMU_HFCORECLKEN0_USB | CMU_HFCORECLKEN0_USBC; #if defined( CMU_OSCENCMD_USHFRCOEN ) CMU->USHFRCOCONF = CMU_USHFRCOCONF_BAND_48MHZ; CMU_ClockSelectSet( cmuClock_USBC, cmuSelect_USHFRCO ); /* Enable USHFRCO Clock Recovery mode. */ CMU->USBCRCTRL |= CMU_USBCRCTRL_EN; /* Turn on Low Energy Mode (LEM) features. */ USB->CTRL = USB_CTRL_LEMOSCCTRL_GATE | USB_CTRL_LEMIDLEEN | USB_CTRL_LEMPHYCTRL; #else CMU_ClockSelectSet( cmuClock_USBC, cmuSelect_HFCLK ); #endif USBHAL_DisableGlobalInt(); if ( USBDHAL_CoreInit( totalRxFifoSize, totalTxFifoSize ) == USB_STATUS_OK ) { USBDHAL_EnableUsbResetAndSuspendInt(); USBHAL_EnableGlobalInt(); NVIC_ClearPendingIRQ( USB_IRQn ); NVIC_EnableIRQ( USB_IRQn ); } else { CORE_EXIT_CRITICAL(); DEBUG_USB_API_PUTS( "\nUSBD_Init(), FIFO setup error" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } #if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF ) if ( USBHAL_VbusIsOn() ) { USBD_SetUsbState( USBD_STATE_POWERED ); } else #endif { USBD_SetUsbState( USBD_STATE_NONE ); } CORE_EXIT_CRITICAL(); return USB_STATUS_OK; }
/***************************************************************************//** * @brief * Start a read (OUT) transfer on an endpoint. * * @note * The transfer buffer length must be a multiple of 4 bytes in length and * WORD (4 byte) aligned. When allocating the buffer, round buffer length up. * If it is possible that the host will send more data than your device * expects, round buffer size up to the next multiple of maxpacket size. * * @param[in] epAddr * Endpoint address. * * @param[in] data * Pointer to transfer data buffer. * * @param[in] byteCount * Transfer length. * * @param[in] callback * Function to be called on transfer completion. Supply NULL if no callback * is needed. See @ref USB_XferCompleteCb_TypeDef. * * @return * @ref USB_STATUS_OK on success, else an appropriate error code. ******************************************************************************/ int USBD_Read( int epAddr, void *data, int byteCount, USB_XferCompleteCb_TypeDef callback ) { USBD_Ep_TypeDef *ep = USBD_GetEpFromAddr( epAddr ); if ( ep == NULL ) { DEBUG_USB_API_PUTS( "\nUSBD_Read(), Illegal endpoint" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } if ( ( byteCount > MAX_XFER_LEN ) || ( ( byteCount / ep->packetSize ) > MAX_PACKETS_PR_XFER ) ) { DEBUG_USB_API_PUTS( "\nUSBD_Read(), Illegal transfer size" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } #if !defined( USB_SLAVEMODE ) if ( (uint32_t)data & 3 ) { DEBUG_USB_API_PUTS( "\nUSBD_Read(), Misaligned data buffer" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } #endif INT_Disable(); if ( USBDHAL_EpIsStalled( ep ) ) { INT_Enable(); DEBUG_USB_API_PUTS( "\nUSBD_Read(), Endpoint is halted" ); return USB_STATUS_EP_STALLED; } if ( ep->state != D_EP_IDLE ) { INT_Enable(); DEBUG_USB_API_PUTS( "\nUSBD_Read(), Endpoint is busy" ); return USB_STATUS_EP_BUSY; } if ( ( ep->num > 0 ) && ( USBD_GetUsbState() != USBD_STATE_CONFIGURED ) ) { INT_Enable(); DEBUG_USB_API_PUTS( "\nUSBD_Read(), Device not configured" ); return USB_STATUS_DEVICE_UNCONFIGURED; } ep->buf = (uint8_t*)data; ep->remaining = byteCount; ep->xferred = 0; if ( ep->num == 0 ) { ep->in = false; } else if ( ep->in != false ) { INT_Enable(); DEBUG_USB_API_PUTS( "\nUSBD_Read(), Illegal EP direction" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } ep->state = D_EP_RECEIVING; ep->xferCompleteCb = callback; USBD_ArmEp( ep ); INT_Enable(); return USB_STATUS_OK; }
/***************************************************************************//** * @brief * Initializes USB device hardware and internal protocol stack data structures, * then connects the data-line (D+ or D-) pullup resistor to signal host that * enumeration can begin. * * @note * You may later use @ref USBD_Disconnect() and @ref USBD_Connect() to force * reenumeration. * * @param[in] p * Pointer to device initialization struct. See @ref USBD_Init_TypeDef. * * @return * @ref USB_STATUS_OK on success, else an appropriate error code. ******************************************************************************/ int USBD_Init( const USBD_Init_TypeDef *p ) { int numEps; USBD_Ep_TypeDef *ep; uint8_t txFifoNum; uint8_t *conf, *confEnd; USB_EndpointDescriptor_TypeDef *epd; uint32_t totalRxFifoSize, totalTxFifoSize, numInEps, numOutEps; CMU_ClockSelectSet( cmuClock_HF, cmuSelect_HFXO ); #if ( USB_USBC_32kHz_CLK == USB_USBC_32kHz_CLK_LFXO ) CMU_OscillatorEnable( cmuOsc_LFXO, true, false ); #else CMU_OscillatorEnable( cmuOsc_LFRCO, true, false ); #endif USBTIMER_Init(); memset( dev, 0, sizeof( USBD_Device_TypeDef ) ); dev->setup = dev->setupPkt; dev->deviceDescriptor = p->deviceDescriptor; dev->configDescriptor = (USB_ConfigurationDescriptor_TypeDef*) p->configDescriptor; dev->stringDescriptors = p->stringDescriptors; dev->numberOfStrings = p->numberOfStrings; dev->state = USBD_STATE_LASTMARKER; dev->savedState = USBD_STATE_NONE; dev->lastState = USBD_STATE_NONE; dev->callbacks = p->callbacks; dev->remoteWakeupEnabled = false; /* Initialize EP0 */ ep = &dev->ep[ 0 ]; ep->in = false; ep->buf = NULL; ep->num = 0; ep->mask = 1; ep->addr = 0; ep->type = USB_EPTYPE_CTRL; ep->txFifoNum = 0; ep->packetSize = USB_EP0_SIZE; ep->remaining = 0; ep->xferred = 0; ep->state = D_EP_IDLE; ep->xferCompleteCb = NULL; ep->fifoSize = USB_EP0_SIZE / 4; totalTxFifoSize = ep->fifoSize * p->bufferingMultiplier[ 0 ]; totalRxFifoSize = (ep->fifoSize + 1) * p->bufferingMultiplier[ 0 ]; #if defined( DEBUG_USB_API ) /* Do a sanity check on the configuration descriptor */ { int i; /* Check if bLength's adds up exactly to wTotalLength */ i = 0; conf = (uint8_t*)dev->configDescriptor; confEnd = conf + dev->configDescriptor->wTotalLength; while ( conf < confEnd ) { if ( *conf == 0 ) break; i += *conf; conf += *conf; } if ( ( conf != confEnd ) || ( i != dev->configDescriptor->wTotalLength ) ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal configuration descriptor" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } } #endif /* defined( DEBUG_USB_API ) */ /* Parse configuration decriptor */ numEps = 0; numInEps = 0; numOutEps = 0; conf = (uint8_t*)dev->configDescriptor; confEnd = conf + dev->configDescriptor->wTotalLength; txFifoNum = 1; while ( conf < confEnd ) { if ( *conf == 0 ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal configuration descriptor" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } if ( *(conf + 1) == USB_ENDPOINT_DESCRIPTOR ) { numEps++; epd = (USB_EndpointDescriptor_TypeDef*)conf; ep = &dev->ep[ numEps ]; ep->in = ( epd->bEndpointAddress & USB_SETUP_DIR_MASK ) != 0; ep->buf = NULL; ep->addr = epd->bEndpointAddress; ep->num = ep->addr & USB_EPNUM_MASK; ep->mask = 1 << ep->num; ep->type = epd->bmAttributes & CONFIG_DESC_BM_TRANSFERTYPE; ep->packetSize = epd->wMaxPacketSize; ep->remaining = 0; ep->xferred = 0; ep->state = D_EP_IDLE; ep->xferCompleteCb = NULL; if ( ep->in ) { numInEps++; ep->txFifoNum = txFifoNum++; ep->fifoSize = (ep->packetSize/4) * p->bufferingMultiplier[ numEps ]; dev->inEpAddr2EpIndex[ ep->num ] = numEps; totalTxFifoSize += ep->fifoSize; if ( ep->num > MAX_NUM_IN_EPS ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal IN EP address" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } } else { numOutEps++; ep->fifoSize = (ep->packetSize/4 + 1) * p->bufferingMultiplier[ numEps ]; dev->outEpAddr2EpIndex[ ep->num ] = numEps; totalRxFifoSize += ep->fifoSize; if ( ep->num > MAX_NUM_OUT_EPS ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal OUT EP address" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } } } conf += *conf; } /* Rx-FIFO size: SETUP packets : 4*n + 6 n=#CTRL EP's * GOTNAK : 1 * Status info : 2*n n=#OUT EP's (EP0 included) in HW */ totalRxFifoSize += 10 + 1 + ( 2 * (MAX_NUM_OUT_EPS + 1) ); if ( numEps != NUM_EP_USED ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal EP count" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } if ( numInEps > MAX_NUM_IN_EPS ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal IN EP count" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } if ( numOutEps > MAX_NUM_OUT_EPS ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal OUT EP count" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } INT_Disable(); /* Enable USB clock */ CMU->HFCORECLKEN0 |= CMU_HFCORECLKEN0_USB | CMU_HFCORECLKEN0_USBC; CMU_ClockSelectSet( cmuClock_USBC, cmuSelect_HFCLK ); USBHAL_DisableGlobalInt(); if ( USBDHAL_CoreInit( totalRxFifoSize, totalTxFifoSize ) == USB_STATUS_OK ) { USBDHAL_EnableUsbResetInt(); USBHAL_EnableGlobalInt(); NVIC_ClearPendingIRQ( USB_IRQn ); NVIC_EnableIRQ( USB_IRQn ); } else { INT_Enable(); DEBUG_USB_API_PUTS( "\nUSBD_Init(), FIFO setup error" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } #if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF ) if ( USBHAL_VbusIsOn() ) { USBD_SetUsbState( USBD_STATE_POWERED ); } else #endif { USBD_SetUsbState( USBD_STATE_NONE ); } INT_Enable(); return USB_STATUS_OK; }
/***************************************************************************//** * @brief * Initializes USB device hardware and internal protocol stack data structures, * then connects the data-line (D+ or D-) pullup resistor to signal host that * enumeration can begin. * * @note * You may later use @ref USBD_Disconnect() and @ref USBD_Connect() to force * reenumeration. * * @param[in] p * Pointer to device initialization struct. See @ref USBD_Init_TypeDef. * * @return * @ref USB_STATUS_OK on success, else an appropriate error code. ******************************************************************************/ int USBD_Init( const USBD_Init_TypeDef *p ) { int numEps; USBD_Ep_TypeDef *ep; uint8_t txFifoNum; uint8_t *conf, *confEnd; USB_EndpointDescriptor_TypeDef *epd; USB_InterfaceDescriptor_TypeDef *id; uint32_t totalRxFifoSize, totalTxFifoSize, numInEps, numOutEps; #if !defined( USB_CORECLK_HFRCO ) || !defined( CMU_OSCENCMD_USHFRCOEN ) /* Devices supporting crystal-less USB can use HFRCO or HFXO as core clock. */ /* All other devices must use HFXO as core clock. */ if ( CMU_ClockSelectGet( cmuClock_HF ) != cmuSelect_HFXO ) { CMU_ClockSelectSet( cmuClock_HF, cmuSelect_HFXO ); } #endif #if !defined( CMU_OSCENCMD_USHFRCOEN ) #if ( USB_USBC_32kHz_CLK == USB_USBC_32kHz_CLK_LFXO ) CMU_OscillatorEnable(cmuOsc_LFXO, true, false); #else CMU_OscillatorEnable(cmuOsc_LFRCO, true, false); #endif #else CMU_ClockEnable(cmuClock_CORELE, true); /* LFC clock is needed to detect USB suspend when LEMIDLE is activated. */ #if ( USB_USBC_32kHz_CLK == USB_USBC_32kHz_CLK_LFXO ) CMU_ClockSelectSet(cmuClock_LFC, cmuSelect_LFXO); #else CMU_ClockSelectSet(cmuClock_LFC, cmuSelect_LFRCO); #endif CMU_ClockEnable(cmuClock_USBLE, true); #endif USBTIMER_Init(); memset( dev, 0, sizeof( USBD_Device_TypeDef ) ); dev->setup = dev->setupPkt; dev->deviceDescriptor = p->deviceDescriptor; dev->configDescriptor = (USB_ConfigurationDescriptor_TypeDef*) p->configDescriptor; dev->stringDescriptors = p->stringDescriptors; dev->numberOfStrings = p->numberOfStrings; dev->state = USBD_STATE_LASTMARKER; dev->savedState = USBD_STATE_NONE; dev->lastState = USBD_STATE_NONE; dev->callbacks = p->callbacks; dev->remoteWakeupEnabled = false; /* Initialize EP0 */ ep = &dev->ep[ 0 ]; ep->in = false; ep->buf = NULL; ep->num = 0; ep->mask = 1; ep->addr = 0; ep->type = USB_EPTYPE_CTRL; ep->txFifoNum = 0; ep->packetSize = p->deviceDescriptor->bMaxPacketSize0; if ( ep->packetSize == 32 ) { dev->ep0MpsCode = _USB_DOEP0CTL_MPS_32B; } else if ( ep->packetSize == 64 ) { dev->ep0MpsCode = _USB_DOEP0CTL_MPS_64B; } else { return USB_STATUS_ILLEGAL; } ep->remaining = 0; ep->xferred = 0; ep->state = D_EP_IDLE; ep->xferCompleteCb = NULL; ep->fifoSize = ep->packetSize / 4; totalTxFifoSize = ep->fifoSize * p->bufferingMultiplier[ 0 ]; totalRxFifoSize = (ep->fifoSize + 1) * p->bufferingMultiplier[ 0 ]; #if defined( DEBUG_USB_API ) /* Do a sanity check on the configuration descriptor */ { int i; /* Check if bLength's adds up exactly to wTotalLength */ i = 0; conf = (uint8_t*)dev->configDescriptor; confEnd = conf + dev->configDescriptor->wTotalLength; while ( conf < confEnd ) { if ( *conf == 0 ) break; i += *conf; conf += *conf; } if ( ( conf != confEnd ) || ( i != dev->configDescriptor->wTotalLength ) ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal configuration descriptor" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } } #endif /* defined( DEBUG_USB_API ) */ /* Parse configuration decriptor */ numEps = 0; numInEps = 0; numOutEps = 0; conf = (uint8_t*)dev->configDescriptor; confEnd = conf + dev->configDescriptor->wTotalLength; txFifoNum = 1; while ( conf < confEnd ) { if ( *conf == 0 ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal configuration descriptor" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } if ( *(conf + 1) == USB_ENDPOINT_DESCRIPTOR ) { numEps++; epd = (USB_EndpointDescriptor_TypeDef*)conf; #if defined( __GNUC__ ) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Warray-bounds" #endif ep = &dev->ep[ numEps ]; #if defined( __GNUC__ ) #pragma GCC diagnostic pop #endif ep->in = ( epd->bEndpointAddress & USB_SETUP_DIR_MASK ) != 0; ep->buf = NULL; ep->addr = epd->bEndpointAddress; ep->num = ep->addr & USB_EPNUM_MASK; ep->mask = 1 << ep->num; ep->type = epd->bmAttributes & CONFIG_DESC_BM_TRANSFERTYPE; ep->packetSize = epd->wMaxPacketSize; ep->remaining = 0; ep->xferred = 0; ep->state = D_EP_IDLE; ep->xferCompleteCb = NULL; if ( p->bufferingMultiplier[ numEps ] == 0 ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal EP fifo buffer configuration" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } if ( ep->in ) { numInEps++; ep->txFifoNum = txFifoNum++; ep->fifoSize = ( ( ep->packetSize + 3 ) / 4 ) * p->bufferingMultiplier[ numEps ]; dev->inEpAddr2EpIndex[ ep->num ] = numEps; totalTxFifoSize += ep->fifoSize; if ( ep->num > MAX_NUM_IN_EPS ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal IN EP address" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } } else { numOutEps++; ep->fifoSize = ( ( ( ep->packetSize + 3 ) / 4 ) + 1 ) * p->bufferingMultiplier[ numEps ]; dev->outEpAddr2EpIndex[ ep->num ] = numEps; totalRxFifoSize += ep->fifoSize; if ( ep->num > MAX_NUM_OUT_EPS ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal OUT EP address" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } } } else if ( *(conf + 1) == USB_INTERFACE_DESCRIPTOR ) { id = (USB_InterfaceDescriptor_TypeDef*)conf; if ( id->bAlternateSetting == 0 ) // Only check default interfaces { if ( dev->numberOfInterfaces != id->bInterfaceNumber ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal interface number" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } dev->numberOfInterfaces++; } } conf += *conf; } /* Rx-FIFO size: SETUP packets : 4*n + 6 n=#CTRL EP's * GOTNAK : 1 * Status info : 2*n n=#OUT EP's (EP0 included) in HW */ totalRxFifoSize += 10 + 1 + ( 2 * (MAX_NUM_OUT_EPS + 1) ); if ( dev->configDescriptor->bNumInterfaces != dev->numberOfInterfaces ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal interface count" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } if ( numEps != NUM_EP_USED ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal EP count" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } if ( numInEps > MAX_NUM_IN_EPS ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal IN EP count" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } if ( numOutEps > MAX_NUM_OUT_EPS ) { DEBUG_USB_API_PUTS( "\nUSBD_Init(), Illegal OUT EP count" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } INT_Disable(); /* Enable USB clock */ CMU->HFCORECLKEN0 |= CMU_HFCORECLKEN0_USB | CMU_HFCORECLKEN0_USBC; #if defined( CMU_OSCENCMD_USHFRCOEN ) CMU->USHFRCOCONF = CMU_USHFRCOCONF_BAND_48MHZ; CMU_ClockSelectSet( cmuClock_USBC, cmuSelect_USHFRCO ); /* Enable USHFRCO Clock Recovery mode. */ CMU->USBCRCTRL |= CMU_USBCRCTRL_EN; /* Turn on Low Energy Mode (LEM) features. */ USB->CTRL = USB_CTRL_LEMOSCCTRL_GATE | USB_CTRL_LEMIDLEEN | USB_CTRL_LEMPHYCTRL; #else CMU_ClockSelectSet( cmuClock_USBC, cmuSelect_HFCLK ); #endif USBHAL_DisableGlobalInt(); if ( USBDHAL_CoreInit( totalRxFifoSize, totalTxFifoSize ) == USB_STATUS_OK ) { USBDHAL_EnableUsbResetAndSuspendInt(); USBHAL_EnableGlobalInt(); NVIC_ClearPendingIRQ( USB_IRQn ); NVIC_EnableIRQ( USB_IRQn ); } else { INT_Enable(); DEBUG_USB_API_PUTS( "\nUSBD_Init(), FIFO setup error" ); EFM_ASSERT( false ); return USB_STATUS_ILLEGAL; } #if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF ) if ( USBHAL_VbusIsOn() ) { USBD_SetUsbState( USBD_STATE_POWERED ); } else #endif { USBD_SetUsbState( USBD_STATE_NONE ); } INT_Enable(); return USB_STATUS_OK; }