diff --git a/gamecube.c b/gamecube.c index 8625205..8a1a09a 100644 --- a/gamecube.c +++ b/gamecube.c @@ -23,20 +23,23 @@ #include "gcn64_protocol.h" /*********** prototypes *************/ -static void gamecubeInit(void); -static char gamecubeUpdate(void); -static char gamecubeChanged(void); +static void gamecubeInit(unsigned char chn); +static char gamecubeUpdate(unsigned char chn); +static char gamecubeChanged(unsigned char chn); -static char gc_rumbling = 0; -static char origins_set = 0; -static unsigned char orig_x, orig_y, orig_cx, orig_cy; +static char gc_rumbling[GAMEPAD_MAX_CHANNELS] = { }; +static char origins_set[GAMEPAD_MAX_CHANNELS] = { }; +static unsigned char orig_x[GAMEPAD_MAX_CHANNELS]; +static unsigned char orig_y[GAMEPAD_MAX_CHANNELS]; +static unsigned char orig_cx[GAMEPAD_MAX_CHANNELS]; +static unsigned char orig_cy[GAMEPAD_MAX_CHANNELS]; -static void gamecubeInit(void) +static void gamecubeInit(unsigned char chn) { - gamecubeUpdate(); + gamecubeUpdate(chn); } -void gc_decodeAnswer(unsigned char data[8]) +void gc_decodeAnswer(unsigned char chn, unsigned char data[8]) { unsigned char x,y,cx,cy; @@ -91,74 +94,74 @@ void gc_decodeAnswer(unsigned char data[8]) last_built_report.gc.rt = data[7]; memcpy(last_built_report.gc.raw_data, data, 8); - if (origins_set) { - last_built_report.gc.x = ((int)x-(int)orig_x); - last_built_report.gc.y = ((int)y-(int)orig_y); - last_built_report.gc.cx = ((int)cx-(int)orig_cx); - last_built_report.gc.cy = ((int)cy-(int)orig_cy); + if (origins_set[chn]) { + last_built_report.gc.x = ((int)x-(int)orig_x[chn]); + last_built_report.gc.y = ((int)y-(int)orig_y[chn]); + last_built_report.gc.cx = ((int)cx-(int)orig_cx[chn]); + last_built_report.gc.cy = ((int)cy-(int)orig_cy[chn]); } else { - orig_x = x; - orig_y = y; - orig_cx = cx; - orig_cy = cy; + orig_x[chn] = x; + orig_y[chn] = y; + orig_cx[chn] = cx; + orig_cy[chn] = cy; last_built_report.gc.x = 0; last_built_report.gc.y = 0; last_built_report.gc.cx = 0; last_built_report.gc.cy = 0; - origins_set = 1; + origins_set[chn] = 1; } } -static char gamecubeUpdate() +static char gamecubeUpdate(unsigned char chn) { unsigned char tmpdata[GC_GETSTATUS_REPLY_LENGTH]; unsigned char count; tmpdata[0] = GC_GETSTATUS1; tmpdata[1] = GC_GETSTATUS2; - tmpdata[2] = GC_GETSTATUS3(gc_rumbling); + tmpdata[2] = GC_GETSTATUS3(gc_rumbling[chn]); count = gcn64_transaction(GCN64_CHANNEL_0, tmpdata, 3, tmpdata, GC_GETSTATUS_REPLY_LENGTH); if (count != GC_GETSTATUS_REPLY_LENGTH) { return 1; } - gc_decodeAnswer(tmpdata); + gc_decodeAnswer(chn, tmpdata); return 0; } -static void gamecubeHotplug(void) +static void gamecubeHotplug(unsigned char chn) { // Make sure next read becomes the refence center values - origins_set = 0; + origins_set[chn] = 0; } -static char gamecubeProbe(void) +static char gamecubeProbe(unsigned char chn) { - origins_set = 0; + origins_set[chn] = 0; - if (gamecubeUpdate()) { + if (gamecubeUpdate(chn)) { return 0; } return 1; } -static char gamecubeChanged(void) +static char gamecubeChanged(unsigned char chn) { return memcmp(&last_built_report, &last_sent_report, sizeof(gamepad_data)); } -static void gamecubeGetReport(gamepad_data *dst) +static void gamecubeGetReport(unsigned char chn, gamepad_data *dst) { if (dst) memcpy(dst, &last_built_report, sizeof(gamepad_data)); } -static void gamecubeVibration(char enable) +static void gamecubeVibration(unsigned char chn, char enable) { - gc_rumbling = enable; + gc_rumbling[chn] = enable; } Gamepad GamecubeGamepad = { diff --git a/gamecube.h b/gamecube.h index d699288..3949ab7 100644 --- a/gamecube.h +++ b/gamecube.h @@ -1,7 +1,4 @@ #include "gamepads.h" -#define GAMECUBE_UPDATE_NORMAL 0 -#define GAMECUBE_UPDATE_ORIGIN 1 - Gamepad *gamecubeGetGamepad(void); diff --git a/gamepads.c b/gamepads.c index b25f5b5..8144055 100644 --- a/gamepads.c +++ b/gamepads.c @@ -1,5 +1,5 @@ /* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware - Copyright (C) 2007-2015 Raphael Assenat + 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 @@ -16,7 +16,8 @@ */ #include "gamepads.h" -/* Shared between N64 and GC (only one is used at a time). Saves memory. */ +/* Shared between N64 and GC (only one is used at a time). Saves memory. */ gamepad_data last_sent_report; gamepad_data last_built_report; + diff --git a/gamepads.h b/gamepads.h index 7a60726..014fd27 100644 --- a/gamepads.h +++ b/gamepads.h @@ -71,14 +71,15 @@ typedef struct _gamepad_data { }; } gamepad_data; +#define GAMEPAD_MAX_CHANNELS 2 typedef struct { - void (*init)(void); - char (*update)(void); - char (*changed)(void); - void (*hotplug)(void); - void (*getReport)(gamepad_data *dst); - void (*setVibration)(char enable); - char (*probe)(void); /* return true if found */ + void (*init)(unsigned char chn); + char (*update)(unsigned char chn); + char (*changed)(unsigned char chn); + void (*hotplug)(unsigned char chn); + void (*getReport)(unsigned char chn, gamepad_data *dst); + void (*setVibration)(unsigned char chn, char enable); + char (*probe)(unsigned char chn); /* return true if found */ } Gamepad; /* What was most recently read from the controller */ diff --git a/hiddata.c b/hiddata.c index 5f53127..9306d40 100644 --- a/hiddata.c +++ b/hiddata.c @@ -41,7 +41,7 @@ static unsigned char cmdbuf[CMDBUF_SIZE]; static volatile unsigned char cmdbuf_len = 0; /*** Get/Set report called from interrupt context! */ -uint16_t hiddata_get_report(struct usb_request *rq, const uint8_t **dat) +uint16_t hiddata_get_report(void *ctx, struct usb_request *rq, const uint8_t **dat) { // printf("Get data\n"); if (state == STATE_COMMAND_DONE) { @@ -56,7 +56,7 @@ uint16_t hiddata_get_report(struct usb_request *rq, const uint8_t **dat) } /*** Get/Set report called from interrupt context! */ -uint8_t hiddata_set_report(const struct usb_request *rq, const uint8_t *dat, uint16_t len) +uint8_t hiddata_set_report(void *ctx, const struct usb_request *rq, const uint8_t *dat, uint16_t len) { #ifdef DEBUG int i; @@ -74,7 +74,7 @@ uint8_t hiddata_set_report(const struct usb_request *rq, const uint8_t *dat, uin return 0; } -static void hiddata_processCommandBuffer(void) +static void hiddata_processCommandBuffer(struct hiddata_ops *ops) { unsigned char channel; #ifdef DEBUG @@ -114,7 +114,9 @@ static void hiddata_processCommandBuffer(void) break; case RQ_GCN64_SUSPEND_POLLING: // CMD: RQ, PARAM - g_polling_suspended = cmdbuf[1]; + if (ops && ops->suspendPolling) { + ops->suspendPolling(cmdbuf[1]); + } break; case RQ_GCN64_GET_VERSION: // CMD: RQ @@ -135,7 +137,9 @@ static void hiddata_processCommandBuffer(void) case RQ_GCN64_SET_VIBRATION: // CMD : RQ, CHN, Vibrate // Answer: RQ, CHN, Vibrate - usbpad_forceVibrate(cmdbuf[2]); + if (ops && ops->forceVibration) { + ops->forceVibration(cmdbuf[1], cmdbuf[2]); + } cmdbuf_len = 3; break; } @@ -151,7 +155,7 @@ static void hiddata_processCommandBuffer(void) state = STATE_COMMAND_DONE; } -void hiddata_doTask(void) +void hiddata_doTask(struct hiddata_ops *ops) { switch (state) { @@ -161,7 +165,7 @@ void hiddata_doTask(void) break; case STATE_NEW_COMMAND: - hiddata_processCommandBuffer(); + hiddata_processCommandBuffer(ops); break; case STATE_COMMAND_DONE: diff --git a/hiddata.h b/hiddata.h index 3160032..316f359 100644 --- a/hiddata.h +++ b/hiddata.h @@ -4,9 +4,14 @@ #include #include "usb.h" -uint16_t hiddata_get_report(struct usb_request *rq, const uint8_t **dat); -uint8_t hiddata_set_report(const struct usb_request *rq, const uint8_t *dat, uint16_t len); +struct hiddata_ops { + void (*suspendPolling)(uint8_t suspend); + void (*forceVibration)(uint8_t channel, uint8_t force); +}; -void hiddata_doTask(void); +uint16_t hiddata_get_report(void *ctx, struct usb_request *rq, const uint8_t **dat); +uint8_t hiddata_set_report(void *ctx, const struct usb_request *rq, const uint8_t *dat, uint16_t len); + +void hiddata_doTask(struct hiddata_ops *ops); #endif diff --git a/main.c b/main.c index 2bbda14..8875beb 100644 --- a/main.c +++ b/main.c @@ -38,6 +38,8 @@ #include "intervaltimer.h" #include "requests.h" +#define NUM_PLAYERS 2 + #define GCN64_USB_PID 0x001D #define N64_USB_PID 0x0020 #define GC_USB_PID 0x0021 @@ -58,6 +60,22 @@ struct cfg0 { 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 = { @@ -65,13 +83,13 @@ static const struct cfg0 cfg0 PROGMEM = { .bLength = sizeof(struct usb_configuration_descriptor), .bDescriptorType = CONFIGURATION_DESCRIPTOR, .wTotalLength = sizeof(cfg0), // includes all descriptors returned together - .bNumInterfaces = 2, + .bNumInterfaces = NUM_PLAYERS + 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 + // Main interface, HID (player 1) .interface = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = INTERFACE_DESCRIPTOR, @@ -96,7 +114,7 @@ static const struct cfg0 cfg0 PROGMEM = { .bDescriptorType = ENDPOINT_DESCRIPTOR, .bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 1, // 0x81 .bmAttributes = TRANSFER_TYPE_INT, - .wMaxPacketsize = 32, + .wMaxPacketsize = 16, .bInterval = LS_FS_INTERVAL_MS(1), }, @@ -129,6 +147,37 @@ 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), + .bDescriptorType = INTERFACE_DESCRIPTOR, + .bInterfaceNumber = 2, + .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), + }, + .ep3_in = { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = ENDPOINT_DESCRIPTOR, + .bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 3, // 0x83 + .bmAttributes = TRANSFER_TYPE_INT, + .wMaxPacketsize = 16, + .bInterval = LS_FS_INTERVAL_MS(1), + }, +#endif + }; struct usb_device_descriptor device_descriptor = { @@ -150,6 +199,16 @@ struct usb_device_descriptor device_descriptor = { /** **/ +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, @@ -159,13 +218,13 @@ static struct usb_parameters usb_params = { .num_strings = NUM_USB_STRINGS, .strings = g_usb_strings, - .n_hid_interfaces = 2, + .n_hid_interfaces = 1 + NUM_PLAYERS, .hid_params = { [0] = { .reportdesc = gcn64_usbHidReportDescriptor, .reportdesc_len = sizeof(gcn64_usbHidReportDescriptor), - .getReport = usbpad_hid_get_report, - .setReport = usbpad_hid_set_report, + .getReport = _usbpad_hid_get_report, + .setReport = _usbpad_hid_set_report, }, [1] = { .reportdesc = dataHidReport, @@ -173,6 +232,14 @@ 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 }, }; @@ -229,13 +296,11 @@ void hwinit(void) } -#define NUM_PAD_TYPES 2 - unsigned char current_pad_type = CONTROLLER_IS_ABSENT; -Gamepad *detectPad(void) +Gamepad *detectPad(unsigned char chn) { - current_pad_type = gcn64_detectController(GCN64_CHANNEL_0); + current_pad_type = gcn64_detectController(chn); switch (current_pad_type) { @@ -269,7 +334,6 @@ void eeprom_app_ready(void) g_usb_strings[USB_STRING_SERIAL_IDX] = serial_from_eeprom; } -char g_polling_suspended = 0; void pollDelay(void) { @@ -279,25 +343,51 @@ void pollDelay(void) } } +static struct usbpad usbpads[NUM_PLAYERS]; +static char g_polling_suspended = 0; + +static void setSuspendPolling(uint8_t suspend) +{ + g_polling_suspended = suspend; +} + +static void forceVibration(uint8_t channel, uint8_t force) +{ + if (channel < NUM_PLAYERS) { + usbpad_forceVibrate(&usbpads[channel], force); + } +} + +static struct hiddata_ops hiddata_ops = { + .suspendPolling = setSuspendPolling, + .forceVibration = forceVibration, +}; + #define STATE_WAIT_POLLTIME 0 #define STATE_POLL_PAD 1 #define STATE_WAIT_INTERRUPT_READY 2 #define STATE_TRANSMIT 3 +#define STATE_WAIT_INTERRUPT_READY_P2 4 +#define STATE_TRANSMIT_P2 5 int main(void) { - Gamepad *pad = NULL; + Gamepad *pads[NUM_PLAYERS] = { }; gamepad_data pad_data; unsigned char gamepad_vibrate = 0; unsigned char state = STATE_WAIT_POLLTIME; - int error_count=0; + int error_count[NUM_PLAYERS] = { }; + int i; + int channel; hwinit(); usart1_init(); eeprom_init(); intervaltimer_init(); - usbpad_init(); + for (i=0; ihotplug)) { - // For gamecube, this make sure the next - // analog values we read become the center - // reference. - pad->hotplug(); + for (channel=0; channelhotplug)) { + // For gamecube, this make sure the next + // analog values we read become the center + // reference. + pads[channel]->hotplug(channel); + } } - } - if (pad) { - if (pad->update()) { - error_count++; - if (error_count > MAX_READ_ERRORS) { - pad = NULL; - state = STATE_WAIT_POLLTIME; - error_count = 0; + + /* 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; break; } } else { - error_count=0; + /* Just make sure the gamepad state holds valid data + * to appear inactive (no buttons and axes in neutral) */ + usbpad_update(&usbpads[channel], NULL); } - - if (pad->changed()) { - - pad->getReport(&pad_data); - usbpad_update(&pad_data); - state = STATE_WAIT_INTERRUPT_READY; - break; - } - } else { - /* Just make sure the gamepad state holds valid data - * to appear inactive (no buttons and axes in neutral) */ - usbpad_update(NULL); } - state = STATE_WAIT_POLLTIME; + /* 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: - if (usb_interruptReady()) { + if (usb_interruptReady_ep1()) { state = STATE_TRANSMIT; } break; case STATE_TRANSMIT: - usb_interruptSend(usbpad_getReportBuffer(), usbpad_getReportSize()); + usb_interruptSend_ep1(usbpad_getReportBuffer(&usbpads[0]), usbpad_getReportSize()); + if (NUM_PLAYERS > 1) { + state = STATE_WAIT_INTERRUPT_READY_P2; + } else { + state = STATE_WAIT_POLLTIME; + } + break; + + case STATE_WAIT_INTERRUPT_READY_P2: + if (usb_interruptReady_ep3()) { + state = STATE_TRANSMIT_P2; + } + break; + + case STATE_TRANSMIT_P2: + usb_interruptSend_ep3(usbpad_getReportBuffer(&usbpads[1]), usbpad_getReportSize()); state = STATE_WAIT_POLLTIME; break; + } - gamepad_vibrate = usbpad_mustVibrate(); - if (last_v != gamepad_vibrate) { - if (pad && pad->setVibration) { - pad->setVibration(gamepad_vibrate); + 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; } - last_v = gamepad_vibrate; } } diff --git a/n64.c b/n64.c index 9c4823d..ecbb980 100644 --- a/n64.c +++ b/n64.c @@ -25,32 +25,33 @@ #undef BUTTON_A_RUMBLE_TEST /*********** prototypes *************/ -static void n64Init(void); -static char n64Update(void); -static char n64Changed(void); -static void n64GetReport(gamepad_data *dst); -static void n64SetVibration(char enable); +static void n64Init(unsigned char chn); +static char n64Update(unsigned char chn); +static char n64Changed(unsigned char chn); +static void n64GetReport(unsigned char chn, gamepad_data *dst); +static void n64SetVibration(unsigned char chn, char enable); -static char must_rumble = 0; +static char must_rumble[GAMEPAD_MAX_CHANNELS] = { }; #ifdef BUTTON_A_RUMBLE_TEST -static char force_rumble = 0; +static char force_rumble[GAMEPAD_MAX_CHANNELS] = { }; #endif +static unsigned char n64_rumble_state[GAMEPAD_MAX_CHANNELS] = { }; -static void n64Init(void) -{ - n64Update(); -} +unsigned char tmpdata[40]; // Shared between channels -#define RSTATE_INIT 0 +#define RSTATE_UNAVAILABLE 0 #define RSTATE_OFF 1 #define RSTATE_TURNON 2 #define RSTATE_ON 3 #define RSTATE_TURNOFF 4 -#define RSTATE_UNAVAILABLE 5 -static unsigned char n64_rumble_state = RSTATE_UNAVAILABLE; -unsigned char tmpdata[40]; +#define RSTATE_INIT 5 -static char initRumble(void) +static void n64Init(unsigned char chn) +{ + n64Update(chn); +} + +static char initRumble(unsigned char chn) { int count; unsigned char data[4]; @@ -60,14 +61,14 @@ static char initRumble(void) tmpdata[2] = 0x01; memset(tmpdata+3, 0x80, 32); - count = gcn64_transaction(GCN64_CHANNEL_0, tmpdata, 35, data, sizeof(data)); + count = gcn64_transaction(chn, tmpdata, 35, data, sizeof(data)); if (count == 1) return 0; return -1; } -static char controlRumble(char enable) +static char controlRumble(unsigned char chn, char enable) { int count; unsigned char data[4]; @@ -76,14 +77,14 @@ static char controlRumble(char enable) tmpdata[1] = 0xc0; tmpdata[2] = 0x1b; memset(tmpdata+3, enable ? 0x01 : 0x00, 32); - count = gcn64_transaction(GCN64_CHANNEL_0, tmpdata, 35, data, sizeof(data)); + count = gcn64_transaction(chn, tmpdata, 35, data, sizeof(data)); if (count == 1) return 0; return -1; } -static char n64Update(void) +static char n64Update(unsigned char chn) { unsigned char count; unsigned char x,y; @@ -101,78 +102,78 @@ static char n64Update(void) * Bit 1 tells is if there was something connected that has been removed. */ tmpdata[0] = N64_GET_CAPABILITIES; - count = gcn64_transaction(GCN64_CHANNEL_0, tmpdata, 1, caps, sizeof(caps)); + count = gcn64_transaction(chn, tmpdata, 1, caps, sizeof(caps)); if (count != N64_CAPS_REPLY_LENGTH) { // a failed read could mean the pack or controller was gone. Init // will be necessary next time we detect a pack is present. - n64_rumble_state = RSTATE_INIT; + n64_rumble_state[chn] = RSTATE_INIT; return -1; } /* Detect when a pack becomes present and schedule initialisation when it happens. */ - if ((caps[2] & 0x01) && (n64_rumble_state == RSTATE_UNAVAILABLE)) { - n64_rumble_state = RSTATE_INIT; + if ((caps[2] & 0x01) && (n64_rumble_state[chn] == RSTATE_UNAVAILABLE)) { + n64_rumble_state[chn] = RSTATE_INIT; } /* Detect when a pack is removed. */ if (!(caps[2] & 0x01) || (caps[2] & 0x02) ) { - n64_rumble_state = RSTATE_UNAVAILABLE; + n64_rumble_state[chn] = RSTATE_UNAVAILABLE; } #ifdef BUTTON_A_RUMBLE_TEST - must_rumble = force_rumble; + must_rumble[chn] = force_rumble[chn]; //printf("Caps: %02x %02x %02x\r\n", caps[0], caps[1], caps[2]); #endif - switch (n64_rumble_state) + switch (n64_rumble_state[chn]) { case RSTATE_INIT: /* Retry until the controller answers with a full byte. */ - if (initRumble() != 0) { - if (initRumble() != 0) { - n64_rumble_state = RSTATE_UNAVAILABLE; + if (initRumble(chn) != 0) { + if (initRumble(chn) != 0) { + n64_rumble_state[chn] = RSTATE_UNAVAILABLE; } break; } - if (must_rumble) { - controlRumble(1); - n64_rumble_state = RSTATE_ON; + if (must_rumble[chn]) { + controlRumble(chn, 1); + n64_rumble_state[chn] = RSTATE_ON; } else { - controlRumble(0); - n64_rumble_state = RSTATE_OFF; + controlRumble(chn, 0); + n64_rumble_state[chn] = RSTATE_OFF; } break; case RSTATE_TURNON: - if (0 == controlRumble(1)) { - n64_rumble_state = RSTATE_ON; + if (0 == controlRumble(chn, 1)) { + n64_rumble_state[chn] = RSTATE_ON; } break; case RSTATE_TURNOFF: - if (0 == controlRumble(0)) { - n64_rumble_state = RSTATE_OFF; + if (0 == controlRumble(chn, 0)) { + n64_rumble_state[chn] = RSTATE_OFF; } break; case RSTATE_ON: - if (!must_rumble) { - controlRumble(0); - n64_rumble_state = RSTATE_OFF; + if (!must_rumble[chn]) { + controlRumble(chn, 0); + n64_rumble_state[chn] = RSTATE_OFF; } break; case RSTATE_OFF: - if (must_rumble) { - controlRumble(1); - n64_rumble_state = RSTATE_ON; + if (must_rumble[chn]) { + controlRumble(chn, 1); + n64_rumble_state[chn] = RSTATE_ON; } break; } tmpdata[0] = N64_GET_STATUS; - count = gcn64_transaction(GCN64_CHANNEL_0, tmpdata, 1, status, sizeof(status)); + count = gcn64_transaction(chn, tmpdata, 1, status, sizeof(status)); if (count != N64_GET_STATUS_REPLY_LENGTH) { return -1; } @@ -206,9 +207,9 @@ static char n64Update(void) #ifdef BUTTON_A_RUMBLE_TEST if (btns1 & 0x80) { - force_rumble = 1; + force_rumble[chn] = 1; } else { - force_rumble = 0; + force_rumble[chn] = 0; } #endif @@ -250,7 +251,7 @@ static char n64Update(void) return 0; } -static char n64Probe(void) +static char n64Probe(unsigned char chn) { int count; char i; @@ -267,14 +268,14 @@ static char n64Probe(void) * Bit 1 tells is if there was something connected that has been removed. */ - n64_rumble_state = RSTATE_UNAVAILABLE; + n64_rumble_state[chn] = RSTATE_UNAVAILABLE; for (i=0; i<15; i++) { _delay_ms(30); tmp = N64_GET_CAPABILITIES; - count = gcn64_transaction(GCN64_CHANNEL_0, &tmp, 1, data, sizeof(data)); + count = gcn64_transaction(chn, &tmp, 1, data, sizeof(data)); if (count == N64_CAPS_REPLY_LENGTH) { return 1; @@ -283,12 +284,12 @@ static char n64Probe(void) return 0; } -static char n64Changed(void) +static char n64Changed(unsigned char chn) { return memcmp(&last_built_report, &last_sent_report, sizeof(gamepad_data)); } -static void n64GetReport(gamepad_data *dst) +static void n64GetReport(unsigned char chn, gamepad_data *dst) { if (dst) memcpy(dst, &last_built_report, sizeof(gamepad_data)); @@ -296,9 +297,9 @@ static void n64GetReport(gamepad_data *dst) memcpy(&last_sent_report, &last_built_report, sizeof(gamepad_data)); } -static void n64SetVibration(char enable) +static void n64SetVibration(unsigned char chn, char enable) { - must_rumble = enable; + must_rumble[chn] = enable; } static Gamepad N64Gamepad = { diff --git a/usb.c b/usb.c index 0484aca..e08b10c 100644 --- a/usb.c +++ b/usb.c @@ -434,7 +434,9 @@ static void handleSetupPacket(struct usb_request *rq) if (g_params->hid_params[rq->wIndex].getReport) { const unsigned char *data; uint16_t len; - len = g_params->hid_params[rq->wIndex].getReport(rq, &data); + len = g_params->hid_params[rq->wIndex].getReport( + g_params->hid_params[rq->wIndex].ctx, + rq, &data); if (len) { buf2EP(0, data, len, rq->wLength, 0); } @@ -513,7 +515,9 @@ static void handleDataPacket(const struct usb_request *rq, uint8_t *dat, uint16_ return; if (g_params->hid_params[rq->wIndex].setReport) { - if (g_params->hid_params[rq->wIndex].setReport(rq, dat, len)) { + if (g_params->hid_params[rq->wIndex].setReport( + g_params->hid_params[rq->wIndex].ctx, + rq, dat, len)) { UECONX |= (1< #include +#include #include "usb.h" #include "gamepads.h" #include "usbpad.h" @@ -24,7 +25,6 @@ #include "config.h" #define REPORT_ID 1 -#define REPORT_SIZE 15 // Output Report IDs for various functions #define REPORT_SET_EFFECT 0x01 @@ -48,28 +48,17 @@ #define PID_SIMULTANEOUS_MAX 3 #define PID_BLOCK_LOAD_REPORT 2 -static void buildIdleReport(unsigned char dstbuf[REPORT_SIZE]); +static void buildIdleReport(unsigned char dstbuf[USBPAD_REPORT_SIZE]); -static volatile unsigned char gamepad_vibrate = 0; // output -static unsigned char vibration_on = 0, force_vibrate = 0; -static unsigned char constant_force = 0; -static unsigned char periodic_magnitude = 0; - -static unsigned char _FFB_effect_index; -#define LOOP_MAX 0xFFFF -static unsigned int _loop_count; - -static unsigned char gamepad_report0[REPORT_SIZE]; -static unsigned char hid_report_data[8]; // Used for force feedback - -void usbpad_init() +void usbpad_init(struct usbpad *pad) { - buildIdleReport(gamepad_report0); + memset(pad, 0, sizeof(struct usbpad)); + buildIdleReport(pad->gamepad_report0); } int usbpad_getReportSize(void) { - return REPORT_SIZE; + return USBPAD_REPORT_SIZE; } static int16_t minmax(int16_t input, int16_t min, int16_t max) @@ -88,7 +77,7 @@ static void btnsToReport(unsigned short buttons, unsigned char dstbuf[2]) dstbuf[1] = buttons >> 8; } -static void buildIdleReport(unsigned char dstbuf[REPORT_SIZE]) +static void buildIdleReport(unsigned char dstbuf[USBPAD_REPORT_SIZE]) { int i; @@ -105,7 +94,7 @@ static void buildIdleReport(unsigned char dstbuf[REPORT_SIZE]) dstbuf[14] = 0; } -static void buildReportFromGC(const gc_pad_data *gc_data, unsigned char dstbuf[REPORT_SIZE]) +static void buildReportFromGC(const gc_pad_data *gc_data, unsigned char dstbuf[USBPAD_REPORT_SIZE]) { int16_t xval,yval,cxval,cyval,ltrig,rtrig; uint16_t buttons; @@ -174,7 +163,7 @@ static void buildReportFromGC(const gc_pad_data *gc_data, unsigned char dstbuf[R btnsToReport(buttons, dstbuf+13); } -static void buildReportFromN64(const n64_pad_data *n64_data, unsigned char dstbuf[REPORT_SIZE]) +static void buildReportFromN64(const n64_pad_data *n64_data, unsigned char dstbuf[USBPAD_REPORT_SIZE]) { int16_t xval, yval; uint16_t buttons; @@ -201,22 +190,22 @@ static void buildReportFromN64(const n64_pad_data *n64_data, unsigned char dstbu btnsToReport(buttons, dstbuf+13); } -void usbpad_update(const gamepad_data *pad_data) +void usbpad_update(struct usbpad *pad, const gamepad_data *pad_data) { /* Always start with an idle report. Specific report builders can just * simply ignore unused parts */ - buildIdleReport(gamepad_report0); + buildIdleReport(pad->gamepad_report0); if (pad_data) { switch (pad_data->pad_type) { case PAD_TYPE_N64: - buildReportFromN64(&pad_data->n64, gamepad_report0); + buildReportFromN64(&pad_data->n64, pad->gamepad_report0); break; case PAD_TYPE_GAMECUBE: - buildReportFromGC(&pad_data->gc, gamepad_report0); + buildReportFromGC(&pad_data->gc, pad->gamepad_report0); break; default: @@ -225,38 +214,38 @@ void usbpad_update(const gamepad_data *pad_data) } } -void usbpad_forceVibrate(char force) +void usbpad_forceVibrate(struct usbpad *pad, char force) { - force_vibrate = force; + pad->force_vibrate = force; } -char usbpad_mustVibrate(void) +char usbpad_mustVibrate(struct usbpad *pad) { - if (force_vibrate) { + if (pad->force_vibrate) { return 1; } - if (!vibration_on) { - gamepad_vibrate = 0; + if (!pad->vibration_on) { + pad->gamepad_vibrate = 0; } else { - if (constant_force > 0x7f) { - gamepad_vibrate = 1; - } else if (periodic_magnitude > 0x7f) { - gamepad_vibrate = 1; + if (pad->constant_force > 0x7f) { + pad->gamepad_vibrate = 1; + } else if (pad->periodic_magnitude > 0x7f) { + pad->gamepad_vibrate = 1; } else { - gamepad_vibrate = 0; + pad->gamepad_vibrate = 0; } } - return gamepad_vibrate; + return pad->gamepad_vibrate; } -unsigned char *usbpad_getReportBuffer(void) +unsigned char *usbpad_getReportBuffer(struct usbpad *pad) { - return gamepad_report0; + return pad->gamepad_report0; } -uint16_t usbpad_hid_get_report(struct usb_request *rq, const uint8_t **dat) +uint16_t usbpad_hid_get_report(struct usbpad *pad, struct usb_request *rq, const uint8_t **dat) { uint8_t report_id = (rq->wValue & 0xff); @@ -271,15 +260,16 @@ uint16_t usbpad_hid_get_report(struct usb_request *rq, const uint8_t **dat) if (report_id == 1) { // Joystick // report_id = rq->wValue & 0xff // interface = rq->wIndex - *dat = gamepad_report0; + *dat = pad->gamepad_report0; printf_P(PSTR("Get joy report\r\n")); - return REPORT_SIZE; + return USBPAD_REPORT_SIZE + ; } else if (report_id == 2) { // 2 : ES playing - hid_report_data[0] = report_id; - hid_report_data[1] = 0; - hid_report_data[2] = _FFB_effect_index; + pad->hid_report_data[0] = report_id; + pad->hid_report_data[1] = 0; + pad->hid_report_data[2] = pad->_FFB_effect_index; printf_P(PSTR("ES playing\r\n")); - *dat = hid_report_data; + *dat = pad->hid_report_data; return 3; } else { printf_P(PSTR("Get input report %d ??\r\n"), rq->wValue & 0xff); @@ -289,32 +279,32 @@ uint16_t usbpad_hid_get_report(struct usb_request *rq, const uint8_t **dat) case HID_REPORT_TYPE_FEATURE: if (report_id == PID_BLOCK_LOAD_REPORT) { - hid_report_data[0] = report_id; - hid_report_data[1] = 0x1; // Effect block index - hid_report_data[2] = 0x1; // (1: success, 2: oom, 3: load error) - hid_report_data[3] = 10; - hid_report_data[4] = 10; + pad->hid_report_data[0] = report_id; + pad->hid_report_data[1] = 0x1; // Effect block index + pad->hid_report_data[2] = 0x1; // (1: success, 2: oom, 3: load error) + pad->hid_report_data[3] = 10; + pad->hid_report_data[4] = 10; printf_P(PSTR("block load\r\n")); - *dat = hid_report_data; + *dat = pad->hid_report_data; return 5; } else if (report_id == PID_SIMULTANEOUS_MAX) { - hid_report_data[0] = report_id; + pad->hid_report_data[0] = report_id; // ROM Effect Block count - hid_report_data[1] = 0x1; - hid_report_data[2] = 0x1; + pad->hid_report_data[1] = 0x1; + pad->hid_report_data[2] = 0x1; // PID pool move report? - hid_report_data[3] = 0xff; - hid_report_data[4] = 1; + pad->hid_report_data[3] = 0xff; + pad->hid_report_data[4] = 1; printf_P(PSTR("simultaneous max\r\n")); - *dat = hid_report_data; + *dat = pad->hid_report_data; return 5; } else if (report_id == REPORT_CREATE_EFFECT) { - hid_report_data[0] = report_id; - hid_report_data[1] = 1; + pad->hid_report_data[0] = report_id; + pad->hid_report_data[1] = 1; printf_P(PSTR("create effect\r\n")); - *dat = hid_report_data; + *dat = pad->hid_report_data; return 2; } else { printf_P(PSTR("Unknown feature %d\r\n"), rq->wValue & 0xff); @@ -326,7 +316,7 @@ uint16_t usbpad_hid_get_report(struct usb_request *rq, const uint8_t **dat) return 0; } -uint8_t usbpad_hid_set_report(const struct usb_request *rq, const uint8_t *data, uint16_t len) +uint8_t usbpad_hid_set_report(struct usbpad *pad, const struct usb_request *rq, const uint8_t *data, uint16_t len) { if (len < 1) { printf_P(PSTR("shrt\n")); @@ -345,24 +335,24 @@ uint8_t usbpad_hid_set_report(const struct usb_request *rq, const uint8_t *data, break; case REPORT_DISABLE_ACTUATORS: printf_P(PSTR("disable actuators\r\n")); - periodic_magnitude = 0; - constant_force = 0; - vibration_on = 0; + pad->periodic_magnitude = 0; + pad->constant_force = 0; + pad->vibration_on = 0; break; case REPORT_PID_POOL: printf_P(PSTR("pid pool\r\n")); break; case REPORT_SET_EFFECT: - _FFB_effect_index = data[1]; + pad->_FFB_effect_index = data[1]; printf_P(PSTR("set effect %d\r\n"), data[1]); break; case REPORT_SET_PERIODIC: - periodic_magnitude = data[2]; + pad->periodic_magnitude = data[2]; printf_P(PSTR("periodic mag: %d"), data[2]); break; case REPORT_SET_CONSTANT_FORCE: if (data[1] == 1) { - constant_force = data[2]; + pad->constant_force = data[2]; printf_P(PSTR("Constant force %d\r\n"), data[2]); } break; @@ -373,7 +363,7 @@ uint8_t usbpad_hid_set_report(const struct usb_request *rq, const uint8_t *data, * Byte 1 : bit 7=rom flag, bits 6-0=effect block index * Byte 2 : Effect operation * Byte 3 : Loop count */ - _loop_count = data[3]<<3; + pad->_loop_count = data[3]<<3; printf_P(PSTR("EFFECT OP: rom=%s, idx=0x%02x"), data[1] & 0x80 ? "Yes":"No", data[1] & 0x7F); @@ -386,17 +376,17 @@ uint8_t usbpad_hid_set_report(const struct usb_request *rq, const uint8_t *data, { case EFFECT_OP_START: printf_P(PSTR("Start\r\n")); - vibration_on = 1; + pad->vibration_on = 1; break; case EFFECT_OP_START_SOLO: printf_P(PSTR("Start solo\r\n")); - vibration_on = 1; + pad->vibration_on = 1; break; case EFFECT_OP_STOP: printf_P(PSTR("Stop\r\n")); - vibration_on = 0; + pad->vibration_on = 0; break; } break; @@ -424,7 +414,7 @@ uint8_t usbpad_hid_set_report(const struct usb_request *rq, const uint8_t *data, switch(data[0]) { case REPORT_CREATE_EFFECT: - _FFB_effect_index = data[1]; + pad->_FFB_effect_index = data[1]; printf_P(PSTR("create effect %d\n"), data[1]); break; diff --git a/usbpad.h b/usbpad.h index 6b7ff8c..12a6ac3 100644 --- a/usbpad.h +++ b/usbpad.h @@ -4,16 +4,32 @@ #include "gamepads.h" #include "usb.h" -void usbpad_init(void); +#define USBPAD_REPORT_SIZE 15 + +struct usbpad { + volatile unsigned char gamepad_vibrate; // output + unsigned char vibration_on, force_vibrate; + unsigned char constant_force; + unsigned char periodic_magnitude; + + unsigned char _FFB_effect_index; +#define LOOP_MAX 0xFFFF + unsigned int _loop_count; + + unsigned char gamepad_report0[USBPAD_REPORT_SIZE]; + unsigned char hid_report_data[8]; // Used for force feedback +}; + +void usbpad_init(struct usbpad *pad); int usbpad_getReportSize(void); -unsigned char *usbpad_getReportBuffer(void); +unsigned char *usbpad_getReportBuffer(struct usbpad *pad); -void usbpad_update(const gamepad_data *pad_data); -char usbpad_mustVibrate(void); -void usbpad_forceVibrate(char force); +void usbpad_update(struct usbpad *pad, const gamepad_data *pad_data); +char usbpad_mustVibrate(struct usbpad *pad); +void usbpad_forceVibrate(struct usbpad *pad, char force); -uint8_t usbpad_hid_set_report(const struct usb_request *rq, const uint8_t *data, uint16_t len); -uint16_t usbpad_hid_get_report(struct usb_request *rq, const uint8_t **dat); +uint8_t usbpad_hid_set_report(struct usbpad *pad, const struct usb_request *rq, const uint8_t *data, uint16_t len); +uint16_t usbpad_hid_get_report(struct usbpad *pad, struct usb_request *rq, const uint8_t **dat); // For mappings. ID starts at 0. #define USB_BTN(id) (0x0001 << (id))