You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
usb-firewall-fork/Upstream/Src/upstream_statemachine.c

348 lines
8.8 KiB

/*
* upstream_statemachine.c
*
* Created on: 20/08/2015
* Author: Robert Fisk
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "upstream_statemachine.h"
#include "upstream_spi.h"
#include "upstream_hid.h"
#include "usb_device.h"
#include "usbd_core.h"
#include "usbd_msc.h"
#include "usbd_hid.h"
#include "build_config.h"
#define POLL_DEVICE_CONNECTED_TIMEOUT_MS 1000
UpstreamStateTypeDef UpstreamState = STATE_TEST_INTERFACE;
InterfaceCommandClassTypeDef ConfiguredDeviceClass = COMMAND_CLASS_INTERFACE;
uint32_t PollDeviceConnectedTimer;
void Upstream_StateMachine_TestInterfaceReplyCallback(UpstreamPacketTypeDef* replyPacket);
void Upstream_StateMachine_NotifyDevice(UpstreamPacketTypeDef* freePacket);
void Upstream_StateMachine_NotifyDeviceReplyCallback(UpstreamPacketTypeDef* replyPacket);
void Upstream_StateMachine_PollDeviceConnectedReplyCallback(UpstreamPacketTypeDef* replyPacket);
void Upstream_InitStateMachine(void)
{
UpstreamPacketTypeDef* freePacket;
uint16_t i;
uint8_t testDataValue;
Upstream_InitSPI();
//Prepare SPI test packet
freePacket = Upstream_GetFreePacketImmediately();
if (freePacket == NULL)
{
UpstreamState = STATE_ERROR;
return;
}
freePacket->Length16 = UPSTREAM_PACKET_LEN_16;
freePacket->CommandClass = COMMAND_CLASS_INTERFACE;
freePacket->Command = COMMAND_INTERFACE_ECHO;
//Fill our test packet with some junk
testDataValue = 0xFF;
for (i = 0; i < MSC_MEDIA_PACKET; i++)
{
freePacket->Data[i] = testDataValue;
testDataValue += 39;
}
if (Upstream_TransmitPacket(freePacket) == HAL_OK)
{
Upstream_ReceivePacket(Upstream_StateMachine_TestInterfaceReplyCallback);
}
}
//Used by upstream_spi freakout macro, indicates we should stop everything.
void Upstream_StateMachine_SetErrorState(void)
{
UpstreamState = STATE_ERROR;
if ((ConfiguredDeviceClass > COMMAND_CLASS_INTERFACE) &&
(ConfiguredDeviceClass < COMMAND_CLASS_ERROR))
{
USBD_DeInit(&hUsbDeviceFS);
}
}
InterfaceCommandClassTypeDef Upstream_StateMachine_CheckActiveClass(void)
{
if (UpstreamState == STATE_ERROR)
{
return COMMAND_CLASS_ERROR;
}
if ((UpstreamState == STATE_DEVICE_ACTIVE) ||
(UpstreamState == STATE_SUSPENDED))
{
return ConfiguredDeviceClass;
}
//else:
UPSTREAM_STATEMACHINE_FREAKOUT;
return COMMAND_CLASS_INTERFACE;
}
uint32_t Upstream_StateMachine_GetSuspendState(void)
{
if (UpstreamState == STATE_SUSPENDED)
{
return 1;
}
return 0;
}
void Upstream_StateMachine_TestInterfaceReplyCallback(UpstreamPacketTypeDef* replyPacket)
{
uint16_t i;
uint8_t testDataValue;
if (UpstreamState >= STATE_ERROR)
{
return;
}
if ((UpstreamState != STATE_TEST_INTERFACE) ||
(replyPacket == NULL))
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
}
if (replyPacket->Length16 != UPSTREAM_PACKET_LEN_16)
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
}
testDataValue = 0xFF;
for (i = 0; i < MSC_MEDIA_PACKET; i++)
{
if (replyPacket->Data[i] != testDataValue)
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
}
testDataValue += 39;
}
//SPI interface passed checks. Now we wait for a device to be attached to downstream.
Upstream_StateMachine_NotifyDevice(replyPacket);
}
void Upstream_StateMachine_NotifyDevice(UpstreamPacketTypeDef* freePacket)
{
UpstreamState = STATE_WAIT_DEVICE;
freePacket->Length16 = UPSTREAM_PACKET_HEADER_LEN_16;
freePacket->CommandClass = COMMAND_CLASS_INTERFACE;
freePacket->Command = COMMAND_INTERFACE_NOTIFY_DEVICE;
if (Upstream_TransmitPacket(freePacket) == HAL_OK)
{
Upstream_ReceivePacket(Upstream_StateMachine_NotifyDeviceReplyCallback);
}
}
void Upstream_StateMachine_NotifyDeviceReplyCallback(UpstreamPacketTypeDef* replyPacket)
{
InterfaceCommandClassTypeDef newActiveClass = COMMAND_CLASS_INTERFACE;
USBD_ClassTypeDef* newClassPointer;
if (UpstreamState >= STATE_ERROR)
{
return;
}
if ((UpstreamState != STATE_WAIT_DEVICE) ||
(replyPacket == NULL))
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
}
if (replyPacket->Length16 != (UPSTREAM_PACKET_HEADER_LEN_16 + 1))
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
}
switch (replyPacket->Data[0])
{
#ifdef CONFIG_MASS_STORAGE_ENABLED
case COMMAND_CLASS_MASS_STORAGE:
newActiveClass = COMMAND_CLASS_MASS_STORAGE;
newClassPointer = &USBD_MSC;
break;
#endif
#ifdef CONFIG_MOUSE_ENABLED
case COMMAND_CLASS_HID_MOUSE:
newActiveClass = COMMAND_CLASS_HID_MOUSE;
newClassPointer = &USBD_HID;
USBD_HID_PreinitMouse();
break;
#endif
#ifdef CONFIG_KEYBOARD_ENABLED
case COMMAND_CLASS_HID_KEYBOARD:
newActiveClass = COMMAND_CLASS_HID_KEYBOARD;
newClassPointer = &USBD_HID;
USBD_HID_PreinitKeyboard();
break;
#endif
//Add other supported classes here...
}
Upstream_ReleasePacket(replyPacket);
if (newActiveClass == COMMAND_CLASS_INTERFACE)
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
}
//Downstream should never change the active device class without rebooting!
if ((ConfiguredDeviceClass != COMMAND_CLASS_INTERFACE) &&
(ConfiguredDeviceClass != newActiveClass))
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
}
UpstreamState = STATE_DEVICE_ACTIVE;
ConfiguredDeviceClass = newActiveClass;
USB_Device_Init();
USBD_RegisterClass(&hUsbDeviceFS, newClassPointer);
USBD_Start(&hUsbDeviceFS);
//The USB device stack will now receive commands from our host.
//All we need to do is monitor for downstream device disconnection.
}
void Upstream_StateMachine_DeviceDisconnected(void)
{
if ((ConfiguredDeviceClass == COMMAND_CLASS_INTERFACE) ||
(ConfiguredDeviceClass >= COMMAND_CLASS_ERROR))
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
}
USBD_DeInit(&hUsbDeviceFS);
Upstream_GetFreePacket(Upstream_StateMachine_NotifyDevice);
}
//Suspend event activated by our host
void Upstream_StateMachine_Suspend(void)
{
if (UpstreamState >= STATE_SUSPENDED) //Ignore when already suspended, or in error state
{
return;
}
if (UpstreamState != STATE_DEVICE_ACTIVE)
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
}
PollDeviceConnectedTimer = 0;
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 how I'd wakeup the host, IF IT ACTUALLY WORKED!
//USBD_LL_WakeupHost(&hUsbDeviceFS);
//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);
}
//Called by Systick_Handler every 1ms, at high interrupt priority.
void Upstream_StateMachine_PollDeviceConnected(void)
{
UpstreamPacketTypeDef* freePacket;
if ((UpstreamState != STATE_SUSPENDED) ||
(ConfiguredDeviceClass != COMMAND_CLASS_MASS_STORAGE))
{
return;
}
if (++PollDeviceConnectedTimer >= POLL_DEVICE_CONNECTED_TIMEOUT_MS)
{
PollDeviceConnectedTimer = 0;
freePacket = Upstream_GetFreePacketImmediately();
if (freePacket == NULL)
{
UpstreamState = STATE_ERROR;
return;
}
freePacket->Length16 = UPSTREAM_PACKET_HEADER_LEN_16;
freePacket->CommandClass = COMMAND_CLASS_MASS_STORAGE;
freePacket->Command = COMMAND_MSC_POLL_DISCONNECT;
if (Upstream_TransmitPacket(freePacket) == HAL_OK)
{
Upstream_ReceivePacket(Upstream_StateMachine_PollDeviceConnectedReplyCallback);
}
}
}
void Upstream_StateMachine_PollDeviceConnectedReplyCallback(UpstreamPacketTypeDef* replyPacket)
{
if ((UpstreamState != STATE_SUSPENDED) ||
(replyPacket == NULL))
{
UPSTREAM_STATEMACHINE_FREAKOUT;
return;
}
//Downstream device is still connected, so nothing to do here
Upstream_ReleasePacket(replyPacket);
}