static char opg_driver_name[] = "OPiPad Gadget HID Driver for PS4"; #define OPG_USB_VERSION cpu_to_le16(0x200) #define OPG_DEVICE_CLASS USB_CLASS_PER_INTERFACE #define OPG_DEVICE_SUB_CLASS 0 #define OPG_DEVICE_PROTOCOL 0 #define OPG_VENDOR_ID 0x6666 #define OPG_PRODUCT_ID 0x0884 #define USE_EP_OUT static char opg_hid_report[] = { 0x05, 0x01, // usage page (desktop) 0x09, 0x05, // usage (gamepad) 0xa1, 0x01, // collection (application) 0x85, 0x01, // report id (1) 0x09, 0x30, // usage (x) 0x09, 0x31, // usage (y) 0x09, 0x32, // usage (z) 0x09, 0x35, // usage (rz) 0x15, 0x00, // logical minimum (0) 0x26, 0xff, 0x00, // logical maximum (255) 0x75, 0x08, // report size (8) 0x95, 0x04, // report count (4) 0x81, 0x02, // input (variable) 0x09, 0x39, // usage (hat switch) 0x15, 0x00, // logical minimum (0) 0x25, 0x07, // logical maximum (7) 0x35, 0x00, // physical minimum (0) 0x46, 0x3b, 0x01, // physical maximum (315) 0x65, 0x14, // unit (degrees) 0x75, 0x04, // report size (4) 0x95, 0x01, // report count (1) 0x81, 0x42, // input (variable, null state) 0x65, 0x00, // unit 0x05, 0x09, // usage page (button) 0x19, 0x01, // usage minimum (1) 0x29, 0x0e, // usage maximum (14) 0x15, 0x00, // logical minimum (0) 0x25, 0x01, // logical maximum (1) 0x75, 0x01, // report size (1) 0x95, 0x0e, // report count (14) 0x81, 0x02, // input (variable) 0x06, 0x00, 0xff, // usage page (ff00h) 0x09, 0x20, // usage (20h) 0x75, 0x06, // report size (6) 0x95, 0x01, // report count (1) 0x81, 0x02, // input (variable) 0x05, 0x01, // usage page (desktop) 0x09, 0x33, // usage (rx) 0x09, 0x34, // usage (ry) 0x15, 0x00, // logical minimum (0) 0x26, 0xff, 0x00, // logical maximum (255) 0x75, 0x08, // report size (8) 0x95, 0x02, // report count (2) 0x81, 0x02, // input (variable) 0x06, 0x00, 0xff, // usage page (ff00h) 0x09, 0x21, // usage (21h) 0x95, 0x36, // report count (54) 0x81, 0x02, // input (variable) 0x85, 0x05, // report id (5) 0x09, 0x22, // usage (22h) 0x95, 0x1f, // report count (31) 0x91, 0x02, // output (variable) 0x85, 0x03, // report id (3) 0x0a, 0x21, 0x27, // usage (2721h) 0x95, 0x2f, // report count (47) 0xb1, 0x02, // feature (variable) 0xc0, // end collection 0x06, 0xf0, 0xff, // usage page (fff0h) 0x09, 0x40, // usage (40h) 0xa1, 0x01, // collection (application) 0x85, 0xf0, // report id (240) 0x09, 0x47, // usage (47h) 0x95, 0x3f, // report count (63) 0xb1, 0x02, // feature (variable) 0x85, 0xf1, // report id (241) 0x09, 0x48, // usage (48h) 0x95, 0x3f, // report count (63) 0xb1, 0x02, // feature (variable) 0x85, 0xf2, // report id (242) 0x09, 0x49, // usage (49h) 0x95, 0x0f, // report count (15) 0xb1, 0x02, // feature (variable) 0x85, 0xf3, // report id (243) 0x0a, 0x01, 0x47, // usage (4701h) 0x95, 0x07, // report count (7) 0xb1, 0x02, // feature (variable) 0xc0, // end collection }; struct opg_config_descriptor { struct usb_config_descriptor config; struct usb_interface_descriptor interface; struct usb_hid_descriptor hid; struct usb_short_endpoint_descriptor ep_in; struct usb_short_endpoint_descriptor ep_out; } __attribute__ ((packed)) opg_config_desc = { .config = { .bLength = USB_DT_CONFIG_SIZE, .bDescriptorType = USB_DT_CONFIG, .wTotalLength = cpu_to_le16(sizeof(struct opg_config_descriptor)), .bNumInterfaces = 1, .bConfigurationValue = 1, .iConfiguration = IDX_NULL, .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, .bMaxPower = 250, // x 2mA }, .interface = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 0, .bAlternateSetting = 0, .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_HID, .bInterfaceSubClass = 0x00, .bInterfaceProtocol = 0x00, .iInterface = IDX_NULL, }, .hid = { .bLength = sizeof(struct usb_hid_descriptor), .bDescriptorType = USB_DT_HID, .bcdHID = cpu_to_le16(0x0111), .bCountryCode = 0, .bNumReports = 1, .bReportType = USB_DT_HID_REPORT, .wReportLength = cpu_to_le16(sizeof(opg_hid_report)), }, .ep_in = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN | 4, // will be overriden .bmAttributes = USB_ENDPOINT_XFER_INT | USB_ENDPOINT_SYNC_NONE | USB_ENDPOINT_USAGE_DATA, .wMaxPacketSize = 64, .bInterval = 5, }, .ep_out = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT | 3, // will be overriden .bmAttributes = USB_ENDPOINT_XFER_INT | USB_ENDPOINT_SYNC_NONE | USB_ENDPOINT_USAGE_DATA, .wMaxPacketSize = 64, .bInterval = 5, }, }; static __u8 opg_report[] = { // report ID 0x01, // X Y Z Rz 0x80, 0x80, 0x80, 0x80, // 4 Buttons | 4-bits Hat switch 0x00 | 0x08, // 10 Buttons, 6 vendor specific unknown 0x00, 0x00, // Rx Ry 0x00, 0x00, // vendor specific unknown 0x99, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe7, 0xff, 0x6e, 0x20, 0xd9, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00 }; static const char report0303[] = { 0x03, 0x21, 0x27, 0x04, 0x41, 0x00, 0x2c, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const char report03f3[] = { 0xf3, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00 }; static const char* opg_get_string(int idx) { switch (idx) { case IDX_MANUFACTURER: return "TOYOSHIMA-HOUSE"; case IDX_PRODUCT: return "OPiPad PS4 Adaptor"; case IDX_SERIAL: return "0"; default: break; } return NULL; } static void opg_update_report(void) { const struct gpio_hid_state* state = gpio_get_state(); opg_report[1] = !state->left ? 0 : !state->right ? 0xff: 0x80; opg_report[2] = !state->up ? 0 : !state->down ? 0xff: 0x80; opg_report[5] = 0x08 | (!state->y ? 0x80 : 0) | (!state->b ? 0x40 : 0) | (!state->a ? 0x20 : 0) | (!state->x ? 0x10 : 0); opg_report[6] = (!state->start ? 0x20 : 0) | (!state->back ? 0x10 : 0) | (!state->rt ? 0x08 : 0) | (!state->lt ? 0x04 : 0) | (!state->rb ? 0x02 : 0) | (!state->lb ? 0x01 : 0); opg_report[7] = (opg_report[7] & 0xfc) | (!state->meta ? 0x01: 0); opg_report[7] += 4; // count-up } // The per-8mins auth is not implemented in this source code. static int opg_setup( struct usb_gadget* gadget, const struct usb_ctrlrequest* r) { struct driver_data* data = get_gadget_data(gadget); int type = r->bRequestType & USB_TYPE_MASK; printk("%s: bRequestType: %02x, bRequest: %02x, wValue: %04x, " "wIndex: %04x, wLendth: %04x\n", opg_driver_name, r->bRequestType, r->bRequest, r->wValue, r->wIndex, r->wLength); if (type == USB_TYPE_CLASS && r->bRequest == HID_REQ_GET_REPORT) { switch(le16_to_cpu(r->wValue)) { case 0x0303: memcpy(data->ep0_request->buf, report0303, sizeof(report0303)); return sizeof(report0303); case 0x03f2: opg_update_report(); memcpy(data->ep0_request->buf, opg_report, sizeof(opg_report)); return sizeof(opg_report); case 0x03f3: memcpy(data->ep0_request->buf, report03f3, sizeof(report03f3)); return sizeof(report03f3); default: printk("%s: report type/id: %04x\n", opg_driver_name, r->wValue); break; } } return -EOPNOTSUPP; }