#ifndef USB_GAMEPAD_H #define USB_GAMEPAD_H #include #ifndef GAMEPAD_CLASS #define GAMEPAD_CLASS UsbGamepad #endif #include "../common.h" #include "HID.h" // ATT: 20 chars max (including NULL at the end) according to Arduino source code. // Additionally serial number is used to differentiate arduino projects to have different button maps! const char* gp_serial = "SF30 Pro"; class Gamepad_ : public PluggableUSBModule { private: uint8_t reportId; protected: uint8_t epType[1]; uint8_t protocol; uint8_t idle; /* int getInterface(uint8_t* interfaceCount); int getDescriptor(USBSetup& setup); uint8_t getShortName(char *name); bool setup(USBSetup& setup); */ int getInterface(uint8_t* interfaceCount) { *interfaceCount += 1; // uses 1 HIDDescriptor hidInterface = {D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_NONE, HID_PROTOCOL_NONE), D_HIDREPORT(sizeof(_hidReportDescriptor)), D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01)}; return USB_SendControl(0, &hidInterface, sizeof(hidInterface)); } int getDescriptor(USBSetup& setup) { // Check if this is a HID Class Descriptor request if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) { return 0; } if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) { return 0; } // In a HID Class Descriptor wIndex cointains the interface number if (setup.wIndex != pluggedInterface) { return 0; } // Reset the protocol on reenumeration. Normally the host should not assume the state of the protocol // due to the USB specs, but Windows and Linux just assumes its in report mode. protocol = HID_REPORT_PROTOCOL; this->sentHidReportDescriptor = true; return USB_SendControl(TRANSFER_PGM, _hidReportDescriptor, sizeof(_hidReportDescriptor)); } bool setup(USBSetup& setup) { if (pluggedInterface != setup.wIndex) { return false; } uint8_t request = setup.bRequest; uint8_t requestType = setup.bmRequestType; if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE) { if (request == HID_GET_REPORT) { // TODO: HID_GetReport(); return true; } if (request == HID_GET_PROTOCOL) { // TODO: Send8(protocol); return true; } } if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) { if (request == HID_SET_PROTOCOL) { protocol = setup.wValueL; return true; } if (request == HID_SET_IDLE) { idle = setup.wValueL; return true; } if (request == HID_SET_REPORT) { } } return false; } uint8_t getShortName(char* name) { if (!next) { strcpy(name, gp_serial); return strlen(name); } return 0; } public: boolean sentHidReportDescriptor = false; Gamepad_(void) : PluggableUSBModule(1, 1, epType), protocol(HID_REPORT_PROTOCOL), idle(1) { epType[0] = EP_TYPE_INTERRUPT_IN; PluggableUSB().plug(this); } void send(const void* d, int len) { USB_Send(pluggedEndpoint | TRANSFER_RELEASE, d, len); } }; class UsbGamepad : public AbstractGamepad { public: String deviceManufacturer; String deviceName; Gamepad_ gamepad[GAMEPAD_COUNT]; UsbGamepad(String deviceName = "8BitDo SN30 Pro+", String deviceManufacturer = "8Bitdo SF30 Pro") : AbstractGamepad() { this->deviceName = deviceName; this->deviceManufacturer = deviceManufacturer; } virtual bool isConnected(void) { // if the first one is connected, we assume they all are return gamepad[0].sentHidReportDescriptor; } virtual void sendHidReport(const uint8_t cIdx, const void* d, int len) { gamepad[cIdx].send(d, len); } }; #endif // USB_GAMEPAD_H