From 35b254a90f0575d85eeaead1b5199ecd65ca6cad Mon Sep 17 00:00:00 2001 From: William Toohey Date: Thu, 9 Feb 2017 20:38:06 +1000 Subject: [PATCH] =?UTF-8?q?Fix=20config=20comunication=20=F0=9F=99=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Turns out using 6 endpoints when you only support 5 is a recipe for disaster --- .../PocketVoltex/DFU/hid_bootloader_loader.py | 43 ++-- Firmware/PocketVoltex/DFU/libusb-1.0.dll | Bin 0 -> 97280 bytes Firmware/PocketVoltex/Descriptors.c | 183 ++++++++++-------- Firmware/PocketVoltex/Descriptors.h | 55 +++--- Firmware/PocketVoltex/PocketVoltex.c | 61 ++---- Firmware/PocketVoltex/makefile | 2 +- 6 files changed, 171 insertions(+), 173 deletions(-) create mode 100644 Firmware/PocketVoltex/DFU/libusb-1.0.dll diff --git a/Firmware/PocketVoltex/DFU/hid_bootloader_loader.py b/Firmware/PocketVoltex/DFU/hid_bootloader_loader.py index 917d45f..a9487b0 100644 --- a/Firmware/PocketVoltex/DFU/hid_bootloader_loader.py +++ b/Firmware/PocketVoltex/DFU/hid_bootloader_loader.py @@ -24,6 +24,10 @@ from pywinusb import hid from intelhex import IntelHex import time +# new bootloader +import usb1 + +ENDPOINT_SIZE = 32 # Device information table device_info_map = dict() @@ -52,26 +56,23 @@ def get_hid_device_handle(): return valid_hid_devices[0] def bootloader_boot(): - hid_device_filter = hid.HidDeviceFilter(vendor_id=0x16D0, - product_id=0x0A6D, - product_name="Pocket Voltex Config") - - valid_hid_devices = hid_device_filter.get_devices() - - if len(valid_hid_devices) is 0: - return False - else: - try: - hid_device = valid_hid_devices[0] - hid_device.open() - # switch count + static stuff + report id - output_report_data = [0] * (8 + 9 + 1) - # report ID is 0 at byte 0 - output_report_data[1] = 42; - - hid_device.send_output_report(output_report_data) - finally: - hid_device.close() + # eventually the hid loader will use this too + with usb1.USBContext() as context: + voltex = None + for dev in context.getDeviceList(): + if dev.getVendorID() == 0x16D0 and dev.getProductID() == 0x0A6D: + # because there is the pseudo-composite-parent we ignore + try: + voltex = dev.open() + print "Open success!" + except: + print "Open fail" + if voltex is None: + # Device not present, or user is not allowed to access device. + print("Couldn't find device to reboot") + return False + with voltex.claimInterface(0): + voltex.bulkWrite(1, [42] + [0]*(ENDPOINT_SIZE-1)) return True def send_page_data(hid_device, address, data): @@ -93,7 +94,7 @@ def program_device(hex_data, device_info): print("No valid HID device found.") sys.exit(1) print("Rebooted to bootloader") - time.sleep(6) + time.sleep(4) hid_device = get_hid_device_handle() if hid_device is None: print("No valid HID device found.") diff --git a/Firmware/PocketVoltex/DFU/libusb-1.0.dll b/Firmware/PocketVoltex/DFU/libusb-1.0.dll new file mode 100644 index 0000000000000000000000000000000000000000..5377cb454afc6395709cffea5333fc773458ad23 GIT binary patch literal 97280 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 literal 0 HcmV?d00001 diff --git a/Firmware/PocketVoltex/Descriptors.c b/Firmware/PocketVoltex/Descriptors.c index 5ff46d9..15f3783 100644 --- a/Firmware/PocketVoltex/Descriptors.c +++ b/Firmware/PocketVoltex/Descriptors.c @@ -34,20 +34,77 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM LEDReport[] = HID_RI_END_COLLECTION(0), }; -const USB_Descriptor_HIDReport_Datatype_t PROGMEM KeyboardReport[] = +const USB_Descriptor_HIDReport_Datatype_t PROGMEM InputsReport[] = { - // Use the HID class driver's standard Keyboard report - HID_DESCRIPTOR_KEYBOARD(SWITCH_COUNT) -}; - -const USB_Descriptor_HIDReport_Datatype_t PROGMEM MouseReport[] = -{ - /* Use the HID class driver's standard Mouse report. + /* Mouse Report * Min/max X/Y -128 to 127 * NOTE: need at least 1 button or report does not work * Relative coordinates */ - HID_DESCRIPTOR_MOUSE(-128, 127, -128, 127, 1, false) + HID_RI_USAGE_PAGE(8, 0x01), /* Generic Desktop */ + HID_RI_USAGE(8, 0x02), /* Mouse */ + HID_RI_COLLECTION(8, 0x01), /* Application */ + HID_RI_REPORT_ID(8, HID_REPORTID_MouseReport), + HID_RI_USAGE(8, 0x01), /* Pointer */ + HID_RI_COLLECTION(8, 0x00), /* Physical */ + HID_RI_USAGE_PAGE(8, 0x09), /* Button */ + HID_RI_USAGE_MINIMUM(8, 0x01), + HID_RI_USAGE_MAXIMUM(8, 1), + HID_RI_LOGICAL_MINIMUM(8, 0x00), + HID_RI_LOGICAL_MAXIMUM(8, 0x01), + HID_RI_REPORT_COUNT(8, 1), + HID_RI_REPORT_SIZE(8, 0x01), + HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), + HID_RI_REPORT_COUNT(8, 0x01), + HID_RI_REPORT_SIZE(8, 7), /* 7 bits padding for our 1 button */ + HID_RI_INPUT(8, HID_IOF_CONSTANT), + HID_RI_USAGE_PAGE(8, 0x01), /* Generic Desktop */ + HID_RI_USAGE(8, 0x30), /* Usage X */ + HID_RI_USAGE(8, 0x31), /* Usage Y */ + HID_RI_LOGICAL_MINIMUM(16, -128), + HID_RI_LOGICAL_MAXIMUM(16, 127), + HID_RI_PHYSICAL_MINIMUM(16, -128), + HID_RI_PHYSICAL_MAXIMUM(16, 127), + HID_RI_REPORT_COUNT(8, 0x02), + HID_RI_REPORT_SIZE(8, 8), + HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE), + HID_RI_END_COLLECTION(0), + HID_RI_END_COLLECTION(0), + + /* Keyboard Report */ + HID_RI_USAGE_PAGE(8, 0x01), /* Generic Desktop */ + HID_RI_USAGE(8, 0x06), /* Keyboard */ + HID_RI_COLLECTION(8, 0x01), /* Application */ + HID_RI_REPORT_ID(8, HID_REPORTID_KeyboardReport), + HID_RI_USAGE_PAGE(8, 0x07), /* Key Codes */ + HID_RI_USAGE_MINIMUM(8, 0xE0), /* Keyboard Left Control */ + HID_RI_USAGE_MAXIMUM(8, 0xE7), /* Keyboard Right GUI */ + HID_RI_LOGICAL_MINIMUM(8, 0x00), + HID_RI_LOGICAL_MAXIMUM(8, 0x01), + HID_RI_REPORT_SIZE(8, 0x01), + HID_RI_REPORT_COUNT(8, 0x08), + HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), + HID_RI_REPORT_COUNT(8, 0x01), + HID_RI_REPORT_SIZE(8, 0x08), + HID_RI_INPUT(8, HID_IOF_CONSTANT), + HID_RI_USAGE_PAGE(8, 0x08), /* LEDs */ + HID_RI_USAGE_MINIMUM(8, 0x01), /* Num Lock */ + HID_RI_USAGE_MAXIMUM(8, 0x05), /* Kana */ + HID_RI_REPORT_COUNT(8, 0x05), + HID_RI_REPORT_SIZE(8, 0x01), + HID_RI_OUTPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE | HID_IOF_NON_VOLATILE), + HID_RI_REPORT_COUNT(8, 0x01), + HID_RI_REPORT_SIZE(8, 0x03), + HID_RI_OUTPUT(8, HID_IOF_CONSTANT), + HID_RI_LOGICAL_MINIMUM(8, 0x00), + HID_RI_LOGICAL_MAXIMUM(8, 0xFF), + HID_RI_USAGE_PAGE(8, 0x07), /* Keyboard */ + HID_RI_USAGE_MINIMUM(8, 0x00), /* Reserved (no event indicated) */ + HID_RI_USAGE_MAXIMUM(8, 0xFF), + HID_RI_REPORT_COUNT(8, SWITCH_COUNT), + HID_RI_REPORT_SIZE(8, 0x08), + HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE), + HID_RI_END_COLLECTION(0), }; @@ -108,7 +165,7 @@ const USB_Descriptor_Device_t PROGMEM DeviceDescriptor = .ManufacturerStrIndex = STRING_ID_Manufacturer, .ProductStrIndex = STRING_ID_Product, - .SerialNumStrIndex = STRING_ID_Product, + .SerialNumStrIndex = USE_INTERNAL_SERIAL, .NumberOfConfigurations = FIXED_NUM_CONFIGURATIONS }; @@ -120,7 +177,7 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = .Header = {.Size = sizeof(USB_Descriptor_Configuration_Header_t), .Type = DTYPE_Configuration}, .TotalConfigurationSize = sizeof(USB_Descriptor_Configuration_t), - .TotalInterfaces = 4, + .TotalInterfaces = 3, .ConfigurationNumber = 1, .ConfigurationStrIndex = STRING_ID_Product, @@ -146,17 +203,6 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = .InterfaceStrIndex = STRING_ID_Config }, - .Config_DataInEndpoint = - { - .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, - - .EndpointAddress = CONFIG_IN_EPADDR, - .Attributes = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), - .EndpointSize = CONFIG_EPSIZE, - // ignored but required - .PollingIntervalMS = 0x05 - }, - .Config_DataOutEndpoint = { .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, @@ -168,11 +214,22 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = .PollingIntervalMS = 0x05 }, + .Config_DataInEndpoint = + { + .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, + + .EndpointAddress = CONFIG_IN_EPADDR, + .Attributes = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), + .EndpointSize = CONFIG_EPSIZE, + // ignored but required + .PollingIntervalMS = 0x05 + }, + .HID1_Interface = { .Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface}, - .InterfaceNumber = INTERFACE_ID_Keyboard, + .InterfaceNumber = INTERFACE_ID_Inputs, .AlternateSetting = 0x00, .TotalEndpoints = 1, @@ -181,10 +238,10 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = .SubClass = HID_CSCP_NonBootSubclass, .Protocol = HID_CSCP_NonBootProtocol, - .InterfaceStrIndex = NO_DESCRIPTOR + .InterfaceStrIndex = STRING_ID_Controls }, - .HID1_KeyboardHID = + .HID1_InputsHID = { .Header = {.Size = sizeof(USB_HID_Descriptor_HID_t), .Type = HID_DTYPE_HID}, @@ -192,57 +249,20 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = .CountryCode = 0x00, .TotalReportDescriptors = 1, .HIDReportType = HID_DTYPE_Report, - .HIDReportLength = sizeof(KeyboardReport) + .HIDReportLength = sizeof(InputsReport) }, .HID1_ReportINEndpoint = { .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, - .EndpointAddress = KEYBOARD_EPADDR, + .EndpointAddress = INPUTS_EPADDR, .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), - .EndpointSize = KEYBOARD_EPSIZE, + .EndpointSize = INPUTS_EPSIZE, .PollingIntervalMS = 0x01 }, - .HID2_MouseInterface = - { - .Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface}, - - .InterfaceNumber = INTERFACE_ID_Mouse, - .AlternateSetting = 0x00, - - .TotalEndpoints = 1, - - .Class = HID_CSCP_HIDClass, - .SubClass = HID_CSCP_BootSubclass, - .Protocol = HID_CSCP_MouseBootProtocol, - - .InterfaceStrIndex = STRING_ID_Knobs - }, - - .HID2_MouseHID = - { - .Header = {.Size = sizeof(USB_HID_Descriptor_HID_t), .Type = HID_DTYPE_HID}, - - .HIDSpec = VERSION_BCD(1,1,1), - .CountryCode = 0x00, - .TotalReportDescriptors = 1, - .HIDReportType = HID_DTYPE_Report, - .HIDReportLength = sizeof(MouseReport) - }, - - .HID2_ReportINEndpoint = - { - .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, - - .EndpointAddress = MOUSE_IN_EPADDR, - .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), - .EndpointSize = MOUSE_EPSIZE, - .PollingIntervalMS = 0x01 - }, - - .HID3_Interface = + .HID2_Interface = { .Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface}, @@ -258,7 +278,7 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = .InterfaceStrIndex = STRING_ID_LED }, - .HID3_LEDHID = + .HID2_LEDHID = { .Header = {.Size = sizeof(USB_HID_Descriptor_HID_t), .Type = HID_DTYPE_HID}, @@ -269,7 +289,7 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = .HIDReportLength = sizeof(LEDReport) }, - .HID3_ReportINEndpoint = + .HID2_ReportINEndpoint = { .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, @@ -288,7 +308,7 @@ const USB_Descriptor_String_t PROGMEM LanguageString = USB_STRING_DESCRIPTOR_ARR const USB_Descriptor_String_t PROGMEM ManufacturerString = USB_STRING_DESCRIPTOR(L"mon.im"); const USB_Descriptor_String_t PROGMEM ProductString = USB_STRING_DESCRIPTOR(L"Pocket Voltex"); const USB_Descriptor_String_t PROGMEM LEDString = USB_STRING_DESCRIPTOR(L"Pocket Voltex LEDs"); -const USB_Descriptor_String_t PROGMEM KnobString = USB_STRING_DESCRIPTOR(L"Pocket Voltex Knobs"); +const USB_Descriptor_String_t PROGMEM ControlsString = USB_STRING_DESCRIPTOR(L"Pocket Voltex Controls"); const USB_Descriptor_String_t PROGMEM ConfigString = USB_STRING_DESCRIPTOR(L"Pocket Voltex Config"); // There may be a better way to do this const USB_Descriptor_String_t PROGMEM LEDString_indiv[] = { @@ -421,9 +441,9 @@ uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, Address = &LEDString; Size = pgm_read_byte(&LEDString.Header.Size); break; - case STRING_ID_Knobs: - Address = &KnobString; - Size = pgm_read_byte(&KnobString.Header.Size); + case STRING_ID_Controls: + Address = &ControlsString; + Size = pgm_read_byte(&ControlsString.Header.Size); break; case STRING_ID_Config: Address = &ConfigString; @@ -439,26 +459,19 @@ uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, case HID_DTYPE_HID: Size = sizeof(USB_HID_Descriptor_HID_t); switch (wIndex) { - case INTERFACE_ID_Keyboard: - Address = &ConfigurationDescriptor.HID1_KeyboardHID; - break; - case INTERFACE_ID_Mouse: - Address = &ConfigurationDescriptor.HID2_MouseHID; + case INTERFACE_ID_Inputs: + Address = &ConfigurationDescriptor.HID1_InputsHID; break; case INTERFACE_ID_LED: - Address = &ConfigurationDescriptor.HID3_LEDHID; + Address = &ConfigurationDescriptor.HID2_LEDHID; break; } break; case HID_DTYPE_Report: switch (wIndex) { - case INTERFACE_ID_Keyboard: - Address = &KeyboardReport; - Size = sizeof(KeyboardReport); - break; - case INTERFACE_ID_Mouse: - Address = &MouseReport; - Size = sizeof(MouseReport); + case INTERFACE_ID_Inputs: + Address = &InputsReport; + Size = sizeof(InputsReport); break; case INTERFACE_ID_LED: Address = &LEDReport; diff --git a/Firmware/PocketVoltex/Descriptors.h b/Firmware/PocketVoltex/Descriptors.h index 459a7cd..30bfd96 100644 --- a/Firmware/PocketVoltex/Descriptors.h +++ b/Firmware/PocketVoltex/Descriptors.h @@ -55,23 +55,18 @@ // Config interface as we can't talk to composite directly USB_Descriptor_Interface_t Config_Interface; - USB_Descriptor_Endpoint_t Config_DataInEndpoint; USB_Descriptor_Endpoint_t Config_DataOutEndpoint; + USB_Descriptor_Endpoint_t Config_DataInEndpoint; - // Keyboard HID Interface + // Inputs HID Interface USB_Descriptor_Interface_t HID1_Interface; - USB_HID_Descriptor_HID_t HID1_KeyboardHID; + USB_HID_Descriptor_HID_t HID1_InputsHID; USB_Descriptor_Endpoint_t HID1_ReportINEndpoint; - // Mouse HID Interface - USB_Descriptor_Interface_t HID2_MouseInterface; - USB_HID_Descriptor_HID_t HID2_MouseHID; - USB_Descriptor_Endpoint_t HID2_ReportINEndpoint; - // LED HID Interface for pretty lights - USB_Descriptor_Interface_t HID3_Interface; - USB_HID_Descriptor_HID_t HID3_LEDHID; - USB_Descriptor_Endpoint_t HID3_ReportINEndpoint; + USB_Descriptor_Interface_t HID2_Interface; + USB_HID_Descriptor_HID_t HID2_LEDHID; + USB_Descriptor_Endpoint_t HID2_ReportINEndpoint; } USB_Descriptor_Configuration_t; /** Enum for the device interface descriptor IDs within the device. @@ -79,9 +74,8 @@ enum InterfaceDescriptors_t { INTERFACE_ID_Config = 0, /**< Config interface descriptor ID */ - INTERFACE_ID_Keyboard = 1, /**< Keyboard interface descriptor ID */ - INTERFACE_ID_Mouse = 2, /**< Mouse interface descriptor ID */ - INTERFACE_ID_LED = 3, /**< LED interface descriptor ID */ + INTERFACE_ID_Inputs = 1, /**< Keyboard and mouse interface descriptor ID */ + INTERFACE_ID_LED = 2, /**< LED interface descriptor ID */ }; /** Enum for the device string descriptor IDs within the device. Each string descriptor should @@ -91,7 +85,7 @@ STRING_ID_Language = 0, /**< Supported Languages string descriptor ID (must be zero) */ STRING_ID_Manufacturer = 1, /**< Manufacturer string ID */ STRING_ID_Product = 2, /**< Product string ID */ - STRING_ID_Knobs = 3, + STRING_ID_Controls = 3, STRING_ID_Config = 4, STRING_ID_LED = 5, /**< LED string ID */ STRING_ID_LED_Indiv = 6, @@ -102,21 +96,30 @@ URL_ID_Localhost = 1, URL_ID_Config = 2, }; + + /** Enum for the HID report IDs used in the device. */ + enum + { + HID_REPORTID_MouseReport = 0x01, + HID_REPORTID_KeyboardReport = 0x02, + } HID_Report_IDs; /* Macros: */ - #define CONFIG_IN_EPADDR (ENDPOINT_DIR_IN | 1) - #define CONFIG_OUT_EPADDR (ENDPOINT_DIR_OUT | 5) - #define KEYBOARD_EPADDR (ENDPOINT_DIR_IN | 2) - #define MOUSE_IN_EPADDR (ENDPOINT_DIR_IN | 3) + #define CONFIG_OUT_EPADDR (ENDPOINT_DIR_OUT | 1) + #define CONFIG_IN_EPADDR (ENDPOINT_DIR_IN | 2) + #define INPUTS_EPADDR (ENDPOINT_DIR_IN | 3) #define LED_EPADDR (ENDPOINT_DIR_IN | 4) - #define KEYBOARD_EPSIZE SWITCH_COUNT + 2 - #define MOUSE_EPSIZE 8 - #define LED_EPSIZE LED_TOTAL_COUNT - // MUST be 8, 16, 32 or 64 bytes - #define CONFIG_EPSIZE COMMAND_BYTES <= 8 ? 8 : \ - COMMAND_BYTES <= 16 ? 16 : \ - COMMAND_BYTES <= 32 ? 32 : 64 // if it's larger than 64 bytes, you're out of luck + // the board only supports endpoints of 8, 16, 32 or 64 bytes + #define ROUND_UP(bytes) (bytes <= 8 ? 8 : \ + bytes <= 16 ? 16 : \ + bytes <= 32 ? 32 : 64) // if it's larger than 64 bytes, you're out of luck + + // acts like a union, mouse is 8 bytes + #define INPUTS_EPSIZE ROUND_UP(MAX(SWITCH_COUNT + 2, 8)) + #define LED_EPSIZE ROUND_UP(LED_TOTAL_COUNT) + // ignoring board limits, bulk endpoints MUST be 8, 16, 32 or 64 bytes + #define CONFIG_EPSIZE ROUND_UP(COMMAND_BYTES) /* Function Prototypes: */ uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, diff --git a/Firmware/PocketVoltex/PocketVoltex.c b/Firmware/PocketVoltex/PocketVoltex.c index 2e165ce..801c257 100644 --- a/Firmware/PocketVoltex/PocketVoltex.c +++ b/Firmware/PocketVoltex/PocketVoltex.c @@ -34,43 +34,28 @@ typedef struct } LED_Report_t; /** Buffer to hold the previously generated Keyboard HID report, for comparison purposes inside the HID class driver. */ -static uint8_t PrevKeyboardHIDReportBuffer[sizeof(Keyboard_Report_t)]; -static uint8_t PrevMouseHIDReportBuffer[sizeof(USB_MouseReport_Data_t)]; +static uint8_t PrevInputsHIDReportBuffer[MAX(sizeof(Keyboard_Report_t), sizeof(USB_MouseReport_Data_t))]; static uint8_t PrevLEDHIDReportBuffer[sizeof(LED_Report_t)]; +static uint8_t sendKeyboard = 0; + /** LUFA HID Class driver interface configuration and state information. This structure is * passed to all HID Class driver functions, so that multiple instances of the same class * within a device can be differentiated from one another. */ -USB_ClassInfo_HID_Device_t Keyboard_HID_Interface = +USB_ClassInfo_HID_Device_t Inputs_HID_Interface = { .Config = { - .InterfaceNumber = INTERFACE_ID_Keyboard, + .InterfaceNumber = INTERFACE_ID_Inputs, .ReportINEndpoint = { - .Address = KEYBOARD_EPADDR, - .Size = KEYBOARD_EPSIZE, + .Address = INPUTS_EPADDR, + .Size = INPUTS_EPSIZE, .Banks = 1, }, - .PrevReportINBuffer = PrevKeyboardHIDReportBuffer, - .PrevReportINBufferSize = sizeof(PrevKeyboardHIDReportBuffer), - }, -}; - -USB_ClassInfo_HID_Device_t Mouse_HID_Interface = -{ - .Config = - { - .InterfaceNumber = INTERFACE_ID_Mouse, - .ReportINEndpoint = - { - .Address = MOUSE_IN_EPADDR, - .Size = MOUSE_EPSIZE, - .Banks = 1, - }, - .PrevReportINBuffer = PrevMouseHIDReportBuffer, - .PrevReportINBufferSize = sizeof(PrevMouseHIDReportBuffer), + .PrevReportINBuffer = PrevInputsHIDReportBuffer, + .PrevReportINBufferSize = sizeof(PrevInputsHIDReportBuffer), }, }; @@ -170,8 +155,7 @@ int main(void) for (;;) { - HID_Device_USBTask(&Keyboard_HID_Interface); - HID_Device_USBTask(&Mouse_HID_Interface); + HID_Device_USBTask(&Inputs_HID_Interface); HID_Device_USBTask(&LED_HID_Interface); USB_USBTask(); @@ -253,11 +237,12 @@ bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDIn void* ReportData, uint16_t* const ReportSize) { - if(ReportType != HID_REPORT_ITEM_In) { + if(ReportType != HID_REPORT_ITEM_In || HIDInterfaceInfo != &Inputs_HID_Interface) { *ReportSize = 0; return false; } - if (HIDInterfaceInfo == &Keyboard_HID_Interface) { + sendKeyboard ^= 1; + if (sendKeyboard) { Keyboard_Report_t* KeyboardReport = (Keyboard_Report_t*)ReportData; // Only the first 4 bytes are read by bemanitools, so let's use the first @@ -282,11 +267,11 @@ bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDIn } } + *ReportID = HID_REPORTID_KeyboardReport; *ReportSize = sizeof(Keyboard_Report_t); switchesChanged = 0; - return true; - } else if(HIDInterfaceInfo == &Mouse_HID_Interface) { + } else { USB_MouseReport_Data_t* MouseReport = (USB_MouseReport_Data_t*)ReportData; MouseReport->X = encoder_get(0); @@ -297,11 +282,10 @@ bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDIn encoder_set(0, 0); encoder_set(1, 0); + *ReportID = HID_REPORTID_MouseReport; *ReportSize = sizeof(USB_MouseReport_Data_t); - return true; } - *ReportSize = 0; - return false; + return true; } /** HID class driver callback function for the processing of HID reports from the host. @@ -356,10 +340,9 @@ void EVENT_USB_Device_Disconnect(void) /** Event handler for the library USB Configuration Changed event. */ void EVENT_USB_Device_ConfigurationChanged(void) { - Endpoint_ConfigureEndpoint(CONFIG_IN_EPADDR, EP_TYPE_BULK, CONFIG_EPSIZE, 1); Endpoint_ConfigureEndpoint(CONFIG_OUT_EPADDR, EP_TYPE_BULK, CONFIG_EPSIZE, 1); - HID_Device_ConfigureEndpoints(&Keyboard_HID_Interface); - HID_Device_ConfigureEndpoints(&Mouse_HID_Interface); + Endpoint_ConfigureEndpoint(CONFIG_IN_EPADDR, EP_TYPE_BULK, CONFIG_EPSIZE, 1); + HID_Device_ConfigureEndpoints(&Inputs_HID_Interface); HID_Device_ConfigureEndpoints(&LED_HID_Interface); USB_Device_EnableSOFEvents(); @@ -369,16 +352,14 @@ void EVENT_USB_Device_ConfigurationChanged(void) void EVENT_USB_Device_ControlRequest(void) { USB_Process_BOS(); - HID_Device_ProcessControlRequest(&Keyboard_HID_Interface); - HID_Device_ProcessControlRequest(&Mouse_HID_Interface); + HID_Device_ProcessControlRequest(&Inputs_HID_Interface); HID_Device_ProcessControlRequest(&LED_HID_Interface); } /** Event handler for the USB device Start Of Frame event. */ void EVENT_USB_Device_StartOfFrame(void) { - HID_Device_MillisecondElapsed(&Keyboard_HID_Interface); - HID_Device_MillisecondElapsed(&Mouse_HID_Interface); + HID_Device_MillisecondElapsed(&Inputs_HID_Interface); HID_Device_MillisecondElapsed(&LED_HID_Interface); if(hidTimeout > HID_LED_TIMEOUT) { diff --git a/Firmware/PocketVoltex/makefile b/Firmware/PocketVoltex/makefile index 4a3c460..188e7e5 100644 --- a/Firmware/PocketVoltex/makefile +++ b/Firmware/PocketVoltex/makefile @@ -65,4 +65,4 @@ flashboot: $(AVRDUDE) -U flash:w:DFU/BootloaderHID.hex:i flash: all - python DFU/hid_bootloader_loader.py atmega16u2 PocketVoltex.hex + cd DFU && python hid_bootloader_loader.py atmega16u2 ../PocketVoltex.hex