blob: 2eb4c69e194cfa54b71dbda31e0b6f02d720c991 [file]
#include "usbd_def.h"
#include "usbd_cdc_if.h"
#include "usb_host.h"
#include "usbh_cdc.h"
#include "CDCCommandHandler.h"
#include "CDCCommandStream.h"
#include "engine/api_private.hpp"
extern CDCCommandStream g_commandStream;
extern USBD_HandleTypeDef hUsbDeviceHS;
extern USBH_HandleTypeDef hUsbHostHS;
using namespace blit;
namespace multiplayer {
bool peer_connected = false;
void send_handshake();
class CDCUserHandler : public CDCCommandHandler
{
public:
StreamResult StreamData(CDCDataStream &dataStream) override {
// get length and allocate buf
if(!buf) {
if(!dataStream.Get(length))
return srNeedData;
delete[] buf; // just in case, prevent leak
buf = new uint8_t[length];
}
// read
uint8_t byte;
while(read < length && dataStream.Get(byte))
buf[read++] = byte;
// done, send to user
if(read == length) {
if(api.message_received)
api.message_received(buf, length);
delete[] buf;
buf = nullptr;
return srFinish;
}
return srContinue;
}
bool StreamInit(CDCFourCC uCommand) override {
length = read = 0;
return true;
}
private:
uint16_t length = 0, read = 0;
uint8_t *buf = nullptr;
};
// this is also a disconnect/disable
class CDCHandshakeHandler : public CDCCommandHandler {
public:
StreamResult StreamData(CDCDataStream &dataStream) override {
uint8_t val;
dataStream.Get(val);
peer_connected = val != 0;
// reply if we're not the host
if(peer_connected && !USB_GetMode(USB_OTG_HS))
send_handshake();
return srFinish;
}
bool StreamInit(CDCFourCC uCommand) override {
return true;
}
};
CDCUserHandler cdc_user_handler;
CDCHandshakeHandler cdc_handshake_handler;
bool enabled = false;
static uint32_t last_handshake_attempt = 0;
void init() {
g_commandStream.AddCommandHandler(CDCCommandHandler::CDCFourCCMake<'U', 'S', 'E', 'R'>::value, &cdc_user_handler);
g_commandStream.AddCommandHandler(CDCCommandHandler::CDCFourCCMake<'M', 'L', 'T', 'I'>::value, &cdc_handshake_handler);
}
void cdc_send(const uint8_t *data, uint16_t length) {
if(USB_GetMode(USB_OTG_HS)) { // host
if(hUsbHostHS.gState != HOST_CLASS)
return;
// FIXME: should this be using the transmit callback and have a queue somewhere?
USBH_CDC_Transmit(&hUsbHostHS, (uint8_t *)data, length);
while(((CDC_HandleTypeDef *) hUsbHostHS.pActiveClass->pData)->data_tx_state != CDC_IDLE){
MX_USB_HOST_Process();
//HOST_CLASS
if(hUsbHostHS.gState != HOST_CLASS)
break; // stopped being connected
}
} else { // device
while(hUsbDeviceHS.dev_state == USBD_STATE_CONFIGURED && CDC_Transmit_HS((uint8_t *)data, length) == USBD_BUSY);
}
}
void send_handshake() {
uint8_t buf[]{'3', '2', 'B', 'L', 'M', 'L', 'T','I', enabled ? 1 : 0};
cdc_send(buf, 9);
}
bool is_connected() {
return enabled && peer_connected;
}
bool is_usb_connected() {
if(USB_GetMode(USB_OTG_HS))
return hUsbHostHS.gState == HOST_CLASS;
else
return hUsbDeviceHS.dev_state == USBD_STATE_CONFIGURED;
}
void set_enabled(bool enabled) {
multiplayer::enabled = enabled;
// disconnect
if(!enabled)
send_handshake();
// if in device mode, disable/enable VBUS sensing
// TODO: this will fail if we switch to host and back
if(!USB_GetMode(USB_OTG_HS)) {
uint32_t USBx_BASE = (uint32_t)USB_OTG_HS; // this is for USBx_DEVICE...
USBx_DEVICE->DCTL |= USB_OTG_DCTL_SDIS;
if(enabled) {
USB_OTG_HS->GINTMSK &= ~(USB_OTG_GINTMSK_SRQIM | USB_OTG_GINTMSK_OTGINT);
USB_OTG_HS->GCCFG &= ~USB_OTG_GCCFG_VBDEN;
USB_OTG_HS->GOTGCTL |= USB_OTG_GOTGCTL_BVALOEN;
USB_OTG_HS->GOTGCTL |= USB_OTG_GOTGCTL_BVALOVAL;
} else {
USB_OTG_HS->GOTGCTL &= ~USB_OTG_GOTGCTL_BVALOEN;
USB_OTG_HS->GOTGCTL &= ~USB_OTG_GOTGCTL_BVALOVAL;
USB_OTG_HS->GCCFG |= USB_OTG_GCCFG_VBDEN;
USB_OTG_HS->GINTMSK |= USB_OTG_GINTMSK_SRQIM | USB_OTG_GINTMSK_OTGINT;
}
USBx_DEVICE->DCTL &= ~USB_OTG_DCTL_SDIS;
}
}
void send_message(const uint8_t *data, uint16_t length) {
if(!peer_connected)
return;
// header
cdc_send((uint8_t *)"32BLUSER", 8);
cdc_send((uint8_t *)&length, 2);
cdc_send((uint8_t *)data, length);
}
void update() {
// reset peer_connected if the USB was disconnected
if(peer_connected && !is_usb_connected())
peer_connected = false;
// attempt handshake if we're the host
if(!peer_connected && USB_GetMode(USB_OTG_HS) && HAL_GetTick() - last_handshake_attempt > 1000) {
send_handshake();
last_handshake_attempt = HAL_GetTick();
}
}
}