Multi-player support complete

Works, but very light testing so far
This commit is contained in:
Raphael Assenat 2016-10-10 14:19:58 -04:00
parent 90aae55470
commit 4ecc3f571e
12 changed files with 384 additions and 244 deletions

View File

@ -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 = {

View File

@ -1,7 +1,4 @@
#include "gamepads.h"
#define GAMECUBE_UPDATE_NORMAL 0
#define GAMECUBE_UPDATE_ORIGIN 1
Gamepad *gamecubeGetGamepad(void);

View File

@ -1,5 +1,5 @@
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
Copyright (C) 2007-2016 Raphael Assenat <raph@raphnet.net>
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;

View File

@ -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 */

View File

@ -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:

View File

@ -4,9 +4,14 @@
#include <stdint.h>
#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

199
main.c
View File

@ -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; i<NUM_PLAYERS; i++) {
usbpad_init(&usbpads[i]);
}
switch (g_eeprom_data.cfg.mode)
{
@ -321,10 +411,10 @@ int main(void)
while (1)
{
static char last_v = 0;
static char last_v[NUM_PLAYERS] = { };
usb_doTasks();
hiddata_doTask();
hiddata_doTask(&hiddata_ops);
switch(state)
{
@ -338,62 +428,89 @@ int main(void)
break;
case STATE_POLL_PAD:
for (channel=0; channel<NUM_PLAYERS; channel++)
{
/* Try to auto-detect controller if none*/
if (!pad) {
pad = detectPad();
if (pad && (pad->hotplug)) {
if (!pads[channel]) {
pads[channel] = detectPad(channel);
if (pads[channel] && (pads[channel]->hotplug)) {
// For gamecube, this make sure the next
// analog values we read become the center
// reference.
pad->hotplug();
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;
break;
/* 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=0;
error_count[channel]=0;
}
if (pad->changed()) {
pad->getReport(&pad_data);
usbpad_update(&pad_data);
if (pads[channel]->changed(channel))
{
pads[channel]->getReport(channel, &pad_data);
usbpad_update(&usbpads[channel], &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);
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:
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;
}
}

111
n64.c
View File

@ -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 = {

8
usb.c
View File

@ -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<<STALLRQ);
} else {
// xmit status

5
usb.h
View File

@ -176,8 +176,9 @@ struct usb_hid_parameters {
const unsigned char *reportdesc;
// Warning: Called from interrupt handler. Implement accordingly.
uint16_t (*getReport)(struct usb_request *rq, const uint8_t **dat);
uint8_t (*setReport)(const struct usb_request *rq, const uint8_t *dat, uint16_t len);
void *ctx;
uint16_t (*getReport)(void *ctx, struct usb_request *rq, const uint8_t **dat);
uint8_t (*setReport)(void *ctx, const struct usb_request *rq, const uint8_t *dat, uint16_t len);
};
struct usb_parameters {

134
usbpad.c
View File

@ -16,6 +16,7 @@
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#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;

View File

@ -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))