/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware Copyright (C) 2007-2016 Raphael Assenat This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include "main.h" #include "util.h" #include "usart1.h" #include "usb.h" #include "gamepads.h" #include "bootloader.h" #include "gcn64_protocol.h" #include "n64.h" #include "gamecube.h" #include "usbpad.h" #include "eeprom.h" #include "hiddata.h" #include "usbstrings.h" #include "intervaltimer.h" #include "intervaltimer2.h" #include "requests.h" #include "stkchk.h" #define MAX_PLAYERS 2 #define GCN64_USB_PID 0x0060 #define N64_USB_PID 0x0061 #define GC_USB_PID 0x0062 #define DUAL_GCN64_USB_PID 0x0063 #define DUAL_N64_USB_PID 0x0064 #define DUAL_GC_USB_PID 0x0065 #define KEYBOARD_PID 0x0066 #define KEYBOARD_PID2 0x0067 #define KEYBOARD_JS_PID 0x0068 int keyboard_main(void); /* Those .c files are included rather than linked for we * want the sizeof() operator to work on the arrays */ #include "reportdesc.c" #include "dataHidReport.c" #define MAX_READ_ERRORS 30 static uint8_t error_count[MAX_PLAYERS] = { }; struct cfg0 { struct usb_configuration_descriptor configdesc; struct usb_interface_descriptor interface; struct usb_hid_descriptor hid; struct usb_endpoint_descriptor ep1_in; struct usb_interface_descriptor interface_admin; struct usb_hid_descriptor hid_data; struct usb_endpoint_descriptor ep2_in; }; static const struct cfg0 cfg0 PROGMEM = { .configdesc = { .bLength = sizeof(struct usb_configuration_descriptor), .bDescriptorType = CONFIGURATION_DESCRIPTOR, .wTotalLength = sizeof(cfg0), // includes all descriptors returned together .bNumInterfaces = 1 + 1, // one interface per player + one management interface .bConfigurationValue = 1, .bmAttributes = CFG_DESC_ATTR_RESERVED, // set Self-powred and remote-wakeup here if needed. .bMaxPower = 25, // for 50mA }, // Main interface, HID (player 1) .interface = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = INTERFACE_DESCRIPTOR, .bInterfaceNumber = 0, .bAlternateSetting = 0, .bNumEndpoints = 1, .bInterfaceClass = USB_DEVICE_CLASS_HID, .bInterfaceSubClass = HID_SUBCLASS_NONE, .bInterfaceProtocol = HID_PROTOCOL_NONE, }, .hid = { .bLength = sizeof(struct usb_hid_descriptor), .bDescriptorType = HID_DESCRIPTOR, .bcdHid = 0x0101, .bCountryCode = HID_COUNTRY_NOT_SUPPORTED, .bNumDescriptors = 1, // Only a report descriptor .bClassDescriptorType = REPORT_DESCRIPTOR, .wClassDescriptorLength = sizeof(gcn64_usbHidReportDescriptor), }, .ep1_in = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = ENDPOINT_DESCRIPTOR, .bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 1, // 0x81 .bmAttributes = TRANSFER_TYPE_INT, .wMaxPacketsize = 16, .bInterval = LS_FS_INTERVAL_MS(1), }, // Second HID interface for config and update .interface_admin = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = INTERFACE_DESCRIPTOR, .bInterfaceNumber = 1, .bAlternateSetting = 0, .bNumEndpoints = 1, .bInterfaceClass = USB_DEVICE_CLASS_HID, .bInterfaceSubClass = HID_SUBCLASS_NONE, .bInterfaceProtocol = HID_PROTOCOL_NONE, }, .hid_data = { .bLength = sizeof(struct usb_hid_descriptor), .bDescriptorType = HID_DESCRIPTOR, .bcdHid = 0x0101, .bCountryCode = HID_COUNTRY_NOT_SUPPORTED, .bNumDescriptors = 1, // Only a report descriptor .bClassDescriptorType = REPORT_DESCRIPTOR, .wClassDescriptorLength = sizeof(dataHidReport), }, .ep2_in = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = ENDPOINT_DESCRIPTOR, .bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 2, // 0x82 .bmAttributes = TRANSFER_TYPE_INT, .wMaxPacketsize = 64, .bInterval = LS_FS_INTERVAL_MS(1), }, }; static const struct cfg0 cfg0_kb PROGMEM = { .configdesc = { .bLength = sizeof(struct usb_configuration_descriptor), .bDescriptorType = CONFIGURATION_DESCRIPTOR, .wTotalLength = sizeof(cfg0), // includes all descriptors returned together .bNumInterfaces = 1 + 1, // one interface per player + one management interface .bConfigurationValue = 1, .bmAttributes = CFG_DESC_ATTR_RESERVED, // set Self-powred and remote-wakeup here if needed. .bMaxPower = 25, // for 50mA }, // Main interface, HID Keyboard .interface = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = INTERFACE_DESCRIPTOR, .bInterfaceNumber = 0, .bAlternateSetting = 0, .bNumEndpoints = 1, .bInterfaceClass = USB_DEVICE_CLASS_HID, .bInterfaceSubClass = HID_SUBCLASS_NONE, .bInterfaceProtocol = HID_PROTOCOL_NONE, }, .hid = { .bLength = sizeof(struct usb_hid_descriptor), .bDescriptorType = HID_DESCRIPTOR, .bcdHid = 0x0101, .bCountryCode = HID_COUNTRY_NOT_SUPPORTED, .bNumDescriptors = 1, // Only a report descriptor .bClassDescriptorType = REPORT_DESCRIPTOR, .wClassDescriptorLength = sizeof(gcKeyboardReport), }, .ep1_in = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = ENDPOINT_DESCRIPTOR, .bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 1, // 0x81 .bmAttributes = TRANSFER_TYPE_INT, .wMaxPacketsize = 16, .bInterval = LS_FS_INTERVAL_MS(1), }, // Second HID interface for config and update .interface_admin = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = INTERFACE_DESCRIPTOR, .bInterfaceNumber = 1, .bAlternateSetting = 0, .bNumEndpoints = 1, .bInterfaceClass = USB_DEVICE_CLASS_HID, .bInterfaceSubClass = HID_SUBCLASS_NONE, .bInterfaceProtocol = HID_PROTOCOL_NONE, }, .hid_data = { .bLength = sizeof(struct usb_hid_descriptor), .bDescriptorType = HID_DESCRIPTOR, .bcdHid = 0x0101, .bCountryCode = HID_COUNTRY_NOT_SUPPORTED, .bNumDescriptors = 1, // Only a report descriptor .bClassDescriptorType = REPORT_DESCRIPTOR, .wClassDescriptorLength = sizeof(dataHidReport), }, .ep2_in = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = ENDPOINT_DESCRIPTOR, .bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 2, // 0x82 .bmAttributes = TRANSFER_TYPE_INT, .wMaxPacketsize = 64, .bInterval = LS_FS_INTERVAL_MS(1), }, }; struct cfg0_2p { struct usb_configuration_descriptor configdesc; struct usb_interface_descriptor interface; struct usb_hid_descriptor hid; struct usb_endpoint_descriptor ep1_in; struct usb_interface_descriptor interface_p2; struct usb_hid_descriptor hid_p2; struct usb_endpoint_descriptor ep2_in; struct usb_interface_descriptor interface_admin; struct usb_hid_descriptor hid_data; struct usb_endpoint_descriptor ep3_in; }; static const struct cfg0_2p cfg0_2p PROGMEM = { .configdesc = { .bLength = sizeof(struct usb_configuration_descriptor), .bDescriptorType = CONFIGURATION_DESCRIPTOR, .wTotalLength = sizeof(cfg0_2p), // includes all descriptors returned together .bNumInterfaces = 2 + 1, // one interface per player + one management interface .bConfigurationValue = 1, .bmAttributes = CFG_DESC_ATTR_RESERVED, // set Self-powred and remote-wakeup here if needed. .bMaxPower = 25, // for 50mA }, // Main interface, HID (player 1) .interface = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = INTERFACE_DESCRIPTOR, .bInterfaceNumber = 0, .bAlternateSetting = 0, .bNumEndpoints = 1, .bInterfaceClass = USB_DEVICE_CLASS_HID, .bInterfaceSubClass = HID_SUBCLASS_NONE, .bInterfaceProtocol = HID_PROTOCOL_NONE, }, .hid = { .bLength = sizeof(struct usb_hid_descriptor), .bDescriptorType = HID_DESCRIPTOR, .bcdHid = 0x0101, .bCountryCode = HID_COUNTRY_NOT_SUPPORTED, .bNumDescriptors = 1, // Only a report descriptor .bClassDescriptorType = REPORT_DESCRIPTOR, .wClassDescriptorLength = sizeof(gcn64_usbHidReportDescriptor), }, .ep1_in = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = ENDPOINT_DESCRIPTOR, .bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 1, // 0x81 .bmAttributes = TRANSFER_TYPE_INT, .wMaxPacketsize = 16, .bInterval = LS_FS_INTERVAL_MS(1), }, // Main interface, HID (player 2) .interface_p2 = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = INTERFACE_DESCRIPTOR, .bInterfaceNumber = 1, .bAlternateSetting = 0, .bNumEndpoints = 1, .bInterfaceClass = USB_DEVICE_CLASS_HID, .bInterfaceSubClass = HID_SUBCLASS_NONE, .bInterfaceProtocol = HID_PROTOCOL_NONE, }, .hid_p2 = { .bLength = sizeof(struct usb_hid_descriptor), .bDescriptorType = HID_DESCRIPTOR, .bcdHid = 0x0101, .bCountryCode = HID_COUNTRY_NOT_SUPPORTED, .bNumDescriptors = 1, // Only a report descriptor .bClassDescriptorType = REPORT_DESCRIPTOR, .wClassDescriptorLength = sizeof(gcn64_usbHidReportDescriptor), }, .ep2_in = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = ENDPOINT_DESCRIPTOR, .bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 2, // 0x82 .bmAttributes = TRANSFER_TYPE_INT, .wMaxPacketsize = 16, .bInterval = LS_FS_INTERVAL_MS(1), }, // Second HID interface for config and update .interface_admin = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = INTERFACE_DESCRIPTOR, .bInterfaceNumber = 2, .bAlternateSetting = 0, .bNumEndpoints = 1, .bInterfaceClass = USB_DEVICE_CLASS_HID, .bInterfaceSubClass = HID_SUBCLASS_NONE, .bInterfaceProtocol = HID_PROTOCOL_NONE, }, .hid_data = { .bLength = sizeof(struct usb_hid_descriptor), .bDescriptorType = HID_DESCRIPTOR, .bcdHid = 0x0101, .bCountryCode = HID_COUNTRY_NOT_SUPPORTED, .bNumDescriptors = 1, // Only a report descriptor .bClassDescriptorType = REPORT_DESCRIPTOR, .wClassDescriptorLength = sizeof(dataHidReport), }, .ep3_in = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = ENDPOINT_DESCRIPTOR, .bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 3, // 0x83 .bmAttributes = TRANSFER_TYPE_INT, .wMaxPacketsize = 64, .bInterval = LS_FS_INTERVAL_MS(1), }, }; static const struct cfg0_2p cfg0_2p_keyboard PROGMEM = { .configdesc = { .bLength = sizeof(struct usb_configuration_descriptor), .bDescriptorType = CONFIGURATION_DESCRIPTOR, .wTotalLength = sizeof(cfg0_2p), // includes all descriptors returned together .bNumInterfaces = 2 + 1, // one interface per player + one management interface .bConfigurationValue = 1, .bmAttributes = CFG_DESC_ATTR_RESERVED, // set Self-powred and remote-wakeup here if needed. .bMaxPower = 25, // for 50mA }, // Joystick interface .interface = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = INTERFACE_DESCRIPTOR, .bInterfaceNumber = 0, .bAlternateSetting = 0, .bNumEndpoints = 1, .bInterfaceClass = USB_DEVICE_CLASS_HID, .bInterfaceSubClass = HID_SUBCLASS_NONE, .bInterfaceProtocol = HID_PROTOCOL_NONE, }, .hid = { .bLength = sizeof(struct usb_hid_descriptor), .bDescriptorType = HID_DESCRIPTOR, .bcdHid = 0x0101, .bCountryCode = HID_COUNTRY_NOT_SUPPORTED, .bNumDescriptors = 1, // Only a report descriptor .bClassDescriptorType = REPORT_DESCRIPTOR, .wClassDescriptorLength = sizeof(gcn64_usbHidReportDescriptor), }, .ep1_in = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = ENDPOINT_DESCRIPTOR, .bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 1, // 0x81 .bmAttributes = TRANSFER_TYPE_INT, .wMaxPacketsize = 16, .bInterval = LS_FS_INTERVAL_MS(1), }, // HID Keyboard interface .interface_p2 = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = INTERFACE_DESCRIPTOR, .bInterfaceNumber = 1, .bAlternateSetting = 0, .bNumEndpoints = 1, .bInterfaceClass = USB_DEVICE_CLASS_HID, .bInterfaceSubClass = HID_SUBCLASS_NONE, .bInterfaceProtocol = HID_PROTOCOL_NONE, }, .hid_p2 = { .bLength = sizeof(struct usb_hid_descriptor), .bDescriptorType = HID_DESCRIPTOR, .bcdHid = 0x0101, .bCountryCode = HID_COUNTRY_NOT_SUPPORTED, .bNumDescriptors = 1, // Only a report descriptor .bClassDescriptorType = REPORT_DESCRIPTOR, .wClassDescriptorLength = sizeof(gcKeyboardReport), }, .ep2_in = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = ENDPOINT_DESCRIPTOR, .bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 2, // 0x82 .bmAttributes = TRANSFER_TYPE_INT, .wMaxPacketsize = 16, .bInterval = LS_FS_INTERVAL_MS(1), }, // Second HID interface for config and update .interface_admin = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = INTERFACE_DESCRIPTOR, .bInterfaceNumber = 2, .bAlternateSetting = 0, .bNumEndpoints = 1, .bInterfaceClass = USB_DEVICE_CLASS_HID, .bInterfaceSubClass = HID_SUBCLASS_NONE, .bInterfaceProtocol = HID_PROTOCOL_NONE, }, .hid_data = { .bLength = sizeof(struct usb_hid_descriptor), .bDescriptorType = HID_DESCRIPTOR, .bcdHid = 0x0101, .bCountryCode = HID_COUNTRY_NOT_SUPPORTED, .bNumDescriptors = 1, // Only a report descriptor .bClassDescriptorType = REPORT_DESCRIPTOR, .wClassDescriptorLength = sizeof(dataHidReport), }, .ep3_in = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = ENDPOINT_DESCRIPTOR, .bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 3, // 0x83 .bmAttributes = TRANSFER_TYPE_INT, .wMaxPacketsize = 64, .bInterval = LS_FS_INTERVAL_MS(1), }, }; struct usb_device_descriptor device_descriptor = { .bLength = sizeof(struct usb_device_descriptor), .bDescriptorType = DEVICE_DESCRIPTOR, .bcdUSB = 0x0110, .bDeviceClass = 0, // set at interface .bDeviceSubClass = 0, // set at interface .bDeviceProtocol = 0, .bMaxPacketSize = 64, .idVendor = 0x289B, .idProduct = GCN64_USB_PID, .bcdDevice = VERSIONBCD, .bNumConfigurations = 1, .iManufacturer = 1, .iProduct = 2, .iSerialNumber = 3, }; /** **/ static uint16_t _usbpad_hid_get_report(void *ctx, struct usb_request *rq, const uint8_t **dat) { return usbpad_hid_get_report((struct usbpad*)ctx, rq, dat); } static uint8_t _usbpad_hid_set_report(void *ctx, const struct usb_request *rq, const uint8_t *dat, uint16_t len) { return usbpad_hid_set_report((struct usbpad*)ctx, rq, dat, len); } static struct usb_parameters usb_params = { .flags = USB_PARAM_FLAG_CONFDESC_PROGMEM | USB_PARAM_FLAG_REPORTDESC_PROGMEM, .devdesc = (PGM_VOID_P)&device_descriptor, .configdesc = (PGM_VOID_P)&cfg0, // Patched in main() for two players .configdesc_ttllen = sizeof(cfg0), // Patched in main() for two players .num_strings = NUM_USB_STRINGS, .strings = g_usb_strings, .n_hid_interfaces = 1 + 1, // One per player + one management interface (patched in main() for two players) .hid_params = { [0] = { .reportdesc = gcn64_usbHidReportDescriptor, .reportdesc_len = sizeof(gcn64_usbHidReportDescriptor), .getReport = _usbpad_hid_get_report, .setReport = _usbpad_hid_set_report, .endpoint_size = 16, }, [1] = { .reportdesc = dataHidReport, .reportdesc_len = sizeof(dataHidReport), .getReport = hiddata_get_report, .setReport = hiddata_set_report, .endpoint_size = 64, }, }, }; void hwinit(void) { /* PORTB * * 7: NC Output low * 6: NC Output low * 5: NC Output low * 4: NC Output low * 3: MISO Output low * 2: MOSI Output low * 1: SCK Output low * 0: NC Output low */ PORTB = 0x00; DDRB = 0xFF; /* PORTC * * 7: NC Output low * 6: NC Output low * 5: NC Output low * 4: NC Output low * 3: (no such pin) * 2: NC Output low * 1: RESET (N/A: Reset input per fuses) (left floating) * 0: XTAL2 (N/A: Crystal oscillator) (left floating) */ DDRC = 0xfc; PORTC = 0x00; /* PORTD * * 7: HWB Input (external pull-up) * 6: NC Output low * 5: NC Output low * 4: NC Output low * 3: IO3_MCU Input * 2: IO2_MCU Input * 1: IO1_MCU Input * 0: IO0_MCU Input */ PORTD = 0x00; DDRD = 0x70; // System clock. External crystal is 16 Mhz and we want // to run at max. speed. CLKPR = 0x80; CLKPR = 0x0; // Division factor of 1 PRR0 = 0; PRR1 = 0; } uint8_t num_players = 1; unsigned char current_pad_type[NUM_CHANNELS] = { }; Gamepad *detectPad(unsigned char chn) { current_pad_type[chn] = gcn64_detectController(chn); switch (current_pad_type[chn]) { case CONTROLLER_IS_ABSENT: case CONTROLLER_IS_UNKNOWN: return NULL; case CONTROLLER_IS_N64_MOUSE: case CONTROLLER_IS_N64: return n64GetGamepad(); case CONTROLLER_IS_GC: return gamecubeGetGamepad(); case CONTROLLER_IS_GC_KEYBOARD: return gamecubeGetKeyboard(); } return NULL; } /* Called after eeprom content is loaded. */ void eeprom_app_ready(void) { static wchar_t serial_from_eeprom[SERIAL_NUM_LEN+1]; int i; for (i=0; ihotplug)) { // For gamecube, this make sure the next // analog values we read become the center // reference. pads[channel]->hotplug(channel); } } /* Read from the pad by calling update */ if (pads[channel]) { if (pads[channel]->update(channel)) { error_count[channel]++; if (error_count[channel] > MAX_READ_ERRORS) { pads[channel] = NULL; error_count[channel] = 0; continue; } } else { error_count[channel]=0; } if (pads[channel]->changed(channel)) { pads[channel]->getReport(channel, &pad_data); usbpad_update(&usbpads[channel], &pad_data); state = STATE_WAIT_INTERRUPT_READY; continue; } } else { /* Just make sure the gamepad state holds valid data * to appear inactive (no buttons and axes in neutral) */ usbpad_update(&usbpads[channel], NULL); } } /* If there were change on any of the gamepads, state will * be set to STATE_WAIT_INTERRUPT_READY. Otherwise, go back * to WAIT_POLLTIME. */ if (state == STATE_POLL_PAD) { state = STATE_WAIT_POLLTIME; } break; case STATE_WAIT_INTERRUPT_READY: /* Wait until one of the interrupt endpoint is ready */ if (usb_interruptReady_ep1() || (num_players>1 && usb_interruptReady_ep2())) { state = STATE_TRANSMIT; } break; case STATE_TRANSMIT: if (usb_interruptReady_ep1()) { usb_interruptSend_ep1(usbpad_getReportBuffer(&usbpads[0]), usbpad_getReportSize()); } if (num_players>1 && usb_interruptReady_ep2()) { usb_interruptSend_ep2(usbpad_getReportBuffer(&usbpads[1]), usbpad_getReportSize()); } state = STATE_WAIT_POLLTIME; break; } for (channel=0; channel < num_players; channel++) { gamepad_vibrate = usbpad_mustVibrate(&usbpads[channel]); if (last_v[channel] != gamepad_vibrate) { if (pads[channel] && pads[channel]->setVibration) { pads[channel]->setVibration(channel, gamepad_vibrate); } last_v[channel] = gamepad_vibrate; } } } return 0; } int keyboard_main(void) { Gamepad *pads[MAX_PLAYERS] = { }; gamepad_data pad_data; uint8_t gamepad_vibrate = 0; uint8_t state = STATE_WAIT_POLLTIME; uint8_t channel; uint8_t i; hwinit(); usart1_init(); eeprom_init(); intervaltimer_init(); intervaltimer2_init(); stkchk_init(); switch (g_eeprom_data.cfg.mode) { default: case CFG_MODE_KEYBOARD_2: usbstrings_changeProductString_P(PSTR("KB to USB v"VERSIONSTR_SHORT)); device_descriptor.idProduct = KEYBOARD_PID2; usb_params.configdesc = (PGM_VOID_P)&cfg0_kb; usb_params.configdesc_ttllen = sizeof(cfg0_kb); // replace Joystick report descriptor by keyboard usb_params.hid_params[0].reportdesc = gcKeyboardReport; usb_params.hid_params[0].reportdesc_len = sizeof(gcKeyboardReport); break; case CFG_MODE_KEYBOARD: usbstrings_changeProductString_P(PSTR("GC KB to USB v"VERSIONSTR_SHORT)); device_descriptor.idProduct = KEYBOARD_PID; usb_params.configdesc = (PGM_VOID_P)&cfg0_kb; usb_params.configdesc_ttllen = sizeof(cfg0_kb); // replace Joystick report descriptor by keyboard usb_params.hid_params[0].reportdesc = gcKeyboardReport; usb_params.hid_params[0].reportdesc_len = sizeof(gcKeyboardReport); break; case CFG_MODE_KB_AND_JS: usbstrings_changeProductString_P(PSTR("GC KB+JS to USB v"VERSIONSTR_SHORT)); device_descriptor.idProduct = KEYBOARD_JS_PID; usb_params.configdesc = (PGM_VOID_P)&cfg0_2p_keyboard; usb_params.configdesc_ttllen = sizeof(cfg0_2p_keyboard); // Move the management interface to the last position memcpy(usb_params.hid_params + 2, usb_params.hid_params + 1, sizeof(struct usb_hid_parameters)); // Add a second player interface between them (still a joystick) memcpy(usb_params.hid_params + 1, usb_params.hid_params + 0, sizeof(struct usb_hid_parameters)); // Convert second Joystick report descriptor to a keyboard usb_params.hid_params[1].reportdesc = gcKeyboardReport; usb_params.hid_params[1].reportdesc_len = sizeof(gcKeyboardReport); usb_params.n_hid_interfaces = 3; num_players = 2; break; } for (i=0; ihotplug)) { // For gamecube, this make sure the next // analog values we read become the center // reference. pads[channel]->hotplug(channel); } } /* Read from the pad by calling update */ if (pads[channel]) { if (pads[channel]->update(channel)) { error_count[channel]++; if (error_count[channel] > MAX_READ_ERRORS) { pads[channel] = NULL; error_count[channel] = 0; continue; } } else { error_count[channel]=0; } if (pads[channel]->changed(channel)) { pads[channel]->getReport(channel, &pad_data); if ((num_players == 1) && (channel == 0)) { // single-port adapter in keyboard mode (kb in port 1) usbpad_update_kb(&usbpads[channel], &pad_data); } else if ((num_players == 2) && (channel == 1)) { // dual-port adapter in keyboard mode (kb in port 2) usbpad_update_kb(&usbpads[channel], &pad_data); } else { usbpad_update(&usbpads[channel], &pad_data); } state = STATE_WAIT_INTERRUPT_READY; continue; } } else { /* Just make sure the gamepad state holds valid data * to appear inactive (no buttons and axes in neutral) */ usbpad_update(&usbpads[channel], NULL); } } /* If there were change on any of the gamepads, state will * be set to STATE_WAIT_INTERRUPT_READY. Otherwise, go back * to WAIT_POLLTIME. */ if (state == STATE_POLL_PAD) { state = STATE_WAIT_POLLTIME; } break; case STATE_WAIT_INTERRUPT_READY: /* Wait until one of the interrupt endpoint is ready */ if (usb_interruptReady_ep1() || (num_players>1 && usb_interruptReady_ep2())) { state = STATE_TRANSMIT; } break; case STATE_TRANSMIT: if (usb_interruptReady_ep1()) { if (num_players == 1) { // Single-port adapters have the keyboard in port 1 usb_interruptSend_ep1(usbpad_getReportBuffer(&usbpads[0]), usbpad_getReportSizeKB()); } else { usb_interruptSend_ep1(usbpad_getReportBuffer(&usbpads[0]), usbpad_getReportSize()); } } // Keyboard is always in second port on dual port adapters if (num_players>1 && usb_interruptReady_ep2()) { usb_interruptSend_ep2(usbpad_getReportBuffer(&usbpads[1]), usbpad_getReportSizeKB()); } state = STATE_WAIT_POLLTIME; break; } for (channel=0; channel < num_players; channel++) { gamepad_vibrate = usbpad_mustVibrate(&usbpads[channel]); if (last_v[channel] != gamepad_vibrate) { if (pads[channel] && pads[channel]->setVibration) { pads[channel]->setVibration(channel, gamepad_vibrate); } last_v[channel] = gamepad_vibrate; } } } return 0; }