|
|
|
@ -38,12 +38,16 @@
@@ -38,12 +38,16 @@
|
|
|
|
|
#include "intervaltimer.h" |
|
|
|
|
#include "requests.h" |
|
|
|
|
|
|
|
|
|
#define NUM_PLAYERS 2 |
|
|
|
|
#define MAX_PLAYERS 2 |
|
|
|
|
|
|
|
|
|
#define GCN64_USB_PID 0x001D |
|
|
|
|
#define N64_USB_PID 0x0020 |
|
|
|
|
#define GC_USB_PID 0x0021 |
|
|
|
|
|
|
|
|
|
#define DUAL_GCN64_USB_PID 0x0022 |
|
|
|
|
#define DUAL_N64_USB_PID 0x0030 |
|
|
|
|
#define DUAL_GC_USB_PID 0x0031 |
|
|
|
|
|
|
|
|
|
/* Those .c files are included rather than linked for we
|
|
|
|
|
* want the sizeof() operator to work on the arrays */ |
|
|
|
|
#include "reportdesc.c" |
|
|
|
@ -60,30 +64,99 @@ struct cfg0 {
@@ -60,30 +64,99 @@ struct cfg0 {
|
|
|
|
|
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), |
|
|
|
|
}, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
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_admin; |
|
|
|
|
struct usb_hid_descriptor hid_data; |
|
|
|
|
struct usb_endpoint_descriptor ep2_in; |
|
|
|
|
|
|
|
|
|
#if NUM_PLAYERS >= 2 |
|
|
|
|
struct usb_interface_descriptor interface_p2; |
|
|
|
|
struct usb_hid_descriptor hid_p2; |
|
|
|
|
struct usb_endpoint_descriptor ep3_in; |
|
|
|
|
#endif |
|
|
|
|
#if NUM_PLAYERS >= 3 |
|
|
|
|
struct usb_interface_descriptor interface_p3; |
|
|
|
|
struct usb_hid_descriptor hid_p3; |
|
|
|
|
struct usb_endpoint_descriptor ep4_in; |
|
|
|
|
#endif |
|
|
|
|
#if NUM_PLAYERS >= 4 |
|
|
|
|
struct usb_interface_descriptor interface_p4; |
|
|
|
|
struct usb_hid_descriptor hid_p4; |
|
|
|
|
struct usb_endpoint_descriptor ep5_in; |
|
|
|
|
#endif |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static const struct cfg0 cfg0 PROGMEM = { |
|
|
|
|
static const struct cfg0_2p cfg0_2p PROGMEM = { |
|
|
|
|
.configdesc = { |
|
|
|
|
.bLength = sizeof(struct usb_configuration_descriptor), |
|
|
|
|
.bDescriptorType = CONFIGURATION_DESCRIPTOR, |
|
|
|
|
.wTotalLength = sizeof(cfg0), // includes all descriptors returned together
|
|
|
|
|
.bNumInterfaces = NUM_PLAYERS + 1, // one interface per player + one management interface
|
|
|
|
|
.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
|
|
|
|
@ -147,7 +220,6 @@ static const struct cfg0 cfg0 PROGMEM = {
@@ -147,7 +220,6 @@ static const struct cfg0 cfg0 PROGMEM = {
|
|
|
|
|
.bInterval = LS_FS_INTERVAL_MS(1), |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
#if NUM_PLAYERS >= 2 |
|
|
|
|
// Main interface, HID (player 2)
|
|
|
|
|
.interface_p2 = { |
|
|
|
|
.bLength = sizeof(struct usb_interface_descriptor), |
|
|
|
@ -176,8 +248,6 @@ static const struct cfg0 cfg0 PROGMEM = {
@@ -176,8 +248,6 @@ static const struct cfg0 cfg0 PROGMEM = {
|
|
|
|
|
.wMaxPacketsize = 16, |
|
|
|
|
.bInterval = LS_FS_INTERVAL_MS(1), |
|
|
|
|
}, |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
struct usb_device_descriptor device_descriptor = { |
|
|
|
@ -213,12 +283,12 @@ static struct usb_parameters usb_params = {
@@ -213,12 +283,12 @@ 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, |
|
|
|
|
.configdesc_ttllen = sizeof(cfg0), |
|
|
|
|
.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 + NUM_PLAYERS, |
|
|
|
|
.n_hid_interfaces = 1 + 1, // One per player + one management interface (patched in main() for two players)
|
|
|
|
|
.hid_params = { |
|
|
|
|
[0] = { |
|
|
|
|
.reportdesc = gcn64_usbHidReportDescriptor, |
|
|
|
@ -232,14 +302,12 @@ static struct usb_parameters usb_params = {
@@ -232,14 +302,12 @@ static struct usb_parameters usb_params = {
|
|
|
|
|
.getReport = hiddata_get_report, |
|
|
|
|
.setReport = hiddata_set_report, |
|
|
|
|
}, |
|
|
|
|
#if NUM_PLAYERS >= 2 |
|
|
|
|
[2] = { |
|
|
|
|
.reportdesc = gcn64_usbHidReportDescriptor, |
|
|
|
|
.reportdesc_len = sizeof(gcn64_usbHidReportDescriptor), |
|
|
|
|
.getReport = _usbpad_hid_get_report, |
|
|
|
|
.setReport = _usbpad_hid_set_report, |
|
|
|
|
}, |
|
|
|
|
#endif |
|
|
|
|
}, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
@ -343,7 +411,7 @@ void pollDelay(void)
@@ -343,7 +411,7 @@ void pollDelay(void)
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static struct usbpad usbpads[NUM_PLAYERS]; |
|
|
|
|
static struct usbpad usbpads[MAX_PLAYERS]; |
|
|
|
|
static char g_polling_suspended = 0; |
|
|
|
|
|
|
|
|
|
static void setSuspendPolling(uint8_t suspend) |
|
|
|
@ -353,7 +421,7 @@ static void setSuspendPolling(uint8_t suspend)
@@ -353,7 +421,7 @@ static void setSuspendPolling(uint8_t suspend)
|
|
|
|
|
|
|
|
|
|
static void forceVibration(uint8_t channel, uint8_t force) |
|
|
|
|
{ |
|
|
|
|
if (channel < NUM_PLAYERS) { |
|
|
|
|
if (channel < MAX_PLAYERS) { |
|
|
|
|
usbpad_forceVibrate(&usbpads[channel], force); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -372,20 +440,21 @@ static struct hiddata_ops hiddata_ops = {
@@ -372,20 +440,21 @@ static struct hiddata_ops hiddata_ops = {
|
|
|
|
|
|
|
|
|
|
int main(void) |
|
|
|
|
{ |
|
|
|
|
Gamepad *pads[NUM_PLAYERS] = { }; |
|
|
|
|
Gamepad *pads[MAX_PLAYERS] = { }; |
|
|
|
|
gamepad_data pad_data; |
|
|
|
|
unsigned char gamepad_vibrate = 0; |
|
|
|
|
unsigned char state = STATE_WAIT_POLLTIME; |
|
|
|
|
int error_count[NUM_PLAYERS] = { }; |
|
|
|
|
int error_count[MAX_PLAYERS] = { }; |
|
|
|
|
int i; |
|
|
|
|
int channel; |
|
|
|
|
int num_players = 1; |
|
|
|
|
|
|
|
|
|
hwinit(); |
|
|
|
|
usart1_init(); |
|
|
|
|
eeprom_init(); |
|
|
|
|
intervaltimer_init(); |
|
|
|
|
|
|
|
|
|
for (i=0; i<NUM_PLAYERS; i++) { |
|
|
|
|
for (i=0; i<num_players; i++) { |
|
|
|
|
usbpad_init(&usbpads[i]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -404,6 +473,33 @@ int main(void)
@@ -404,6 +473,33 @@ int main(void)
|
|
|
|
|
usbstrings_changeProductString(L"Gamecube to USB v"VERSIONSTR_SHORT); |
|
|
|
|
device_descriptor.idProduct = GC_USB_PID; |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case CFG_MODE_2P_STANDARD: |
|
|
|
|
usbstrings_changeProductString(L"Dual GC/N64 to USB v"VERSIONSTR_SHORT); |
|
|
|
|
device_descriptor.idProduct = DUAL_GCN64_USB_PID; |
|
|
|
|
usb_params.configdesc = (PGM_VOID_P)&cfg0_2p; |
|
|
|
|
usb_params.configdesc_ttllen = sizeof(cfg0_2p); |
|
|
|
|
usb_params.n_hid_interfaces = 3; |
|
|
|
|
num_players = 2; |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case CFG_MODE_2P_N64_ONLY: |
|
|
|
|
usbstrings_changeProductString(L"Dual N64 to USB v"VERSIONSTR_SHORT); |
|
|
|
|
device_descriptor.idProduct = DUAL_N64_USB_PID; |
|
|
|
|
usb_params.configdesc = (PGM_VOID_P)&cfg0_2p; |
|
|
|
|
usb_params.configdesc_ttllen = sizeof(cfg0_2p); |
|
|
|
|
usb_params.n_hid_interfaces = 3; |
|
|
|
|
num_players = 2; |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case CFG_MODE_2P_GC_ONLY: |
|
|
|
|
usbstrings_changeProductString(L"Dual Gamecube to USB v"VERSIONSTR_SHORT); |
|
|
|
|
device_descriptor.idProduct = DUAL_GC_USB_PID; |
|
|
|
|
usb_params.configdesc = (PGM_VOID_P)&cfg0_2p; |
|
|
|
|
usb_params.configdesc_ttllen = sizeof(cfg0_2p); |
|
|
|
|
usb_params.n_hid_interfaces = 3; |
|
|
|
|
num_players = 2; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sei(); |
|
|
|
@ -411,7 +507,7 @@ int main(void)
@@ -411,7 +507,7 @@ int main(void)
|
|
|
|
|
|
|
|
|
|
while (1) |
|
|
|
|
{ |
|
|
|
|
static char last_v[NUM_PLAYERS] = { }; |
|
|
|
|
static char last_v[MAX_PLAYERS] = { }; |
|
|
|
|
|
|
|
|
|
usb_doTasks(); |
|
|
|
|
hiddata_doTask(&hiddata_ops); |
|
|
|
@ -428,7 +524,7 @@ int main(void)
@@ -428,7 +524,7 @@ int main(void)
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case STATE_POLL_PAD: |
|
|
|
|
for (channel=0; channel<NUM_PLAYERS; channel++) |
|
|
|
|
for (channel=0; channel<num_players; channel++) |
|
|
|
|
{ |
|
|
|
|
/* Try to auto-detect controller if none*/ |
|
|
|
|
if (!pads[channel]) { |
|
|
|
@ -483,7 +579,7 @@ int main(void)
@@ -483,7 +579,7 @@ int main(void)
|
|
|
|
|
|
|
|
|
|
case STATE_TRANSMIT: |
|
|
|
|
usb_interruptSend_ep1(usbpad_getReportBuffer(&usbpads[0]), usbpad_getReportSize()); |
|
|
|
|
if (NUM_PLAYERS > 1) { |
|
|
|
|
if (num_players > 1) { |
|
|
|
|
state = STATE_WAIT_INTERRUPT_READY_P2; |
|
|
|
|
} else { |
|
|
|
|
state = STATE_WAIT_POLLTIME; |
|
|
|
@ -503,7 +599,7 @@ int main(void)
@@ -503,7 +599,7 @@ int main(void)
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (channel=0; channel < NUM_PLAYERS; channel++) { |
|
|
|
|
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) { |
|
|
|
|