mirror of
https://github.com/raphnet/gc_n64_usb-v3
synced 2024-12-21 23:08:53 -05:00
Multi-player support complete
Works, but very light testing so far
This commit is contained in:
parent
90aae55470
commit
4ecc3f571e
65
gamecube.c
65
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 = {
|
||||
|
@ -1,7 +1,4 @@
|
||||
#include "gamepads.h"
|
||||
|
||||
#define GAMECUBE_UPDATE_NORMAL 0
|
||||
#define GAMECUBE_UPDATE_ORIGIN 1
|
||||
|
||||
Gamepad *gamecubeGetGamepad(void);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
15
gamepads.h
15
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 */
|
||||
|
18
hiddata.c
18
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:
|
||||
|
11
hiddata.h
11
hiddata.h
@ -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
199
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; 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
111
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 = {
|
||||
|
8
usb.c
8
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<<STALLRQ);
|
||||
} else {
|
||||
// xmit status
|
||||
|
5
usb.h
5
usb.h
@ -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
134
usbpad.c
@ -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;
|
||||
|
||||
|
30
usbpad.h
30
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))
|
||||
|
Loading…
Reference in New Issue
Block a user