After an epic battle, suspend/resume/wakeup is now supported.

It turns out that suspend support on the STM32 USB core is buggy as
heck. Host mode cannot resume after suspend, and device mode cannot
receive resume or send wakeup signalling.

I managed to fake resume support by keeping Downstream and our connected
device running at full power, and simulating a wakeup event to the host
by disconnecting/reconnecting Upstream from the host.
pull/7/head
Robert Fisk 8 years ago
parent e3ddf162ee
commit 0e1dba7c46

@ -41,10 +41,8 @@ typedef uint8_t (*UpstreamHidGetReportCallback)(uint8_t *report,
uint16_t len);
void Upstream_HID_DeInit(void);
void Upstream_HID_GetNextInterruptReport(UpstreamHidGetReportCallback callback);
void Upstream_HID_SendControlReport(UpstreamPacketTypeDef* packetToSend, uint8_t dataLength);
void Upstream_HID_ReallySendControlReport(void);
void Upstream_HID_GetInterruptReport(UpstreamHidGetReportCallback callback);
void Upstream_HID_RequestSendControlReport(UpstreamPacketTypeDef* packetToSend, uint8_t dataLength);

@ -15,6 +15,7 @@
#include "led.h"
#include "upstream_interface_def.h"
#include "usbd_def.h"
typedef enum
@ -22,6 +23,7 @@ typedef enum
STATE_TEST_INTERFACE,
STATE_WAIT_DEVICE,
STATE_DEVICE_ACTIVE,
STATE_SUSPENDED,
STATE_ERROR
} UpstreamStateTypeDef;
@ -39,7 +41,11 @@ typedef enum
void Upstream_InitStateMachine(void);
void Upstream_StateMachine_SetErrorState(void);
InterfaceCommandClassTypeDef Upstream_StateMachine_CheckActiveClass(void);
uint32_t Upstream_StateMachine_GetSuspendState(void);
void Upstream_StateMachine_DeviceDisconnected(void);
void Upstream_StateMachine_Suspend(void);
void Upstream_StateMachine_CheckResume(void);
void Upstream_StateMachine_Wakeup(void);

