diff --git a/Demos/Host/LowLevel/MassStorageHost/Lib/MassStoreCommands.c b/Demos/Host/LowLevel/MassStorageHost/Lib/MassStoreCommands.c index 0d8a231f2e..c86e51b0c5 100644 --- a/Demos/Host/LowLevel/MassStorageHost/Lib/MassStoreCommands.c +++ b/Demos/Host/LowLevel/MassStorageHost/Lib/MassStoreCommands.c @@ -110,20 +110,23 @@ static uint8_t MassStore_SendCommand(CommandBlockWrapper_t* const SCSICommandBlo */ static uint8_t MassStore_WaitForDataReceived(void) { - uint16_t TimeoutMSRem = COMMAND_DATA_TIMEOUT_MS; + uint16_t TimeoutMSRem = COMMAND_DATA_TIMEOUT_MS; + uint16_t PreviousFrameNumber = USB_Host_GetFrameNumber(); /* Select the IN data pipe for data reception */ Pipe_SelectPipe(MASS_STORE_DATA_IN_PIPE); Pipe_Unfreeze(); - + /* Wait until data received in the IN pipe */ while (!(Pipe_IsINReceived())) { + uint16_t CurrentFrameNumber = USB_Host_GetFrameNumber(); + /* Check to see if a new frame has been issued (1ms elapsed) */ - if (USB_INT_HasOccurred(USB_INT_HSOFI)) + if (CurrentFrameNumber != PreviousFrameNumber) { - /* Clear the flag and decrement the timeout period counter */ - USB_INT_Clear(USB_INT_HSOFI); + /* Save the new frame number and decrement the timeout period */ + PreviousFrameNumber = CurrentFrameNumber; TimeoutMSRem--; /* Check to see if the timeout period for the command has elapsed */ diff --git a/Demos/Host/LowLevel/StillImageHost/Lib/StillImageCommands.c b/Demos/Host/LowLevel/StillImageHost/Lib/StillImageCommands.c index a99a95680b..861f55b5de 100644 --- a/Demos/Host/LowLevel/StillImageHost/Lib/StillImageCommands.c +++ b/Demos/Host/LowLevel/StillImageHost/Lib/StillImageCommands.c @@ -108,28 +108,28 @@ uint8_t SImage_ReceiveEventHeader(void) */ uint8_t SImage_ReceiveBlockHeader(void) { - uint16_t TimeoutMSRem = COMMAND_DATA_TIMEOUT_MS; + uint16_t TimeoutMSRem = COMMAND_DATA_TIMEOUT_MS; + uint16_t PreviousFrameNumber = USB_Host_GetFrameNumber(); /* Unfreeze the data IN pipe */ Pipe_SelectPipe(SIMAGE_DATA_IN_PIPE); Pipe_Unfreeze(); /* Wait until data received on the IN pipe */ - while (!(Pipe_IsReadWriteAllowed())) + while (!(Pipe_IsINReceived())) { + uint16_t CurrentFrameNumber = USB_Host_GetFrameNumber(); + /* Check to see if a new frame has been issued (1ms elapsed) */ - if (USB_INT_HasOccurred(USB_INT_HSOFI)) + if (CurrentFrameNumber != PreviousFrameNumber) { - /* Clear the flag and decrement the timeout period counter */ - USB_INT_Clear(USB_INT_HSOFI); + /* Save the new frame number and decrement the timeout period */ + PreviousFrameNumber = CurrentFrameNumber; TimeoutMSRem--; /* Check to see if the timeout period for the command has elapsed */ if (!(TimeoutMSRem)) - { - /* Return error code */ - return PIPE_RWSTREAM_Timeout; - } + return PIPE_RWSTREAM_Timeout; } Pipe_Freeze(); diff --git a/LUFA/Drivers/USB/Class/Host/MassStorage.c b/LUFA/Drivers/USB/Class/Host/MassStorage.c index 14b7741db3..dfa954c5c5 100644 --- a/LUFA/Drivers/USB/Class/Host/MassStorage.c +++ b/LUFA/Drivers/USB/Class/Host/MassStorage.c @@ -166,19 +166,21 @@ static uint8_t MS_Host_SendCommand(USB_ClassInfo_MS_Host_t* const MSInterfaceInf static uint8_t MS_Host_WaitForDataReceived(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo) { - uint16_t TimeoutMSRem = COMMAND_DATA_TIMEOUT_MS; + uint16_t TimeoutMSRem = COMMAND_DATA_TIMEOUT_MS; + uint16_t PreviousFrameNumber = USB_Host_GetFrameNumber(); Pipe_SelectPipe(MSInterfaceInfo->Config.DataINPipeNumber); Pipe_Unfreeze(); while (!(Pipe_IsINReceived())) { - if (USB_INT_HasOccurred(USB_INT_HSOFI)) + uint16_t CurrentFrameNumber = USB_Host_GetFrameNumber(); + + if (CurrentFrameNumber != PreviousFrameNumber) { - USB_INT_Clear(USB_INT_HSOFI); - TimeoutMSRem--; + PreviousFrameNumber = CurrentFrameNumber; - if (!(TimeoutMSRem)) + if (!(TimeoutMSRem--)) return PIPE_RWSTREAM_Timeout; } diff --git a/LUFA/Drivers/USB/Class/Host/StillImage.c b/LUFA/Drivers/USB/Class/Host/StillImage.c index 42c3feca62..e08fc76af3 100644 --- a/LUFA/Drivers/USB/Class/Host/StillImage.c +++ b/LUFA/Drivers/USB/Class/Host/StillImage.c @@ -179,7 +179,8 @@ uint8_t SImage_Host_SendBlockHeader(USB_ClassInfo_SI_Host_t* const SIInterfaceIn uint8_t SImage_Host_ReceiveBlockHeader(USB_ClassInfo_SI_Host_t* const SIInterfaceInfo, SI_PIMA_Container_t* const PIMAHeader) { - uint16_t TimeoutMSRem = COMMAND_DATA_TIMEOUT_MS; + uint16_t TimeoutMSRem = COMMAND_DATA_TIMEOUT_MS; + uint16_t PreviousFrameNumber = USB_Host_GetFrameNumber(); if ((USB_HostState != HOST_STATE_Configured) || !(SIInterfaceInfo->State.IsActive)) return PIPE_RWSTREAM_DeviceDisconnected; @@ -187,17 +188,16 @@ uint8_t SImage_Host_ReceiveBlockHeader(USB_ClassInfo_SI_Host_t* const SIInterfac Pipe_SelectPipe(SIInterfaceInfo->Config.DataINPipeNumber); Pipe_Unfreeze(); - while (!(Pipe_IsReadWriteAllowed())) + while (!(Pipe_IsINReceived())) { - if (USB_INT_HasOccurred(USB_INT_HSOFI)) + uint16_t CurrentFrameNumber = USB_Host_GetFrameNumber(); + + if (CurrentFrameNumber != PreviousFrameNumber) { - USB_INT_Clear(USB_INT_HSOFI); - TimeoutMSRem--; + PreviousFrameNumber = CurrentFrameNumber; - if (!(TimeoutMSRem)) - { - return PIPE_RWSTREAM_Timeout; - } + if (!(TimeoutMSRem--)) + return PIPE_RWSTREAM_Timeout; } Pipe_Freeze(); diff --git a/LUFA/Drivers/USB/HighLevel/Events.h b/LUFA/Drivers/USB/HighLevel/Events.h index 5c295b61fc..ae7debbb73 100644 --- a/LUFA/Drivers/USB/HighLevel/Events.h +++ b/LUFA/Drivers/USB/HighLevel/Events.h @@ -180,6 +180,23 @@ */ void EVENT_USB_Host_DeviceEnumerationComplete(void); + /** Event for USB Start Of Frame detection, when enabled. This event fires at the start of each USB + * frame, once per millisecond, and is synchronized to the USB bus. This can be used as an accurate + * millisecond timer source when the USB bus is not suspended while in host mode. + * + * This event is time-critical; it is run once per millisecond and thus long handlers will significantly + * degrade device performance. This event should only be enabled when needed to reduce device wake-ups. + * + * \note This event is not normally active - it must be manually enabled and disabled via the + * \ref USB_Host_EnableSOFEvents() and \ref USB_Host_DisableSOFEvents() commands after enumeration of + * a USB device. + * \n\n + * + * \note This event does not exist if the USB_DEVICE_ONLY token is supplied to the compiler (see + * \ref Group_USBManagement documentation). + */ + void EVENT_USB_Host_StartOfFrame(void); + /** Event for USB device connection. This event fires when the AVR in device mode and the device is connected * to a host, beginning the enumeration process, measured by a rising level on the AVR's VBUS pin. * @@ -339,6 +356,7 @@ void EVENT_USB_Host_DeviceEnumerationFailed(const uint8_t ErrorCode, const uint8_t SubErrorCode) ATTR_WEAK ATTR_ALIAS(USB_Event_Stub); + void EVENT_USB_Host_StartOfFrame(void) ATTR_WEAK ATTR_ALIAS(USB_Event_Stub); #endif #if defined(USB_CAN_BE_DEVICE) diff --git a/LUFA/Drivers/USB/LowLevel/Device.h b/LUFA/Drivers/USB/LowLevel/Device.h index 17984c719f..3007936125 100644 --- a/LUFA/Drivers/USB/LowLevel/Device.h +++ b/LUFA/Drivers/USB/LowLevel/Device.h @@ -140,6 +140,14 @@ }; /* Inline Functions: */ + /** Returns the current USB frame number, when in device mode. Every millisecond the USB bus is active (i.e. enumerated to a host) + * the frame number is incremented by one. + */ + static inline uint16_t USB_Device_GetFrameNumber(void) + { + return UDFNUM; + } + /** Enables the device mode Start Of Frame events. When enabled, this causes the * \ref EVENT_USB_Device_StartOfFrame() event to fire once per millisecond, synchronized to the USB bus, * at the start of each USB frame when enumerated in device mode. diff --git a/LUFA/Drivers/USB/LowLevel/Endpoint.c b/LUFA/Drivers/USB/LowLevel/Endpoint.c index 4b2d1c059b..fdb6c7469e 100644 --- a/LUFA/Drivers/USB/LowLevel/Endpoint.c +++ b/LUFA/Drivers/USB/LowLevel/Endpoint.c @@ -142,6 +142,8 @@ uint8_t Endpoint_WaitUntilReady(void) uint16_t TimeoutMSRem = USB_STREAM_TIMEOUT_MS; #endif + uint16_t PreviousFrameNumber = USB_Device_GetFrameNumber(); + for (;;) { if (Endpoint_GetEndpointDirection() == ENDPOINT_DIR_IN) @@ -161,10 +163,12 @@ uint8_t Endpoint_WaitUntilReady(void) return ENDPOINT_READYWAIT_BusSuspended; else if (Endpoint_IsStalled()) return ENDPOINT_READYWAIT_EndpointStalled; - - if (USB_INT_HasOccurred(USB_INT_SOFI)) + + uint16_t CurrentFrameNumber = USB_Device_GetFrameNumber(); + + if (CurrentFrameNumber != PreviousFrameNumber) { - USB_INT_Clear(USB_INT_SOFI); + PreviousFrameNumber = CurrentFrameNumber; if (!(TimeoutMSRem--)) return ENDPOINT_READYWAIT_Timeout; diff --git a/LUFA/Drivers/USB/LowLevel/Host.c b/LUFA/Drivers/USB/LowLevel/Host.c index 5b7a714bab..2b66e28b4b 100644 --- a/LUFA/Drivers/USB/LowLevel/Host.c +++ b/LUFA/Drivers/USB/LowLevel/Host.c @@ -194,16 +194,19 @@ void USB_Host_ProcessNextHostState(void) uint8_t USB_Host_WaitMS(uint8_t MS) { - bool BusSuspended = USB_Host_IsBusSuspended(); - uint8_t ErrorCode = HOST_WAITERROR_Successful; + bool BusSuspended = USB_Host_IsBusSuspended(); + uint8_t ErrorCode = HOST_WAITERROR_Successful; + uint16_t PreviousFrameNumber = USB_Host_GetFrameNumber(); USB_Host_ResumeBus(); while (MS) { - if (USB_INT_HasOccurred(USB_INT_HSOFI)) + uint16_t CurrentFrameNumber = USB_Host_GetFrameNumber(); + + if (CurrentFrameNumber != PreviousFrameNumber) { - USB_INT_Clear(USB_INT_HSOFI); + PreviousFrameNumber = CurrentFrameNumber; MS--; } @@ -245,11 +248,13 @@ static void USB_Host_ResetDevice(void) USB_Host_ResetBus(); while (!(USB_Host_IsBusResetComplete())); - USB_Host_ResumeBus(); - USB_INT_Clear(USB_INT_HSOFI); + bool HSOFIEnabled = USB_INT_IsEnabled(USB_INT_HSOFI); + USB_INT_Disable(USB_INT_HSOFI); + USB_INT_Clear(USB_INT_HSOFI); + for (uint8_t MSRem = 10; MSRem != 0; MSRem--) { /* Workaround for powerless-pull-up devices. After a USB bus reset, @@ -267,6 +272,9 @@ static void USB_Host_ResetDevice(void) _delay_ms(1); } + if (HSOFIEnabled) + USB_INT_Enable(USB_INT_HSOFI); + if (BusSuspended) USB_Host_SuspendBus(); diff --git a/LUFA/Drivers/USB/LowLevel/Host.h b/LUFA/Drivers/USB/LowLevel/Host.h index 8623c4ec6b..40f630e845 100644 --- a/LUFA/Drivers/USB/LowLevel/Host.h +++ b/LUFA/Drivers/USB/LowLevel/Host.h @@ -245,6 +245,33 @@ }; /* Inline Functions: */ + /** Returns the current USB frame number, when in host mode. Every millisecond the USB bus is active (i.e. not suspended) + * the frame number is incremented by one. + */ + static inline uint16_t USB_Host_GetFrameNumber(void) + { + return UHFNUM; + } + + /** Enables the host mode Start Of Frame events. When enabled, this causes the + * \ref EVENT_USB_Host_StartOfFrame() event to fire once per millisecond, synchronized to the USB bus, + * at the start of each USB frame when a device is enumerated while in host mode. + */ + static inline void USB_Host_EnableSOFEvents(void) ATTR_ALWAYS_INLINE; + static inline void USB_Host_EnableSOFEvents(void) + { + USB_INT_Enable(USB_INT_HSOFI); + } + + /** Disables the host mode Start Of Frame events. When disabled, this stops the firing of the + * \ref EVENT_USB_Host_StartOfFrame() event when enumerated in host mode. + */ + static inline void USB_Host_DisableSOFEvents(void) ATTR_ALWAYS_INLINE; + static inline void USB_Host_DisableSOFEvents(void) + { + USB_INT_Disable(USB_INT_HSOFI); + } + /** Resets the USB bus, including the endpoints in any attached device and pipes on the AVR host. * USB bus resets leave the default control pipe configured (if already configured). * diff --git a/LUFA/Drivers/USB/LowLevel/Pipe.c b/LUFA/Drivers/USB/LowLevel/Pipe.c index a8eb50f639..5f1090245b 100644 --- a/LUFA/Drivers/USB/LowLevel/Pipe.c +++ b/LUFA/Drivers/USB/LowLevel/Pipe.c @@ -133,6 +133,8 @@ uint8_t Pipe_WaitUntilReady(void) #else uint16_t TimeoutMSRem = USB_STREAM_TIMEOUT_MS; #endif + + uint16_t PreviousFrameNumber = USB_Host_GetFrameNumber(); for (;;) { @@ -151,10 +153,12 @@ uint8_t Pipe_WaitUntilReady(void) return PIPE_READYWAIT_PipeStalled; else if (USB_HostState == HOST_STATE_Unattached) return PIPE_READYWAIT_DeviceDisconnected; - - if (USB_INT_HasOccurred(USB_INT_HSOFI)) + + uint16_t CurrentFrameNumber = USB_Host_GetFrameNumber(); + + if (CurrentFrameNumber != PreviousFrameNumber) { - USB_INT_Clear(USB_INT_HSOFI); + PreviousFrameNumber = CurrentFrameNumber; if (!(TimeoutMSRem--)) return PIPE_READYWAIT_Timeout; diff --git a/LUFA/Drivers/USB/LowLevel/USBInterrupt.c b/LUFA/Drivers/USB/LowLevel/USBInterrupt.c index 1c6d6caa79..36541cf7fa 100644 --- a/LUFA/Drivers/USB/LowLevel/USBInterrupt.c +++ b/LUFA/Drivers/USB/LowLevel/USBInterrupt.c @@ -209,6 +209,13 @@ ISR(USB_GEN_vect, ISR_BLOCK) USB_ResetInterface(); } + + if (USB_INT_HasOccurred(USB_INT_HSOFI) && USB_INT_IsEnabled(USB_INT_HSOFI)) + { + USB_INT_Clear(USB_INT_HSOFI); + + EVENT_USB_Host_StartOfFrame(); + } #endif #if defined(USB_CAN_BE_BOTH) diff --git a/LUFA/ManPages/ChangeLog.txt b/LUFA/ManPages/ChangeLog.txt index 0467e2815d..9a801b400d 100644 --- a/LUFA/ManPages/ChangeLog.txt +++ b/LUFA/ManPages/ChangeLog.txt @@ -13,6 +13,9 @@ * - Moved the Pipe and Endpoint stream related code to two new USB library core source files EndpointStream.c and PipeStream.c * - Added board hardware driver support for the Olimex AVR-USB-162 development board (thanks to Steve Fawcett) * - Added board hardware driver support for the USBFOO development board + * - Added new USB_Device_GetFrameNumber() and USB_Host_GetFrameNumber() functions to retrieve the current USB frame number + * - Added new USB_Host_EnableSOFEvents(), USB_Host_DisableSOFEvents() and EVENT_USB_Host_StartOfFrame() for the user application + * handling of USB Start of Frame events while in USB Host mode * * Changed: * - Removed complicated logic for the Endpoint_ConfigureEndpoint() function to use inlined or function called versions @@ -29,6 +32,8 @@ * - Changed the signature of the CALLBACK_USB_GetDescriptor() callback function so that the descriptor pointer is const, to remove * the need for extra casting inside the callback (thanks to Jonathan Kollasch) * - Reduced HOST_DEVICE_SETTLE_DELAY_MS to 1000ms down from 1500ms to improve device compatibility while in USB Host mode + * - Changed over all demos, drivers and internal functions to use the current frame number over the Start of Frame flag where possible + * to free up the Start of Frame flag for interrupt use in the user application * * Fixed: * - Fixed USB_GetHIDReportItemInfo() function modifying the given report item's data when the report item does not exist