/****************************************************************************** * Function: void USBCtrlTrfOutHandler(void) * * PreCondition: None * * Input: None * * Output: None * * Side Effects: None * * Overview: This routine handles an OUT transaction according to * which control transfer state is currently active. * * Note: Note that if the the control transfer was from * host to device, the session owner should be notified * at the end of each OUT transaction to service the * received data. * *****************************************************************************/ void USBCtrlTrfOutHandler(void) { if(ctrl_trf_state == CTRL_TRF_RX) { //USBCtrlTrfRxService(); //Don't really need a USBCtrlTrfRxService() handler in this bootloader firmware. //The PC application doesn't use control transfers for sending data to the device. ep0Bo.ADR = (byte*)(&CtrlTrfData); ep0Bo.Cnt = EP0_BUFF_SIZE; if(ep0Bo.Stat.DTS == 0) ep0Bo.Stat._byte = _USIE|_DAT1|_DTSEN; else ep0Bo.Stat._byte = _USIE|_DAT0|_DTSEN; } else //In this case the last OUT transaction must have been a status stage of a CTRL_TRF_TX { //Prepare EP0 OUT for the next SETUP transaction. USBPrepareForNextSetupTrf(); ep0Bo.Cnt = EP0_BUFF_SIZE; ep0Bo.ADR = (byte*)(&SetupPkt); ep0Bo.Stat._byte = _USIE|_DAT0|_DTSEN|_BSTALL; } }//end USBCtrlTrfOutHandler
/****************************************************************************** * Function: void USBProtocolResetHandler(void) * * PreCondition: A USB bus reset is received from the host. * * Input: None * * Output: None * * Side Effects: Currently, this routine flushes any pending USB * transactions. It empties out the USTAT FIFO. This action * might not be desirable in some applications. * * Overview: Once a USB bus reset is received from the host, this * routine should be called. It resets the device address to * zero, disables all non-EP0 endpoints, initializes EP0 to * be ready for default communication, clears all USB * interrupt flags, unmasks applicable USB interrupts, and * reinitializes internal state-machine variables. * * Note: None *****************************************************************************/ void USBProtocolResetHandler(void) { UEIR = 0; // Clear all USB error flags UIR = 0; // Clears all USB interrupts //UEIE = 0b10011111; // Unmask all USB error interrupts //UIE = 0b01111011; // Enable all interrupts except ACTVIE UADDR = 0x00; // Reset to default address mDisableEP1to15(); // Reset all non-EP0 UEPn registers UEP0 = EP_CTRL|HSHK_EN; // Init EP0 as a Ctrl EP, see usbdrv.h while(UIRbits.TRNIF == 1) // Flush any pending transactions { UIRbits.TRNIF = 0; /******************************************************************** Bug Fix: August 14, 2007 ********************************************************************* Clearing the transfer complete flag bit, TRNIF, causes the SIE to advance the FIFO. If the next data in the FIFO holding register is valid, the SIE will reassert the interrupt within 6Tcy of clearing TRNIF. If no additional data is preset, TRNIF will remain clear. Additional nops were added in this fix to guarantee that TRNIF is properly updated before being checked again. ********************************************************************/ Nop(); Nop(); Nop(); Nop(); Nop(); Nop(); } UCONbits.PKTDIS = 0; // Make sure packet processing is enabled USBPrepareForNextSetupTrf(); // Declared in usbctrltrf.c usb_stat.RemoteWakeup = 0; // Default status flag to disable usb_active_cfg = 0; // Clear active configuration usb_device_state = DEFAULT_STATE; }//end USBProtocolResetHandler
/****************************************************************************** * Function: void USBCtrlTrfOutHandler(void) * * PreCondition: None * * Input: None * * Output: None * * Side Effects: None * * Overview: This routine handles an OUT transaction according to * which control transfer state is currently active. * * Note: Note that if the the control transfer was from * host to device, the session owner should be notified * at the end of each OUT transaction to service the * received data. * *****************************************************************************/ void USBCtrlTrfOutHandler(void) { if(ctrl_trf_state == CTRL_TRF_RX) { USBCtrlTrfRxService(); /* * Don't have to worry about overwriting _KEEP bit * because if _KEEP was set, TRNIF would not have been * generated in the first place. */ ep0Bo.ADR = (byte*)(&CtrlTrfData); ep0Bo.Cnt = EP0_BUFF_SIZE; if(ep0Bo.Stat.DTS == 0) ep0Bo.Stat._byte = _USIE|_DAT1|_DTSEN; else ep0Bo.Stat._byte = _USIE|_DAT0|_DTSEN; } else //In this case the last OUT transaction must have been a status stage of a CTRL_TRF_TX { //Prepare EP0 OUT for the next SETUP transaction. USBPrepareForNextSetupTrf(); ep0Bo.Cnt = EP0_BUFF_SIZE; ep0Bo.ADR = (byte*)(&SetupPkt); ep0Bo.Stat._byte = _USIE|_DAT0|_DTSEN|_BSTALL; } }//end USBCtrlTrfOutHandler
/****************************************************************************** * Function: void USBProtocolResetHandler(void) * * PreCondition: A USB bus reset is received from the host. * * Input: None * * Output: None * * Side Effects: Currently, this routine flushes any pending USB * transactions. It empties out the USTAT FIFO. This action * might not be desirable in some applications. * * Overview: Once a USB bus reset is received from the host, this * routine should be called. It resets the device address to * zero, disables all non-EP0 endpoints, initializes EP0 to * be ready for default communication, clears all USB * interrupt flags, unmasks applicable USB interrupts, and * reinitializes internal state-machine variables. * * Note: None *****************************************************************************/ void USBProtocolResetHandler(void) { UIR = 0; // Clears all USB interrupts UIE = 0b01111011; // Enable all interrupts except ACTVIE UADDR = 0x00; // Reset to default address UEP0 = EP_CTRL|HSHK_EN; // Init EP0 as a Ctrl EP, see usbdrv.h while(UIRbits.TRNIF == 1) // Flush any pending transactions UIRbits.TRNIF = 0; UCONbits.PKTDIS = 0; // Make sure packet processing is enabled USBPrepareForNextSetupTrf(); // Declared in usbctrltrf.c usb_active_cfg = 0; // Clear active configuration usb_device_state = DEFAULT_STATE; }//end USBProtocolResetHandler
/****************************************************************************** * Function: void USBCtrlTrfInHandler(void) * * PreCondition: None * * Input: None * * Output: None * * Side Effects: None * * Overview: This routine handles an IN transaction according to * which control transfer state is currently active. * * * Note: A Set Address Request must not change the acutal address * of the device until the completion of the control * transfer. The end of the control transfer for Set Address * Request is an IN transaction. Therefore it is necessary * to service this unique situation when the condition is * right. Macro mUSBCheckAdrPendingState is defined in * usb9.h and its function is to specifically service this * event. *****************************************************************************/ void USBCtrlTrfInHandler(void) { mUSBCheckAdrPendingState(); // Must check if in ADR_PENDING_STATE if(ctrl_trf_state == CTRL_TRF_TX) { USBCtrlTrfTxService(); if(ep0Bi.Stat.DTS == 0) ep0Bi.Stat._byte = _USIE|_DAT1|_DTSEN; else ep0Bi.Stat._byte = _USIE|_DAT0|_DTSEN; } else // CTRL_TRF_RX USBPrepareForNextSetupTrf(); }//end USBCtrlTrfInHandler
/****************************************************************************** * Function: void USBStallHandler(void) * * PreCondition: A STALL packet is sent to the host by the SIE. * * Input: None * * Output: None * * Side Effects: None * * Overview: The STALLIF is set anytime the SIE sends out a STALL * packet regardless of which endpoint causes it. * A Setup transaction overrides the STALL function. A stalled * endpoint stops stalling once it receives a setup packet. * In this case, the SIE will accepts the Setup packet and * set the TRNIF flag to notify the firmware. STALL function * for that particular endpoint pipe will be automatically * disabled (direction specific). * * There are a few reasons for an endpoint to be stalled. * 1. When a non-supported USB request is received. * Example: GET_DESCRIPTOR(DEVICE_QUALIFIER) * 2. When an endpoint is currently halted. * 3. When the device class specifies that an endpoint must * stall in response to a specific event. * Example: Mass Storage Device Class * If the CBW is not valid, the device shall * STALL the Bulk-In pipe. * See USB Mass Storage Class Bulk-only Transport * Specification for more details. * * Note: UEPn.EPSTALL can be scanned to see which endpoint causes * the stall event. * If *****************************************************************************/ void USBStallHandler(void) { /* * Does not really have to do anything here, * even for the control endpoint. * All BDs of Endpoint 0 are owned by SIE right now, * but once a Setup Transaction is received, the ownership * for EP0_OUT will be returned to CPU. * When the Setup Transaction is serviced, the ownership * for EP0_IN will then be forced back to CPU by firmware. */ if(UEP0bits.EPSTALL == 1) { USBPrepareForNextSetupTrf(); // Firmware work-around UEP0bits.EPSTALL = 0; } UIRbits.STALLIF = 0; }//end USBStallHandler
/****************************************************************************** * Function: void USBCtrlTrfInHandler(void) * * PreCondition: None * * Input: None * * Output: None * * Side Effects: None * * Overview: This routine handles an IN transaction according to * which control transfer state is currently active. * * * Note: A Set Address Request must not change the acutal address * of the device until the completion of the control * transfer. The end of the control transfer for Set Address * Request is an IN transaction. Therefore it is necessary * to service this unique situation when the condition is * right. Macro mUSBCheckAdrPendingState is defined in * usb9.h and its function is to specifically service this * event. *****************************************************************************/ void USBCtrlTrfInHandler(void) { CTRL_TRF_SETUP xxx; xxx.bAltID = 1; mUSBCheckAdrPendingState(); // Must check if in ADR_PENDING_STATE if(ctrl_trf_state == CTRL_TRF_TX) { USBCtrlTrfTxService(); /******************************************************************** Bug Fix: May 14, 2007 (#F7 - Partial 2/4) ********************************************************************* For a control transfer read, if the host tries to read more data than what it has requested, the peripheral device should stall the extra IN transactions and the status stage. Typically, a host does not try to read more data than what it has requested. The original firmware did not handle this situation. Instead of stalling extra IN transactions, the device kept sending out zero length packets. This work around checks if a short IN packet has been sent or not. If it has, the IN endpoint will be set to install the next IN token. If not, then the original endpoint setup code will be executed. ********************************************************************/ if(short_pkt_status == SHORT_PKT_SENT) { // If a short packet has been sent, don't want to send any more, // stall next time if host is still trying to read. ep0Bi.Stat._byte = _USIE|_BSTALL; } /*******************************************************************/ else { if(ep0Bi.Stat.DTS == 0) ep0Bi.Stat._byte = _USIE|_DAT1|_DTSEN; else ep0Bi.Stat._byte = _USIE|_DAT0|_DTSEN; }//end if(...)else } else // CTRL_TRF_RX USBPrepareForNextSetupTrf(); }//end USBCtrlTrfInHandler
/****************************************************************************** * Function: void USBProtocolResetHandler(void) * * PreCondition: A USB bus reset is received from the host. * * Input: None * * Output: None * * Side Effects: Currently, this routine flushes any pending USB * transactions. It empties out the USTAT FIFO. This action * might not be desirable in some applications. * * Overview: Once a USB bus reset is received from the host, this * routine should be called. It resets the device address to * zero, disables all non-EP0 endpoints, initializes EP0 to * be ready for default communication, clears all USB * interrupt flags, unmasks applicable USB interrupts, and * reinitializes internal state-machine variables. * * Note: None *****************************************************************************/ void USBProtocolResetHandler(void) { UEIR = 0; // Clear all USB error flags UIR = 0; // Clears all USB interrupts UEIE = 0b10011111; // Unmask all USB error interrupts //UIE = 0b01111011; // Enable all interrupts except ACTVIE UIE = 0b00111011; // Enable all interrupts except ACTVIE and SOFIE UADDR = 0x00; // Reset to default address mDisableEP1to15(); // Reset all non-EP0 UEPn registers UEP0 = EP_CTRL|HSHK_EN; // Init EP0 as a Ctrl EP, see usbdrv.h while(UIRbits.TRNIF == 1) // Flush any pending transactions { UIRbits.TRNIF = 0; /******************************************************************** Bug Fix: August 14, 2007 ********************************************************************* Clearing the transfer complete flag bit, TRNIF, causes the SIE to advance the FIFO. If the next data in the FIFO holding register is valid, the SIE will reassert the interrupt within 6Tcy of clearing TRNIF. If no additional data is preset, TRNIF will remain clear. Additional nops were added in this fix to guarantee that TRNIF is properly updated before being checked again. ********************************************************************/ _asm bra 0 //Equivalent to bra $+2, which takes half as much code as 2 nop instructions bra 0 //Equivalent to bra $+2, which takes half as much code as 2 nop instructions _endasm Nop(); } UCONbits.PKTDIS = 0; // Make sure packet processing is enabled USBPrepareForNextSetupTrf(); // Declared in usbctrltrf.c //Prepare EP0 OUT to receive the first SETUP packet ep0Bo.Cnt = EP0_BUFF_SIZE; ep0Bo.ADR = (byte*)(&SetupPkt); ep0Bo.Stat._byte = _USIE|_DAT0|_DTSEN|_BSTALL; usb_stat.RemoteWakeup = 0; // Default status flag to disable usb_active_cfg = 0; // Clear active configuration usb_device_state = DEFAULT_STATE; }//end USBProtocolResetHandler
/****************************************************************************** * Function: void USBCtrlTrfOutHandler(void) * * PreCondition: None * * Input: None * * Output: None * * Side Effects: None * * Overview: This routine handles an OUT transaction according to * which control transfer state is currently active. * * Note: Note that if the the control transfer was from * host to device, the session owner should be notified * at the end of each OUT transaction to service the * received data. * *****************************************************************************/ void USBCtrlTrfOutHandler(void) { if(ctrl_trf_state == CTRL_TRF_RX) { USBCtrlTrfRxService(); /* * Don't have to worry about overwriting _KEEP bit * because if _KEEP was set, TRNIF would not have been * generated in the first place. */ if(ep0Bo.Stat.DTS == 0) ep0Bo.Stat._byte = _USIE|_DAT1|_DTSEN; else ep0Bo.Stat._byte = _USIE|_DAT0|_DTSEN; } else // CTRL_TRF_TX USBPrepareForNextSetupTrf(); }//end USBCtrlTrfOutHandler
/****************************************************************************** * Function: void USBProtocolResetHandler(void) * * PreCondition: A USB bus reset is received from the host. * * Input: None * * Output: None * * Side Effects: Currently, this routine flushes any pending USB * transactions. It empties out the USTAT FIFO. This action * might not be desirable in some applications. * * Overview: Once a USB bus reset is received from the host, this * routine should be called. It resets the device address to * zero, disables all non-EP0 endpoints, initializes EP0 to * be ready for default communication, clears all USB * interrupt flags, unmasks applicable USB interrupts, and * reinitializes internal state-machine variables. * * Note: None *****************************************************************************/ void USBProtocolResetHandler(void) { UEIR = 0; // Clear all USB error flags UIR = 0; // Clears all USB interrupts UEIE = 0b10011111; // Unmask all USB error interrupts UIE = 0b01111011; // Enable all interrupts except ACTVIE UADDR = 0x00; // Reset to default address mDisableEP1to15(); // Reset all non-EP0 UEPn registers UEP0 = EP_CTRL|HSHK_EN; // Init EP0 as a Ctrl EP, see usbdrv.h while(UIRbits.TRNIF == 1) // Flush any pending transactions UIRbits.TRNIF = 0; UCONbits.PKTDIS = 0; // Make sure packet processing is enabled USBPrepareForNextSetupTrf(); // Declared in usbctrltrf.c usb_stat.RemoteWakeup = 0; // Default status flag to disable usb_active_cfg = 0; // Clear active configuration usb_device_state = DEFAULT_STATE; }//end USBProtocolResetHandler
/****************************************************************************** * Function: void USBDriverService(void) * * PreCondition: None * * Input: None * * Output: None * * Side Effects: None * * Overview: This routine is the heart of this firmware. It manages * all USB interrupts. * * Note: Device state transitions through the following stages: * DETACHED -> ATTACHED -> POWERED -> DEFAULT -> * ADDRESS_PENDING -> ADDRESSED -> CONFIGURED -> READY *****************************************************************************/ void USBDriverService(void) { /* * Task A: Service USB Activity Interrupt */ if(UIRbits.ACTVIF && UIEbits.ACTVIE) { UCONbits.SUSPND = 0; UIEbits.ACTVIE = 0; UIRbits.ACTVIF = 0; }//end if /* * Task B: Service USB Bus Reset Interrupt. * When bus reset is received during suspend, ACTVIF will be set first, * once the UCONbits.SUSPND is clear, then the URSTIF bit will be asserted. * This is why URSTIF is checked after ACTVIF. * * The USB reset flag is masked when the USB state is in * DETACHED_STATE or ATTACHED_STATE, and therefore cannot * cause a USB reset event during these two states. */ if(UIRbits.URSTIF && UIEbits.URSTIE) USBProtocolResetHandler(); /* * Task C: Service other USB interrupts */ if(UIRbits.IDLEIF && UIEbits.IDLEIE) { UIEbits.ACTVIE = 1; // Enable bus activity interrupt UIRbits.IDLEIF = 0; UCONbits.SUSPND = 1; // Put USB module in power conserve // mode, SIE clock inactive /* Now, go into power saving */ PIE2bits.USBIE = 1; // Set wakeup source Sleep(); PIR2bits.USBIF = 0; }//end if if(UIRbits.STALLIF && UIEbits.STALLIE) { if(UEP0bits.EPSTALL == 1) { USBPrepareForNextSetupTrf(); // Firmware Work-Around UEP0bits.EPSTALL = 0; }//end if UIRbits.STALLIF = 0; }//end if /* * Pointless to continue servicing if the host has not sent a bus reset. * Once bus reset is received, the device transitions into the DEFAULT * state and is ready for communication. */ if(usb_device_state < DEFAULT_STATE) return; /* * Task D: Servicing USB Transaction Complete Interrupt */ if(UIRbits.TRNIF && UIEbits.TRNIE) { /* * USBCtrlEPService only services transactions over EP0. * It ignores all other EP transactions. */ USBCtrlEPService(); /* * Other EP can be serviced later by responsible device class firmware. * Each device driver knows when an OUT or IN transaction is ready by * checking the buffer ownership bit. * An OUT EP should always be owned by SIE until the data is ready. * An IN EP should always be owned by CPU until the data is ready. * * Because of this logic, it is not necessary to save the USTAT value * of non-EP0 transactions. */ UIRbits.TRNIF = 0; }//end if(UIRbits.TRNIF && UIEbits.TRNIE) }//end USBDriverService