/**************************************************************************//** * Copyright (c) 2015 by Silicon Laboratories Inc. All rights reserved. * * http://developer.silabs.com/legal/version/v11/Silicon_Labs_Software_License_Agreement.txt *****************************************************************************/ #include "si_toolchain.h" #include "efm8_usb.h" #include #include // ----------------------------------------------------------------------------- // Global variables extern SI_SEGMENT_VARIABLE(myUsbDevice, USBD_Device_TypeDef, MEM_MODEL_SEG); extern SI_SEGMENT_VARIABLE(txZero[2], uint8_t, SI_SEG_CODE); // ----------------------------------------------------------------------------- // Function prototypes static void handleUsbEp0Int(void); static void handleUsbResetInt(void); static void handleUsbSuspendInt(void); static void handleUsbResumeInt(void); static void handleUsbEp0Tx(void); static void handleUsbEp0Rx(void); static void USB_ReadFIFOSetup(void); #if (SLAB_USB_EP1IN_USED) void handleUsbIn1Int(void); #endif // SLAB_USB_EP1IN_USED #if (SLAB_USB_EP2IN_USED) void handleUsbIn2Int(void); #endif // SLAB_USB_EP2IN_USED #if (SLAB_USB_EP3IN_USED) void handleUsbIn3Int(void); #endif // SLAB_USB_EP3IN_USED #if (SLAB_USB_EP1OUT_USED) void handleUsbOut1Int(void); #endif // SLAB_USB_EP1OUT_USED #if (SLAB_USB_EP2OUT_USED) void handleUsbOut2Int(void); #endif // SLAB_USB_EP2OUT_USED #if (SLAB_USB_EP3OUT_USED) void handleUsbOut3Int(void); #endif // SLAB_USB_EP3OUT_USED void SendEp0Stall(void); // ----------------------------------------------------------------------------- // Functions /***************************************************************************//** * @brief First-level handler for USB peripheral interrupt * @details If @ref SLAB_USB_POLLED_MODE is 1, this becomes a regular * function instead of an ISR and must be called by the application * periodically. ******************************************************************************/ #if (SLAB_USB_POLLED_MODE == 0) SI_INTERRUPT(usbIrqHandler, USB0_IRQn) #else void usbIrqHandler(void) #endif { uint8_t statusCommon, statusIn, statusOut, indexSave; #if SLAB_USB_HANDLER_CB // Callback to user before processing USBD_EnterHandler(); #endif // Get the interrupt sources statusCommon = USB_GetCommonInts(); statusIn = USB_GetInInts(); statusOut = USB_GetOutInts(); #if SLAB_USB_POLLED_MODE if ((statusCommon == 0) && (statusIn == 0) && (statusOut == 0)) { return; } #endif // Save the current index indexSave = USB_GetIndex(); // Check Common USB Interrupts if (USB_IsSofIntActive(statusCommon)) { #if SLAB_USB_SOF_CB USBD_SofCb(USB_GetSofNumber()); #endif // SLAB_USB_SOF_CB // Check for unhandled USB packets on EP0 and set the corresponding IN or // OUT interrupt active flag if necessary. if (((myUsbDevice.ep0.misc.bits.outPacketPending == true) && (myUsbDevice.ep0.state == D_EP_RECEIVING)) || ((myUsbDevice.ep0.misc.bits.inPacketPending == true) && (myUsbDevice.ep0.state == D_EP_TRANSMITTING))) { USB_SetEp0IntActive(statusIn); } // Check for unhandled USB OUT packets and set the corresponding OUT // interrupt active flag if necessary. #if SLAB_USB_EP1OUT_USED if ((myUsbDevice.ep1out.misc.bits.outPacketPending == true) && (myUsbDevice.ep1out.state == D_EP_RECEIVING)) { USB_SetOut1IntActive(statusOut); } #endif #if SLAB_USB_EP2OUT_USED if ((myUsbDevice.ep2out.misc.bits.outPacketPending == true) && (myUsbDevice.ep2out.state == D_EP_RECEIVING)) { USB_SetOut2IntActive(statusOut); } #endif #if SLAB_USB_EP3OUT_USED if ((myUsbDevice.ep3out.misc.bits.outPacketPending == true) && (myUsbDevice.ep3out.state == D_EP_RECEIVING)) { USB_SetOut3IntActive(statusOut); } #endif #if (SLAB_USB_EP3IN_USED && (SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_ISOC)) if ((myUsbDevice.ep3in.misc.bits.inPacketPending == true) && (myUsbDevice.ep3in.state == D_EP_TRANSMITTING)) { USB_SetIn3IntActive(statusIn); } #endif } if (USB_IsResetIntActive(statusCommon)) { handleUsbResetInt(); // If VBUS is not present on detection of a USB reset, enter suspend mode. #if (SLAB_USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF) if (USB_IsVbusOn() == false) { USB_SetSuspendIntActive(statusCommon); } #endif } if (USB_IsResumeIntActive(statusCommon)) { handleUsbResumeInt(); } if (USB_IsSuspendIntActive(statusCommon)) { handleUsbSuspendInt(); } #if SLAB_USB_EP3IN_USED if (USB_IsIn3IntActive(statusIn)) { handleUsbIn3Int(); } #endif // EP3IN_USED #if SLAB_USB_EP3OUT_USED if (USB_IsOut3IntActive(statusOut)) { handleUsbOut3Int(); } #endif // EP3OUT_USED #if SLAB_USB_EP2IN_USED if (USB_IsIn2IntActive(statusIn)) { handleUsbIn2Int(); } #endif // EP2IN_USED #if SLAB_USB_EP1IN_USED if (USB_IsIn1IntActive(statusIn)) { handleUsbIn1Int(); } #endif // EP1IN_USED #if SLAB_USB_EP2OUT_USED if (USB_IsOut2IntActive(statusOut)) { handleUsbOut2Int(); } #endif // EP2OUT_USED #if SLAB_USB_EP1OUT_USED if (USB_IsOut1IntActive(statusOut)) { handleUsbOut1Int(); } #endif // EP1OUT_USED // Check USB Endpoint 0 Interrupt if (USB_IsEp0IntActive(statusIn)) { handleUsbEp0Int(); } // Restore index USB_SetIndex(indexSave); #if SLAB_USB_HANDLER_CB // Callback to user before exiting USBD_ExitHandler(); #endif } /***************************************************************************//** * @brief Handles Endpoint 0 transfer interrupt ******************************************************************************/ static void handleUsbEp0Int(void) { USB_SetIndex(0); if (USB_Ep0SentStall() || USB_GetSetupEnd()) { USB_Ep0ClearSentStall(); USB_ServicedSetupEnd(); myUsbDevice.ep0.state = D_EP_IDLE; myUsbDevice.ep0.misc.c = 0; } if (USB_Ep0OutPacketReady()) { if (myUsbDevice.ep0.misc.bits.waitForRead == true) { myUsbDevice.ep0.misc.bits.outPacketPending = true; } else if (myUsbDevice.ep0.state == D_EP_IDLE) { myUsbDevice.ep0String.c = USB_STRING_DESCRIPTOR_UTF16LE; USB_ReadFIFOSetup(); // Vendor unique, Class or Standard setup commands override? #if SLAB_USB_SETUP_CMD_CB if (USBD_SetupCmdCb(&myUsbDevice.setup) == USB_STATUS_REQ_UNHANDLED) { #endif if (myUsbDevice.setup.bmRequestType.Type == USB_SETUP_TYPE_STANDARD) { USBDCH9_SetupCmd(); } else { SendEp0Stall(); } #if SLAB_USB_SETUP_CMD_CB } else { // If in-packet but callback didn't setup a USBD_Read and we are expecting a data byte then // we need to wait for the read to be setup and nack packets till USBD_Read is called. if ((myUsbDevice.setup.bmRequestType.Direction == USB_SETUP_DIR_OUT) && (myUsbDevice.ep0.state != D_EP_RECEIVING) && (myUsbDevice.setup.wLength) ) { myUsbDevice.ep0.misc.bits.waitForRead = true; } } #endif } else if (myUsbDevice.ep0.state == D_EP_RECEIVING) { handleUsbEp0Rx(); } else { myUsbDevice.ep0.misc.bits.outPacketPending = true; } } if ((myUsbDevice.ep0.state == D_EP_TRANSMITTING) && (USB_Ep0InPacketReady() == 0)) { handleUsbEp0Tx(); } } /***************************************************************************//** * @brief Reads and formats a setup packet ******************************************************************************/ static void USB_ReadFIFOSetup(void) { uint16_t MEM_MODEL_SEG *ptr = &myUsbDevice.setup; USB_ReadFIFO(0, 8, (uint8_t *)ptr); USB_Ep0ServicedOutPacketReady(); // Modify for Endian-ness of the compiler ptr[1] = le16toh(ptr[1]); ptr[2] = le16toh(ptr[2]); ptr[3] = le16toh(ptr[3]); } /***************************************************************************//** * @brief Handles USB port reset interrupt * @details After receiving a USB reset, halt all endpoints except for * Endpoint 0, set the device state, and configure USB hardware. ******************************************************************************/ static void handleUsbResetInt(void) { // Setup EP0 to receive SETUP packets myUsbDevice.ep0.state = D_EP_IDLE; // Halt all other endpoints #if SLAB_USB_EP1IN_USED myUsbDevice.ep1in.state = D_EP_HALT; #endif #if SLAB_USB_EP2IN_USED myUsbDevice.ep2in.state = D_EP_HALT; #endif #if SLAB_USB_EP3IN_USED myUsbDevice.ep3in.state = D_EP_HALT; #endif #if SLAB_USB_EP1OUT_USED myUsbDevice.ep1out.state = D_EP_HALT; #endif #if SLAB_USB_EP2OUT_USED myUsbDevice.ep2out.state = D_EP_HALT; #endif #if SLAB_USB_EP3OUT_USED myUsbDevice.ep3out.state = D_EP_HALT; #endif // After a USB reset, some USB hardware configurations will be reset and must // be reconfigured. // Re-enable clock recovery #if SLAB_USB_CLOCK_RECOVERY_ENABLED #if SLAB_USB_FULL_SPEED USB_EnableFullSpeedClockRecovery(); #else USB_EnableLowSpeedClockRecovery(); #endif #endif // Re-enable USB interrupts USB_EnableSuspendDetection(); USB_EnableDeviceInts(); // If VBUS is preset, put the device in the Default state. // Otherwise, put it in the Attached state. #if (!(SLAB_USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF)) if (USB_IsVbusOn()) { USBD_SetUsbState(USBD_STATE_DEFAULT); } else { USBD_SetUsbState(USBD_STATE_ATTACHED); } #else USBD_SetUsbState(USBD_STATE_DEFAULT); #endif // (!(SLAB_USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF)) #if SLAB_USB_RESET_CB // Make the USB Reset Callback USBD_ResetCb(); #endif } /***************************************************************************//** * @brief Handle USB port suspend interrupt * @details After receiving a USB reset, set the device state and * call @ref USBD_Suspend() if configured to do so in * @ref SLAB_USB_PWRSAVE_MODE ******************************************************************************/ static void handleUsbSuspendInt(void) { if (myUsbDevice.state >= USBD_STATE_POWERED) { USBD_SetUsbState(USBD_STATE_SUSPENDED); #if (SLAB_USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONSUSPEND) USBD_Suspend(); #endif } } /***************************************************************************//** * @brief Handles USB port resume interrupt * @details Restore the device state to its previous value. ******************************************************************************/ static void handleUsbResumeInt(void) { USBD_SetUsbState(myUsbDevice.savedState); } /***************************************************************************//** * @brief Handles transmit data phase on Endpoint 0 ******************************************************************************/ static void handleUsbEp0Tx(void) { uint8_t count, count_snapshot, i; bool callback = myUsbDevice.ep0.misc.bits.callback; // The number of bytes to send in the next packet must be less than or equal // to the maximum EP0 packet size. count = (myUsbDevice.ep0.remaining >= USB_EP0_SIZE) ? USB_EP0_SIZE : myUsbDevice.ep0.remaining; // Save the packet size for future use. count_snapshot = count; // Strings can use the USB_STRING_DESCRIPTOR_UTF16LE_PACKED type to pack // UTF16LE data without the zero's between each character. // If the current string is of type USB_STRING_DESCRIPTOR_UTF16LE_PACKED, // unpacket it by inserting a zero between each character in the string. if (myUsbDevice.ep0String.encoding.type == USB_STRING_DESCRIPTOR_UTF16LE_PACKED) { // If ep0String.encoding.init is true, this is the beginning of the string. // The first two bytes of the string are the bLength and bDescriptorType // fields. These are no packed like the reset of the string, so write them // to the FIFO and set ep0String.encoding.init to false. if (myUsbDevice.ep0String.encoding.init == true) { USB_WriteFIFO(0, 2, myUsbDevice.ep0.buf, false); myUsbDevice.ep0.buf += 2; count -= 2; myUsbDevice.ep0String.encoding.init = false; } // Insert a 0x00 between each character of the string. for (i = 0; i < count / 2; i++) { USB_WriteFIFO(0, 1, myUsbDevice.ep0.buf, false); myUsbDevice.ep0.buf++; USB_WriteFIFO(0, 1, &txZero, false); } } // For any data other than USB_STRING_DESCRIPTOR_UTF16LE_PACKED, just send the // data normally. else { USB_WriteFIFO(0, count, myUsbDevice.ep0.buf, false); myUsbDevice.ep0.buf += count; } myUsbDevice.ep0.misc.bits.inPacketPending = false; myUsbDevice.ep0.remaining -= count_snapshot; // If the last packet of the transfer is exactly the maximum EP0 packet size, // we will have to send a ZLP (zero-length packet) after the last data packet // to signal to the host that the transfer is complete. // Check for the ZLP packet case here. if ((myUsbDevice.ep0.remaining == 0) && (count_snapshot != USB_EP0_SIZE)) { USB_Ep0SetLastInPacketReady(); myUsbDevice.ep0.state = D_EP_IDLE; myUsbDevice.ep0String.c = USB_STRING_DESCRIPTOR_UTF16LE; myUsbDevice.ep0.misc.c = 0; } else { // Do not call USB_Ep0SetLastInPacketReady() because we still need to send // the ZLP. USB_Ep0SetInPacketReady(); } // Make callback if requested if (callback == true) { USBD_XferCompleteCb(EP0, USB_STATUS_OK, count_snapshot, myUsbDevice.ep0.remaining); } } /***************************************************************************//** * @brief Handles receive data phase on Endpoint 0 ******************************************************************************/ void handleUsbEp0Rx(void) { uint8_t count; USB_Status_TypeDef status; bool callback = myUsbDevice.ep0.misc.bits.callback; // Get the number of bytes received count = USB_Ep0GetCount(); // If the call to USBD_Read() did not give a large enough buffer to hold this // data, set the outPacketPending flag and signal an RX overrun. if (myUsbDevice.ep0.remaining < count) { myUsbDevice.ep0.state = D_EP_IDLE; myUsbDevice.ep0.misc.bits.outPacketPending = true; status = USB_STATUS_EP_RX_BUFFER_OVERRUN; } else { USB_ReadFIFO(0, count, myUsbDevice.ep0.buf); myUsbDevice.ep0.buf += count; myUsbDevice.ep0.remaining -= count; status = USB_STATUS_OK; // If the last packet of the transfer is exactly the maximum EP0 packet // size, we will must wait to receive a ZLP (zero-length packet) after the // last data packet. This signals that the host has completed the transfer. // Check for the ZLP packet case here. if ((myUsbDevice.ep0.remaining == 0) && (count != USB_EP0_SIZE)) { USB_Ep0SetLastOutPacketReady(); myUsbDevice.ep0.state = D_EP_IDLE; myUsbDevice.ep0.misc.bits.callback = false; } else { // Do not call USB_Ep0SetLastOutPacketReady() until we get the ZLP. USB_Ep0ServicedOutPacketReady(); } } // Make callback if requested if (callback == true) { USBD_XferCompleteCb(EP0, status, count, myUsbDevice.ep0.remaining); } } /***************************************************************************//** * @brief Send a procedural stall on Endpoint 0 ******************************************************************************/ void SendEp0Stall(void) { USB_SetIndex(0); myUsbDevice.ep0.state = D_EP_STALL; USB_Ep0SendStall(); } // This function is called from USBD_Init(). It forces the user project to pull // this module from the library so that the declared ISR can be seen and // included. If this is not done then this entire module by never be included // and the ISR will not be present. void forceModuleLoad_usbint(void){}