mirror of
https://github.com/toyoshim/opipad.git
synced 2024-11-25 21:35:29 +00:00
183 lines
5.6 KiB
C
183 lines
5.6 KiB
C
static char opg_driver_name[] = "OPiPad Gadget HID Driver for NEOGEO mini";
|
|
|
|
#define OPG_USB_VERSION cpu_to_le16(0x110)
|
|
#define OPG_DEVICE_CLASS USB_CLASS_PER_INTERFACE
|
|
#define OPG_DEVICE_SUB_CLASS 0
|
|
#define OPG_DEVICE_PROTOCOL 0
|
|
|
|
#define OPG_VENDOR_ID 0x20bc
|
|
#define OPG_PRODUCT_ID 0x5500
|
|
|
|
#define NO_IDX_PRODUCT 1
|
|
#define NO_IDX_SERIAL 1
|
|
|
|
#define USE_EP_OUT
|
|
|
|
static char opg_hid_report[] = {
|
|
0x05, 0x01, // usage page (desktop)
|
|
0x09, 0x05, // usage (gamepad)
|
|
0xa1, 0x01, // collection (application)
|
|
0x15, 0x00, // logical minimum (0)
|
|
0x25, 0x01, // logical maximum (1)
|
|
0x35, 0x00, // physical minimum (0)
|
|
0x45, 0x01, // physical minimum (1)
|
|
0x75, 0x01, // report size (1)
|
|
0x95, 0x0f, // report count (15)
|
|
0x05, 0x09, // usage page (button)
|
|
0x19, 0x01, // usage minimum (1)
|
|
0x29, 0x0f, // usage maximum (15)
|
|
0x81, 0x02, // input (variable)
|
|
0x95, 0x01, // report count (1)
|
|
0x81, 0x01, // input (constant)
|
|
0x05, 0x01, // usage page (desktop)
|
|
0x25, 0x07, // logical maximum (7)
|
|
0x45, 0x07, // physical maximum (7)
|
|
0x75, 0x04, // report size (4)
|
|
0x95, 0x01, // report count (1)
|
|
0x65, 0x14, // unit (degrees)
|
|
0x09, 0x39, // usage (hat switch)
|
|
0x81, 0x42, // input (variable, null state)
|
|
0x65, 0x00, // unit
|
|
0x95, 0x01, // report count (1)
|
|
0x81, 0x01, // input (constant)
|
|
0x26, 0xff, 0x00, // logical maximum (255)
|
|
0x46, 0xff, 0x00, // physical maximum (255)
|
|
0x09, 0x30, // usage (x)
|
|
0x09, 0x31, // usage (y)
|
|
0x09, 0x32, // usage (z)
|
|
0x09, 0x35, // usage (rz)
|
|
0x75, 0x08, // report size (8)
|
|
0x95, 0x04, // report count (4)
|
|
0x81, 0x02, // input (variable)
|
|
0x05, 0x02, // usage page (simulation)
|
|
0x15, 0x00, // logical minimum (0)
|
|
0x25, 0x01, // logical maximum (1)
|
|
0x45, 0x01, // physical maximum (1)
|
|
0x09, 0xc5, // usage (c5h)
|
|
0x09, 0xc4, // usage (c4h)
|
|
0x09, 0x02, // usage (02h)
|
|
0x75, 0x01, // report size (1)
|
|
0x95, 0x03, // report count (3)
|
|
0x81, 0x02, // input (variable)
|
|
0x95, 0x05, // report count (5)
|
|
0x81, 0x01, // input (constant)
|
|
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_out;
|
|
struct usb_short_endpoint_descriptor ep_in;
|
|
} __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(0x0110),
|
|
.bCountryCode = 0,
|
|
.bNumReports = 1,
|
|
.bReportType = USB_DT_HID_REPORT,
|
|
.wReportLength = cpu_to_le16(sizeof(opg_hid_report)),
|
|
},
|
|
.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 = 32,
|
|
.bInterval = 10,
|
|
},
|
|
.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 = 32,
|
|
.bInterval = 10,
|
|
},
|
|
};
|
|
|
|
static __u8 opg_report[] = {
|
|
// _ _ _ D | C _ B A | _ _ _ _ | START SELECT _ _
|
|
0x00, 0x00,
|
|
|
|
// Hat
|
|
// 7 0 1
|
|
// 6 f 2
|
|
// 5 4 3
|
|
0x0f,
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00,
|
|
};
|
|
|
|
static const char* opg_get_string(int idx) {
|
|
switch (idx) {
|
|
case IDX_MANUFACTURER:
|
|
return "JJ";
|
|
default:
|
|
break;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void opg_update_report(void) {
|
|
const struct gpio_hid_state* state = gpio_get_state();
|
|
opg_report[0] =
|
|
(!state->a ? 0x01 : 0) |
|
|
(!state->b ? 0x02 : 0) |
|
|
(!state->x ? 0x08 : 0) |
|
|
(!state->y ? 0x10 : 0);
|
|
opg_report[1] =
|
|
(!state->back ? 0x04 : 0) | (!state->start ? 0x08 : 0);
|
|
|
|
opg_report[2] =
|
|
!state->up ? (!state->left ? 7 : !state->right ? 1 : 0) :
|
|
!state->down ? (!state->left ? 5 : !state->right ? 3: 4) :
|
|
(!state->left ? 6 : !state->right ? 2 : 15);
|
|
}
|
|
|
|
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 0x0100:
|
|
memcpy(data->ep0_request->buf, opg_report, sizeof(opg_report));
|
|
return sizeof(opg_report);
|
|
default:
|
|
printk("%s: report type/id: %04x\n", opg_driver_name, r->wValue);
|
|
break;
|
|
}
|
|
}
|
|
return -EOPNOTSUPP;
|
|
}
|