#include "Descriptors.h" #include "Config.h" #include "LED.h" #define WEBUSB_ID 0x01 #define MS_OS_ID 0x02 const USB_Descriptor_HIDReport_Datatype_t PROGMEM LEDReport[] = { HID_RI_USAGE_PAGE(8, 0x01), /* Generic Desktop */ HID_RI_USAGE(8, 0x00), /* Undefined */ HID_RI_COLLECTION(8, 0x01), /* Application */ // Globals HID_RI_REPORT_COUNT(8, LED_TOTAL_COUNT), HID_RI_REPORT_SIZE(8, 8), HID_RI_LOGICAL_MINIMUM(8, 0), HID_RI_LOGICAL_MAXIMUM(8, BRIGHTNESS_MAX), HID_RI_USAGE_PAGE(8, 0x0A), // Ordinals // Locals 0x79, STRING_ID_LED_Indiv, //HID_RI_STRING_MINIMUM(8, STRING_ID_LED_Indiv), 0x89, STRING_ID_LED_Indiv + LED_TOTAL_COUNT, //HID_RI_STRING_MAXIMUM(8, STRING_ID_LED_Indiv + LED_COUNT), HID_RI_USAGE_MINIMUM(8, 1), // LED 1 HID_RI_USAGE_MAXIMUM(8, LED_TOTAL_COUNT), // LED 8 + buttons HID_RI_OUTPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE | HID_IOF_NON_VOLATILE), // at least 1 INPUT is also needed to be recognised by Bemanitools HID_RI_USAGE_MINIMUM(8, 1), HID_RI_USAGE_MAXIMUM(8, 1), HID_RI_INPUT(8, HID_IOF_CONSTANT), HID_RI_END_COLLECTION(0), }; const USB_Descriptor_HIDReport_Datatype_t PROGMEM InputsReport[] = { /* Mouse Report * Min/max X/Y -128 to 127 * NOTE: need at least 1 button or report does not work * Relative coordinates */ 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), /* Joystick report adjusted from HID_DESCRIPTOR_JOYSTICK */ HID_RI_USAGE_PAGE(8, 0x01), /* Generic Desktop */ HID_RI_USAGE(8, 0x05), /* Game pad */ HID_RI_COLLECTION(8, 0x01), /* Application */ HID_RI_REPORT_ID(8, HID_REPORTID_JoystickReport), HID_RI_USAGE(8, 0x01), /* Pointer */ HID_RI_COLLECTION(8, 0x00), /* Physical */ HID_RI_USAGE(8, 0x30), /* X axis */ HID_RI_USAGE(8, 0x31), /* Y axis */ HID_RI_LOGICAL_MINIMUM(16, 0), HID_RI_LOGICAL_MAXIMUM(16, JOYSTICK_PPR-1), HID_RI_PHYSICAL_MINIMUM(16, 0), HID_RI_PHYSICAL_MAXIMUM(16, 1), HID_RI_REPORT_COUNT(8, 2), /* 2 axis */ HID_RI_REPORT_SIZE(8, 8), HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), HID_RI_END_COLLECTION(0), HID_RI_USAGE_PAGE(8, 0x09), /* Button */ HID_RI_USAGE_MINIMUM(8, 0x01), /* Button 1 */ HID_RI_USAGE_MAXIMUM(8, SWITCH_COUNT), /* Button x */ HID_RI_LOGICAL_MINIMUM(8, 0x00), HID_RI_LOGICAL_MAXIMUM(8, 0x01), HID_RI_REPORT_SIZE(8, 0x01), HID_RI_REPORT_COUNT(8, SWITCH_COUNT), HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), HID_RI_REPORT_SIZE(8, (SWITCH_COUNT % 8) ? (8 - (SWITCH_COUNT % 8)) : 0), HID_RI_REPORT_COUNT(8, 0x01), HID_RI_INPUT(8, HID_IOF_CONSTANT), HID_RI_END_COLLECTION(0) }; const uint8_t PROGMEM MS_OS_Descriptor[] = { MS_OS_DESCRIPTOR_SET ( MS_OS_CONFIG_SUBSET_HEADER ( 0x00, // Config 0 MS_OS_FUNCTION_SUBSET_HEADER ( INTERFACE_ID_Config, // Interface ID MS_OS_COMPAT_ID_WINUSB, /* I'm not entirely convinced this is actually required, but USB on Windows is finnicky at best, and apparently this is a good idea for composite devices. It's a mystery. */ 0x84, 0x00, //wLength: MS_OS_20_FEATURE_REG_PROPERTY, 0x00, // wDescriptorType: MS_OS_20_FEATURE_REG_PROPERTY: 0x04 (Table 9) 0x07, 0x00, //wPropertyDataType: REG_MULTI_SZ (Table 15) 0x2a, 0x00, //wPropertyNameLength: //bPropertyName: "DeviceInterfaceGUID" 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 0x00, 0x50, 0x00, // wPropertyDataLength //bPropertyData: "{975F44D9-0D08-43FD-8B3E-127CA8AFEF9D}". '{', 0x00, '9', 0x00, '7', 0x00, '5', 0x00, 'F', 0x00, '4', 0x00, '4', 0x00, 'D', 0x00, '9', 0x00, '-', 0x00, '0', 0x00, 'D', 0x00, '0', 0x00, '8', 0x00, '-', 0x00, '4', 0x00, '3', 0x00, 'F', 0x00, 'D', 0x00, '-', 0x00, '8', 0x00, 'B', 0x00, '3', 0x00, 'E', 0x00, '-', 0x00, '1', 0x00, '2', 0x00, '7', 0x00, 'C', 0x00, 'A', 0x00, '8', 0x00, 'A', 0x00, 'F', 0x00, 'E', 0x00, 'F', 0x00, '9', 0x00, 'D', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00 ) ) ) }; const uint8_t PROGMEM BOSDescriptor[] = { BOS_DESCRIPTOR ( 2, // 2 capability descriptors in use WEBUSB_CAPABILITY_DESCRIPTOR(WEBUSB_ID, URL_ID_Config), // Vendor request ID, URL ID // Required to force WinUSB driver for driverless WebUSB compatibility MS_OS_20_CAPABILITY_DESCRIPTOR(MS_OS_ID, sizeof(MS_OS_Descriptor)) // Vendor request ID, Descriptor set length ) }; const USB_Descriptor_Device_t PROGMEM DeviceDescriptor_Standard = { .Header = {.Size = sizeof(USB_Descriptor_Device_t), .Type = DTYPE_Device}, .USBSpecification = VERSION_BCD(2,1,0), .Class = USB_CSCP_NoDeviceClass, .SubClass = USB_CSCP_NoDeviceSubclass, .Protocol = USB_CSCP_NoDeviceProtocol, .Endpoint0Size = FIXED_CONTROL_ENDPOINT_SIZE, // mon.im VID/PID pair, unique! .VendorID = 0x16D0, .ProductID = 0x0A6D, // To provide uniqueness .ReleaseNumber = VERSION_BCD(0,0,3), .ManufacturerStrIndex = STRING_ID_Manufacturer, .ProductStrIndex = STRING_ID_Product, .SerialNumStrIndex = USE_INTERNAL_SERIAL, .NumberOfConfigurations = FIXED_NUM_CONFIGURATIONS }; const USB_Descriptor_Device_t PROGMEM DeviceDescriptor_EAC = { .Header = {.Size = sizeof(USB_Descriptor_Device_t), .Type = DTYPE_Device}, .USBSpecification = VERSION_BCD(2,1,0), .Class = USB_CSCP_NoDeviceClass, .SubClass = USB_CSCP_NoDeviceSubclass, .Protocol = USB_CSCP_NoDeviceProtocol, .Endpoint0Size = FIXED_CONTROL_ENDPOINT_SIZE, // to pretend we are someone else .VendorID = 0x1CCF, .ProductID = 0x1014, // To provide uniqueness .ReleaseNumber = VERSION_BCD(0,0,3), .ManufacturerStrIndex = STRING_ID_Manufacturer, .ProductStrIndex = STRING_ID_Product, .SerialNumStrIndex = USE_INTERNAL_SERIAL, .NumberOfConfigurations = FIXED_NUM_CONFIGURATIONS }; const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = { .Config = { .Header = {.Size = sizeof(USB_Descriptor_Configuration_Header_t), .Type = DTYPE_Configuration}, .TotalConfigurationSize = sizeof(USB_Descriptor_Configuration_t), .TotalInterfaces = 3, .ConfigurationNumber = 1, .ConfigurationStrIndex = STRING_ID_Product, .ConfigAttributes = USB_CONFIG_ATTR_RESERVED, .MaxPowerConsumption = USB_CONFIG_POWER_MA(500) }, .Config_Interface = { .Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface}, .InterfaceNumber = INTERFACE_ID_Config, .AlternateSetting = 0, .TotalEndpoints = 2, .Class = 0xFF, .SubClass = 0xFF, .Protocol = 0xFF, .InterfaceStrIndex = STRING_ID_Config }, .Config_DataOutEndpoint = { .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, .EndpointAddress = CONFIG_OUT_EPADDR, .Attributes = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), .EndpointSize = CONFIG_EPSIZE, // ignored but required .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_Inputs, .AlternateSetting = 0x00, .TotalEndpoints = 1, .Class = HID_CSCP_HIDClass, .SubClass = HID_CSCP_NonBootSubclass, .Protocol = HID_CSCP_NonBootProtocol, .InterfaceStrIndex = STRING_ID_Controls }, .HID1_InputsHID = { .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(InputsReport) }, .HID1_ReportINEndpoint = { .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, .EndpointAddress = INPUTS_EPADDR, .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), .EndpointSize = INPUTS_EPSIZE, .PollingIntervalMS = 0x01 }, .HID2_Interface = { .Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface}, .InterfaceNumber = INTERFACE_ID_LED, .AlternateSetting = 0x00, .TotalEndpoints = 1, .Class = HID_CSCP_HIDClass, .SubClass = HID_CSCP_NonBootSubclass, .Protocol = HID_CSCP_NonBootProtocol, .InterfaceStrIndex = STRING_ID_LED }, .HID2_LEDHID = { .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(LEDReport) }, .HID2_ReportINEndpoint = { .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, .EndpointAddress = LED_EPADDR, .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), .EndpointSize = LED_EPSIZE, .PollingIntervalMS = 255 }, }; const USB_Descriptor_URL_t PROGMEM ConfigURL = URL_STRING_DESCRIPTOR(URL_HTTPS, "mon.im/sdvx/config"); const USB_Descriptor_URL_t PROGMEM LocalhostURL = URL_STRING_DESCRIPTOR(URL_HTTP, "localhost"); const USB_Descriptor_String_t PROGMEM LanguageString = USB_STRING_DESCRIPTOR_ARRAY(LANGUAGE_ID_ENG); 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 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[] = { USB_STRING_DESCRIPTOR(L"L1-B"), USB_STRING_DESCRIPTOR(L"L1-G"), USB_STRING_DESCRIPTOR(L"L1-R"), USB_STRING_DESCRIPTOR(L"L2-B"), USB_STRING_DESCRIPTOR(L"L2-G"), USB_STRING_DESCRIPTOR(L"L2-R"), USB_STRING_DESCRIPTOR(L"L3-B"), USB_STRING_DESCRIPTOR(L"L3-G"), USB_STRING_DESCRIPTOR(L"L3-R"), USB_STRING_DESCRIPTOR(L"L4-B"), USB_STRING_DESCRIPTOR(L"L4-G"), USB_STRING_DESCRIPTOR(L"L4-R"), USB_STRING_DESCRIPTOR(L"L5-B"), USB_STRING_DESCRIPTOR(L"L5-G"), USB_STRING_DESCRIPTOR(L"L5-R"), USB_STRING_DESCRIPTOR(L"L6-B"), USB_STRING_DESCRIPTOR(L"L6-G"), USB_STRING_DESCRIPTOR(L"L6-R"), USB_STRING_DESCRIPTOR(L"L7-B"), USB_STRING_DESCRIPTOR(L"L7-G"), USB_STRING_DESCRIPTOR(L"L7-R"), USB_STRING_DESCRIPTOR(L"L8-B"), USB_STRING_DESCRIPTOR(L"L8-G"), USB_STRING_DESCRIPTOR(L"L8-R"), USB_STRING_DESCRIPTOR(L"BT-A"), USB_STRING_DESCRIPTOR(L"BT-B"), USB_STRING_DESCRIPTOR(L"BT-C"), USB_STRING_DESCRIPTOR(L"BT-D"), USB_STRING_DESCRIPTOR(L"FX-L"), USB_STRING_DESCRIPTOR(L"FX-R"), }; void USB_Process_BOS(void) { const void* Address = NULL; uint16_t Size = 0; if(!(Endpoint_IsSETUPReceived()) || USB_ControlRequest.bmRequestType != (REQDIR_DEVICETOHOST | REQTYPE_VENDOR | REQREC_DEVICE)) { return; } switch(USB_ControlRequest.bRequest) { case WEBUSB_ID: if(USB_ControlRequest.wIndex == WEBUSB_REQUEST_GET_URL && USB_ControlRequest.wValue == URL_ID_Config) { Address = &ConfigURL; Size = pgm_read_byte(&ConfigURL.Header.Size); } break; case MS_OS_ID: if(USB_ControlRequest.wIndex == MS_OS_20_DESCRIPTOR_INDEX) { Address = &MS_OS_Descriptor; Size = sizeof(MS_OS_Descriptor); } break; default: return; } if(Address != NULL) { Endpoint_SelectEndpoint(ENDPOINT_CONTROLEP); Endpoint_ClearSETUP(); Endpoint_Write_Control_PStream_LE(Address, Size); Endpoint_ClearOUT(); } } /** This function is called by the library when in device mode, and must be overridden (see library "USB Descriptors" * documentation) by the application code so that the address and size of a requested descriptor can be given * to the USB library. When the device receives a Get Descriptor request on the control endpoint, this function * is called so that the descriptor details can be passed back and the appropriate descriptor sent back to the * USB host. */ uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, const uint16_t wIndex, const void** const DescriptorAddress) { const uint8_t DescriptorType = (wValue >> 8); const uint8_t DescriptorNumber = (wValue & 0xFF); const void* Address = NULL; uint16_t Size = NO_DESCRIPTOR; switch (DescriptorType) { case DTYPE_Device: if(sdvxConfig.controlMode == CONTROL_EAC) { Address = &DeviceDescriptor_EAC; } else { Address = &DeviceDescriptor_Standard; } Size = sizeof(USB_Descriptor_Device_t); break; case DTYPE_Configuration: Address = &ConfigurationDescriptor; Size = sizeof(USB_Descriptor_Configuration_t); break; case DTYPE_BOS: Address = &BOSDescriptor; Size = sizeof(BOSDescriptor); break; case DTYPE_String: switch (DescriptorNumber) { case STRING_ID_Language: Address = &LanguageString; Size = pgm_read_byte(&LanguageString.Header.Size); break; case STRING_ID_Manufacturer: Address = &ManufacturerString; Size = pgm_read_byte(&ManufacturerString.Header.Size); break; case STRING_ID_Product: Address = &ProductString; Size = pgm_read_byte(&ProductString.Header.Size); break; case STRING_ID_LED: Address = &LEDString; Size = pgm_read_byte(&LEDString.Header.Size); break; case STRING_ID_Controls: Address = &ControlsString; Size = pgm_read_byte(&ControlsString.Header.Size); break; case STRING_ID_Config: Address = &ConfigString; Size = pgm_read_byte(&ConfigString.Header.Size); break; default: if(DescriptorNumber >= STRING_ID_LED_Indiv) { Address = &(LEDString_indiv[STRING_ID_LED_Indiv - DescriptorNumber]); Size = pgm_read_byte(&LEDString_indiv[STRING_ID_LED_Indiv - DescriptorNumber].Header.Size); } } break; case HID_DTYPE_HID: Size = sizeof(USB_HID_Descriptor_HID_t); switch (wIndex) { case INTERFACE_ID_Inputs: Address = &ConfigurationDescriptor.HID1_InputsHID; break; case INTERFACE_ID_LED: Address = &ConfigurationDescriptor.HID2_LEDHID; break; } break; case HID_DTYPE_Report: switch (wIndex) { case INTERFACE_ID_Inputs: Address = &InputsReport; Size = sizeof(InputsReport); break; case INTERFACE_ID_LED: Address = &LEDReport; Size = sizeof(LEDReport); break; } break; } *DescriptorAddress = Address; return Size; }