@ -77,7 +77,8 @@ static uint8_t USBD_HID_EP0RxReady(USBD_HandleTypeDef *pdev);
#define USBD_PID_HID 0x0002
#define USBD_PID_MOUSE 0x0002
#define USBD_PID_KEYBOARD 0x0003
USBD_ClassTypeDef USBD_HID =
@ -97,7 +98,7 @@ USBD_ClassTypeDef USBD_HID =
USBD_HID_GetCfgDesc,
USBD_HID_GetCfgDesc,
USBD_HID_GetDeviceQualifierDesc,
USBD_PID_HID
0 //USBD_PID_HID
};
@ -299,6 +300,7 @@ void USBD_HID_PreinitMouse(void)
InReportSize = HID_MOUSE_INPUT_DATA_LEN;
OutReportSize = HID_MOUSE_OUTPUT_DATA_LEN;
USBD_HID.USBDevPid = USBD_PID_MOUSE;
USBD_HID_CfgDesc[USB_HID_CFGDESC__INTERFACE_PROTOCOL_OFFSET] = HID_MOUSE_BOOT_CODE;
USBD_HID_CfgDesc[USB_HID_CFGDESC__HID_REPORT_DESC_SIZE_OFFSET] = HID_MOUSE_REPORT_DESC_SIZE;
USBD_HID_CfgDesc[USB_HID_CFGDESC__EPIN_SIZE_OFFSET] = HID_MOUSE_INPUT_DATA_LEN;
@ -313,6 +315,7 @@ void USBD_HID_PreinitKeyboard(void)
InReportSize = HID_KEYBOARD_INPUT_DATA_LEN;
OutReportSize = HID_KEYBOARD_OUTPUT_DATA_LEN;
USBD_HID.USBDevPid = USBD_PID_KEYBOARD;
USBD_HID_CfgDesc[USB_HID_CFGDESC__INTERFACE_PROTOCOL_OFFSET] = HID_KEYBRD_BOOT_CODE;
USBD_HID_CfgDesc[USB_HID_CFGDESC__HID_REPORT_DESC_SIZE_OFFSET] = HID_KEYBOARD_REPORT_DESC_SIZE;
USBD_HID_CfgDesc[USB_HID_CFGDESC__EPIN_SIZE_OFFSET] = HID_KEYBOARD_INPUT_DATA_LEN;
@ -343,7 +346,7 @@ static uint8_t USBD_HID_Init (USBD_HandleTypeDef *pdev,
((USBD_HID_HandleTypeDef *)pdev->pClassData)->state = HID_IDLE;
USBD_HID_pdev = pdev;
Upstream_HID_GetNextInterruptReport(USBD_HID_SendReport);
Upstream_HID_GetInterruptReport(USBD_HID_SendReport);
}
return ret;
@ -360,6 +363,11 @@ static uint8_t USBD_HID_DeInit (USBD_HandleTypeDef *pdev,
uint8_t cfgidx)
{
Upstream_HID_DeInit();
if (OutReportPacket != NULL)
{
Upstream_ReleasePacket(OutReportPacket);
OutReportPacket = NULL;
}
/* Close HID EPs */
USBD_LL_CloseEP(pdev,
@ -566,7 +574,7 @@ static uint8_t USBD_HID_DataIn (USBD_HandleTypeDef *pdev,
((USBD_HID_HandleTypeDef *)pdev->pClassData)->state = HID_IDLE;
USBD_HID_pdev = pdev;
Upstream_HID_GetNextInterruptReport(USBD_HID_SendReport);
Upstream_HID_GetInterruptReport(USBD_HID_SendReport);
return USBD_OK;
}
@ -586,7 +594,7 @@ static uint8_t *USBD_HID_GetDeviceQualifierDesc (uint16_t *length)
//Called when data is received from host on control endpoint.
//Upstream_HID may send this immediately, but will probably have to save it and send after the next interrupt transfer
//Upstream_HID will send it after the next IN interrupt transfer
static uint8_t USBD_HID_EP0RxReady(USBD_HandleTypeDef *pdev)
{
if ((OutReportPacket == NULL) ||
@ -595,7 +603,7 @@ static uint8_t USBD_HID_EP0RxReady(USBD_HandleTypeDef *pdev)
while(1);
}
Upstream_HID_SendControlReport(OutReportPacket, OutReportSize);
Upstream_HID_RequestSendControlReport(OutReportPacket, OutReportSize);
Upstream_ReleasePacket(OutReportPacket);
OutReportPacket = NULL;
return USBD_OK;

@ -89,7 +89,7 @@
* @{
*/
USBD_StatusTypeDef USBD_Init(USBD_HandleTypeDef *pdev, USBD_DescriptorsTypeDef *pdesc, uint8_t id);
USBD_StatusTypeDef USBD_DeInit(USBD_HandleTypeDef *pdev);
USBD_ClassTypeDef* USBD_DeInit(USBD_HandleTypeDef *pdev);
USBD_StatusTypeDef USBD_Start (USBD_HandleTypeDef *pdev);
USBD_StatusTypeDef USBD_Stop (USBD_HandleTypeDef *pdev);
USBD_StatusTypeDef USBD_RegisterClass(USBD_HandleTypeDef *pdev, USBD_ClassTypeDef *pclass);
@ -146,6 +146,8 @@ USBD_StatusTypeDef USBD_LL_PrepareReceive(USBD_HandleTypeDef *pdev,
uint32_t USBD_LL_GetRxDataSize (USBD_HandleTypeDef *pdev, uint8_t ep_addr);
void USBD_LL_Delay (uint32_t Delay);
void USBD_LL_WakeupHost(USBD_HandleTypeDef *pdev);
/**
* @}
*/

@ -28,6 +28,7 @@
/* Includes ------------------------------------------------------------------*/
#include "usbd_core.h"
#include "usbd_descriptors.h"
#include "upstream_statemachine.h"
/** @addtogroup STM32_USBD_DEVICE_LIBRARY
@ -129,7 +130,7 @@ USBD_StatusTypeDef USBD_Init(USBD_HandleTypeDef *pdev, USBD_DescriptorsTypeDef *
* @param pdev: device instance
* @retval status: status
*/
USBD_StatusTypeDef USBD_DeInit(USBD_HandleTypeDef *pdev)
USBD_ClassTypeDef* USBD_DeInit(USBD_HandleTypeDef *pdev)
{
/* Set Default State */
pdev->dev_state = USBD_STATE_DEFAULT;
@ -143,7 +144,8 @@ USBD_StatusTypeDef USBD_DeInit(USBD_HandleTypeDef *pdev)
/* Initialize low level driver */
USBD_LL_DeInit(pdev);
return USBD_OK;
return pdev->pClass;
// return USBD_OK;
}
@ -456,6 +458,11 @@ USBD_StatusTypeDef USBD_Suspend(USBD_HandleTypeDef *pdev)
{
pdev->dev_old_state = pdev->dev_state;
pdev->dev_state = USBD_STATE_SUSPENDED;
if (pdev->dev_old_state > USBD_STATE_DEFAULT)
{
Upstream_StateMachine_Suspend();
}
return USBD_OK;
}
@ -468,7 +475,12 @@ USBD_StatusTypeDef USBD_Suspend(USBD_HandleTypeDef *pdev)
USBD_StatusTypeDef USBD_Resume(USBD_HandleTypeDef *pdev)
{
pdev->dev_state = pdev->dev_old_state;
pdev->dev_state = pdev->dev_old_state;
if (pdev->dev_old_state > USBD_STATE_DEFAULT)
{
Upstream_StateMachine_CheckResume();
}
return USBD_OK;
}

@ -19,13 +19,16 @@
UpstreamPacketTypeDef* UpstreamHidPacket = NULL;
UpstreamHidGetReportCallback GetReportCallback = NULL;
KeyboardOutStateTypeDef KeyboardOutDataState = KEYBOARD_OUT_STATE_IDLE;
uint8_t KeyboardOutData[HID_KEYBOARD_OUTPUT_DATA_LEN];
KeyboardOutStateTypeDef KeyboardOutDataState = KEYBOARD_OUT_STATE_IDLE;
uint8_t KeyboardOutData[HID_KEYBOARD_OUTPUT_DATA_LEN];
uint8_t GetReportLoopIsRunning = 0;
void Upstream_HID_GetNextInterruptReportReceiveCallback(UpstreamPacketTypeDef* receivedPacket);
void Upstream_HID_ReallySendControlReportReceiveCallback(UpstreamPacketTypeDef* receivedPacket);
static void Upstream_HID_ReceiveInterruptReport(void);
static void Upstream_HID_ReceiveInterruptReportCallback(UpstreamPacketTypeDef* receivedPacket);
static void Upstream_HID_SendControlReport(void);
static void Upstream_HID_SendControlReportCallback(UpstreamPacketTypeDef* receivedPacket);
@ -42,50 +45,42 @@ void Upstream_HID_DeInit(void)
void Upstream_HID_GetNextInterruptReport(UpstreamHidGetReportCallback callback)
//Called by usbd_hid to request our next interrupt report
void Upstream_HID_GetInterruptReport(UpstreamHidGetReportCallback callback)
{
UpstreamPacketTypeDef* freePacket;
InterfaceCommandClassTypeDef activeClass;
GetReportCallback = callback;
activeClass = Upstream_StateMachine_CheckActiveClass();
if ((activeClass != COMMAND_CLASS_HID_MOUSE) &&
(activeClass != COMMAND_CLASS_HID_KEYBOARD)) //add classes here
if (UpstreamHidPacket != NULL)
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
Upstream_ReleasePacket(UpstreamHidPacket);
UpstreamHidPacket = NULL;
}
if (callback != NULL)
{
//This means we were called by the host
//Release packet used for last transaction (if any)
if (UpstreamHidPacket != NULL)
{
Upstream_ReleasePacket(UpstreamHidPacket);
UpstreamHidPacket = NULL;
}
//Wakeup interrupts are apparently broken.
//So we check for resume when the host sends us something here.
Upstream_StateMachine_CheckResume();
if (KeyboardOutDataState == KEYBOARD_OUT_STATE_BUSY)
{
//Just save the callback, because we are still waiting for the OUT report to complete
GetReportCallback = callback;
return;
}
if (GetReportCallback != NULL)
{
//Just return if we already have an outstanding request
return;
}
GetReportCallback = callback;
//Start our internal loop?
if (!GetReportLoopIsRunning)
{
GetReportLoopIsRunning = 1;
Upstream_HID_ReceiveInterruptReport();
}
else
}
//Our internal report request loop
static void Upstream_HID_ReceiveInterruptReport(void)
{
UpstreamPacketTypeDef* freePacket;
InterfaceCommandClassTypeDef activeClass;
activeClass = Upstream_StateMachine_CheckActiveClass();
if ((activeClass != COMMAND_CLASS_HID_MOUSE) &&
(activeClass != COMMAND_CLASS_HID_KEYBOARD)) //add classes here
{
//This means were called on OUT report completion, or retrying after a downstream NAK
if (GetReportCallback == NULL)
{
//The host has not given us the callback yet, so we give up
return;
}
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
}
freePacket = Upstream_GetFreePacketImmediately();
@ -94,23 +89,24 @@ void Upstream_HID_GetNextInterruptReport(UpstreamHidGetReportCallback callback)
return;
}
freePacket->Length16 = UPSTREAM_PACKET_HEADER_LEN_16;
freePacket->CommandClass = activeClass;
freePacket->Command = COMMAND_HID_GET_REPORT;
if (Upstream_TransmitPacket(freePacket) == HAL_OK)
{
Upstream_ReceivePacket(Upstream_HID_GetNextInterruptReportReceiveCallback);
}
else
{
Upstream_ReleasePacket(freePacket);
}
freePacket->Length16 = UPSTREAM_PACKET_HEADER_LEN_16;
freePacket->CommandClass = activeClass;
freePacket->Command = COMMAND_HID_GET_REPORT;
if (Upstream_TransmitPacket(freePacket) == HAL_OK)
{
Upstream_ReceivePacket(Upstream_HID_ReceiveInterruptReportCallback);
}
else
{
Upstream_ReleasePacket(freePacket);
}
}
void Upstream_HID_GetNextInterruptReportReceiveCallback(UpstreamPacketTypeDef* receivedPacket)
static void Upstream_HID_ReceiveInterruptReportCallback(UpstreamPacketTypeDef* receivedPacket)
{
UpstreamHidGetReportCallback tempReportCallback;
InterfaceCommandClassTypeDef activeClass;
@ -126,105 +122,115 @@ void Upstream_HID_GetNextInterruptReportReceiveCallback(UpstreamPacketTypeDef* r
return;
}
if (UpstreamHidPacket != NULL)
{
while(1);
}
if (receivedPacket == NULL) //Error receiving packet
{
return; //Just give up...
}
if (GetReportCallback == NULL) //HID class may have been DeInitted while we were waiting for our reply
{
Upstream_ReleasePacket(receivedPacket);
return;
}
if (receivedPacket->Length16 == UPSTREAM_PACKET_HEADER_LEN_16)
{
//Zero-length reply indicates no data from downstream device
Upstream_ReleasePacket(receivedPacket);
//Check if we need to send OUT data to the keyboard before requesting next Interrupt IN data
if (KeyboardOutDataState == KEYBOARD_OUT_STATE_DATA_READY)
{
Upstream_HID_ReallySendControlReport();
}
else
{
//Otherwise poll downstream again
Upstream_HID_GetNextInterruptReport(NULL);
}
return;
}
if (activeClass == COMMAND_CLASS_HID_MOUSE)
else
{
if (receivedPacket->Length16 != (UPSTREAM_PACKET_HEADER_LEN_16 + ((HID_MOUSE_INPUT_DATA_LEN + 1) / 2)))
if (activeClass == COMMAND_CLASS_HID_MOUSE)
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
if (receivedPacket->Length16 != (UPSTREAM_PACKET_HEADER_LEN_16 + ((HID_MOUSE_INPUT_DATA_LEN + 1) / 2)))
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
}
dataLength = HID_MOUSE_INPUT_DATA_LEN;
if ((receivedPacket->Data[0] & ~((1 << HID_MOUSE_MAX_BUTTONS) - 1)) != 0) //Check number of buttons received
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
}
//Mouse wakeup is triggered by button clicks only
if (receivedPacket->Data[0] != 0)
{
if (Upstream_StateMachine_GetSuspendState())
{
//Send wakeup signal to host instead of returning data packet
GetReportCallback = NULL;
Upstream_StateMachine_Wakeup();
}
}
//Other mouse sanity checks & stuff go here...
}
dataLength = HID_MOUSE_INPUT_DATA_LEN;
if ((receivedPacket->Data[0] & ~((1 << HID_MOUSE_MAX_BUTTONS) - 1)) != 0) //Check number of buttons received
else if (activeClass == COMMAND_CLASS_HID_KEYBOARD)
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
if (receivedPacket->Length16 != (UPSTREAM_PACKET_HEADER_LEN_16 + ((HID_KEYBOARD_INPUT_DATA_LEN + 1) / 2)))
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
}
dataLength = HID_KEYBOARD_INPUT_DATA_LEN;
if (receivedPacket->Data[1] != 0)
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
}
for (i = 2; i < HID_KEYBOARD_INPUT_DATA_LEN; i++)
{
if (receivedPacket->Data[i] > HID_KEYBOARD_MAX_KEY)
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
}
}
if (Upstream_StateMachine_GetSuspendState())
{
//Send wakeup signal to host instead of returning data packet
GetReportCallback = NULL;
Upstream_StateMachine_Wakeup();
}
//Other keyboard sanity checks here...
}
//Other mouse sanity checks & stuff go here...
}
else if (activeClass == COMMAND_CLASS_HID_KEYBOARD)
{
if (receivedPacket->Length16 != (UPSTREAM_PACKET_HEADER_LEN_16 + ((HID_KEYBOARD_INPUT_DATA_LEN + 1) / 2)))
//Other HID classes go here...
else
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
}
dataLength = HID_KEYBOARD_INPUT_DATA_LEN;
if (receivedPacket->Data[1] != 0)
if ((GetReportCallback == NULL) ||
(UpstreamHidPacket != NULL))
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
Upstream_ReleasePacket(receivedPacket);
}
for (i = 2; i < HID_KEYBOARD_INPUT_DATA_LEN; i++)
else
{
if (receivedPacket->Data[i] > HID_KEYBOARD_MAX_KEY)
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
}
//Send resulting data to host
UpstreamHidPacket = receivedPacket; //Save packet so we can free it when upstream USB transaction is done
tempReportCallback = GetReportCallback;
GetReportCallback = NULL;
tempReportCallback(receivedPacket->Data, dataLength);
}
//Other keyboard sanity checks here...
}
//Other HID classes go here...
else
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
}
UpstreamHidPacket = receivedPacket; //Save packet so we can free it when upstream USB transaction is done
tempReportCallback = GetReportCallback;
GetReportCallback = NULL;
tempReportCallback(receivedPacket->Data, dataLength);
//Check if we need to send OUT data to the keyboard before requesting next Interrupt IN data
if (KeyboardOutDataState == KEYBOARD_OUT_STATE_DATA_READY)
{
Upstream_HID_ReallySendControlReport();
Upstream_HID_SendControlReport();
}
else
{
Upstream_HID_ReceiveInterruptReport(); //Otherwise poll downstream again
}
}
void Upstream_HID_SendControlReport(UpstreamPacketTypeDef* packetToSend, uint8_t dataLength)
void Upstream_HID_RequestSendControlReport(UpstreamPacketTypeDef* packetToSend, uint8_t dataLength)
{
InterfaceCommandClassTypeDef activeClass;
uint32_t i;
@ -248,7 +254,7 @@ void Upstream_HID_SendControlReport(UpstreamPacketTypeDef* packetToSend, uint8_t
void Upstream_HID_ReallySendControlReport(void)
static void Upstream_HID_SendControlReport(void)
{
UpstreamPacketTypeDef* freePacket;
uint32_t i;
@ -270,7 +276,7 @@ void Upstream_HID_ReallySendControlReport(void)
if (Upstream_TransmitPacket(freePacket) == HAL_OK)
{
Upstream_ReceivePacket(Upstream_HID_ReallySendControlReportReceiveCallback);
Upstream_ReceivePacket(Upstream_HID_SendControlReportCallback);
}
else
{
@ -280,7 +286,7 @@ void Upstream_HID_ReallySendControlReport(void)
void Upstream_HID_ReallySendControlReportReceiveCallback(UpstreamPacketTypeDef* receivedPacket)
static void Upstream_HID_SendControlReportCallback(UpstreamPacketTypeDef* receivedPacket)
{
InterfaceCommandClassTypeDef activeClass;
@ -291,6 +297,12 @@ void Upstream_HID_ReallySendControlReportReceiveCallback(UpstreamPacketTypeDef*
return;
}
if (KeyboardOutDataState != KEYBOARD_OUT_STATE_BUSY)
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
}
if (receivedPacket == NULL)
{
return; //Just give up...
@ -298,9 +310,8 @@ void Upstream_HID_ReallySendControlReportReceiveCallback(UpstreamPacketTypeDef*
Upstream_ReleasePacket(receivedPacket);
KeyboardOutDataState = KEYBOARD_OUT_STATE_IDLE;
//If upstream host has already requested the next IN report data,
//this will send the request downstream.
Upstream_HID_GetNextInterruptReport(NULL);
Upstream_HID_ReceiveInterruptReport();
}

@ -12,6 +12,7 @@
#include "upstream_statemachine.h"
#include "upstream_spi.h"
#include "upstream_hid.h"
#include "usb_device.h"
#include "usbd_core.h"
#include "usbd_msc.h"
@ -82,7 +83,8 @@ InterfaceCommandClassTypeDef Upstream_StateMachine_CheckActiveClass(void)
return COMMAND_CLASS_ERROR;
}
if (UpstreamState != STATE_DEVICE_ACTIVE)
if ((UpstreamState != STATE_DEVICE_ACTIVE) &&
(UpstreamState != STATE_SUSPENDED))
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return COMMAND_CLASS_INTERFACE;
@ -92,6 +94,16 @@ InterfaceCommandClassTypeDef Upstream_StateMachine_CheckActiveClass(void)
}
uint32_t Upstream_StateMachine_GetSuspendState(void)
{
if (UpstreamState == STATE_SUSPENDED)
{
return 1;
}
return 0;
}
void Upstream_StateMachine_TestInterfaceReplyCallback(UpstreamPacketTypeDef* replyPacket)
{
uint16_t i;
@ -228,3 +240,52 @@ void Upstream_StateMachine_DeviceDisconnected(void)
Upstream_GetFreePacket(Upstream_StateMachine_NotifyDevice);
}
//Suspend event activated by our host
void Upstream_StateMachine_Suspend(void)
{
if (UpstreamState >= STATE_ERROR)
{
return;
}
if (UpstreamState != STATE_DEVICE_ACTIVE)
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
}
UpstreamState = STATE_SUSPENDED;
}
//Resume event activated by our host
void Upstream_StateMachine_CheckResume(void)
{
if (UpstreamState == STATE_SUSPENDED)
{
UpstreamState = STATE_DEVICE_ACTIVE;
}
}
//Activated by downstream report, causing us to wake up the host
void Upstream_StateMachine_Wakeup(void)
{
USBD_ClassTypeDef* activeClass;
if (UpstreamState != STATE_SUSPENDED)
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
}
//This is really ugly! But wakeup seems to be broken on the STM32, so we do it the hard way.
activeClass = USBD_DeInit(&hUsbDeviceFS);
USB_Device_Init();
USBD_RegisterClass(&hUsbDeviceFS, activeClass);
USBD_Start(&hUsbDeviceFS);
// This is how I'd wakeup the host, IF IT ACTUALLY WORKED!
// USBD_LL_WakeupHost(&hUsbDeviceFS);
}

@ -501,4 +501,17 @@ void USBD_LL_Delay (uint32_t Delay)
{
HAL_Delay(Delay);
}
void USBD_LL_WakeupHost(USBD_HandleTypeDef *pdev)
{
if (pdev->dev_remote_wakeup == 1)
{
HAL_PCD_ActivateRemoteWakeup(pdev->pData);
HAL_Delay(10);
HAL_PCD_DeActivateRemoteWakeup(pdev->pData);
}
}
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

Loading…
Cancel
Save