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"
|
#include "gcn64_protocol.h"
|
||||||
|
|
||||||
/*********** prototypes *************/
|
/*********** prototypes *************/
|
||||||
static void gamecubeInit(void);
|
static void gamecubeInit(unsigned char chn);
|
||||||
static char gamecubeUpdate(void);
|
static char gamecubeUpdate(unsigned char chn);
|
||||||
static char gamecubeChanged(void);
|
static char gamecubeChanged(unsigned char chn);
|
||||||
|
|
||||||
static char gc_rumbling = 0;
|
static char gc_rumbling[GAMEPAD_MAX_CHANNELS] = { };
|
||||||
static char origins_set = 0;
|
static char origins_set[GAMEPAD_MAX_CHANNELS] = { };
|
||||||
static unsigned char orig_x, orig_y, orig_cx, orig_cy;
|
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;
|
unsigned char x,y,cx,cy;
|
||||||
|
|
||||||
@ -91,74 +94,74 @@ void gc_decodeAnswer(unsigned char data[8])
|
|||||||
last_built_report.gc.rt = data[7];
|
last_built_report.gc.rt = data[7];
|
||||||
memcpy(last_built_report.gc.raw_data, data, 8);
|
memcpy(last_built_report.gc.raw_data, data, 8);
|
||||||
|
|
||||||
if (origins_set) {
|
if (origins_set[chn]) {
|
||||||
last_built_report.gc.x = ((int)x-(int)orig_x);
|
last_built_report.gc.x = ((int)x-(int)orig_x[chn]);
|
||||||
last_built_report.gc.y = ((int)y-(int)orig_y);
|
last_built_report.gc.y = ((int)y-(int)orig_y[chn]);
|
||||||
last_built_report.gc.cx = ((int)cx-(int)orig_cx);
|
last_built_report.gc.cx = ((int)cx-(int)orig_cx[chn]);
|
||||||
last_built_report.gc.cy = ((int)cy-(int)orig_cy);
|
last_built_report.gc.cy = ((int)cy-(int)orig_cy[chn]);
|
||||||
} else {
|
} else {
|
||||||
orig_x = x;
|
orig_x[chn] = x;
|
||||||
orig_y = y;
|
orig_y[chn] = y;
|
||||||
orig_cx = cx;
|
orig_cx[chn] = cx;
|
||||||
orig_cy = cy;
|
orig_cy[chn] = cy;
|
||||||
last_built_report.gc.x = 0;
|
last_built_report.gc.x = 0;
|
||||||
last_built_report.gc.y = 0;
|
last_built_report.gc.y = 0;
|
||||||
last_built_report.gc.cx = 0;
|
last_built_report.gc.cx = 0;
|
||||||
last_built_report.gc.cy = 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 tmpdata[GC_GETSTATUS_REPLY_LENGTH];
|
||||||
unsigned char count;
|
unsigned char count;
|
||||||
|
|
||||||
tmpdata[0] = GC_GETSTATUS1;
|
tmpdata[0] = GC_GETSTATUS1;
|
||||||
tmpdata[1] = GC_GETSTATUS2;
|
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);
|
count = gcn64_transaction(GCN64_CHANNEL_0, tmpdata, 3, tmpdata, GC_GETSTATUS_REPLY_LENGTH);
|
||||||
if (count != GC_GETSTATUS_REPLY_LENGTH) {
|
if (count != GC_GETSTATUS_REPLY_LENGTH) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
gc_decodeAnswer(tmpdata);
|
gc_decodeAnswer(chn, tmpdata);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gamecubeHotplug(void)
|
static void gamecubeHotplug(unsigned char chn)
|
||||||
{
|
{
|
||||||
// Make sure next read becomes the refence center values
|
// 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 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char gamecubeChanged(void)
|
static char gamecubeChanged(unsigned char chn)
|
||||||
{
|
{
|
||||||
return memcmp(&last_built_report, &last_sent_report, sizeof(gamepad_data));
|
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)
|
if (dst)
|
||||||
memcpy(dst, &last_built_report, sizeof(gamepad_data));
|
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 = {
|
Gamepad GamecubeGamepad = {
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
#include "gamepads.h"
|
#include "gamepads.h"
|
||||||
|
|
||||||
#define GAMECUBE_UPDATE_NORMAL 0
|
|
||||||
#define GAMECUBE_UPDATE_ORIGIN 1
|
|
||||||
|
|
||||||
Gamepad *gamecubeGetGamepad(void);
|
Gamepad *gamecubeGetGamepad(void);
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
|
/* 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
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -16,7 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
#include "gamepads.h"
|
#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_sent_report;
|
||||||
gamepad_data last_built_report;
|
gamepad_data last_built_report;
|
||||||
|
|
||||||
|
15
gamepads.h
15
gamepads.h
@ -71,14 +71,15 @@ typedef struct _gamepad_data {
|
|||||||
};
|
};
|
||||||
} gamepad_data;
|
} gamepad_data;
|
||||||
|
|
||||||
|
#define GAMEPAD_MAX_CHANNELS 2
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void (*init)(void);
|
void (*init)(unsigned char chn);
|
||||||
char (*update)(void);
|
char (*update)(unsigned char chn);
|
||||||
char (*changed)(void);
|
char (*changed)(unsigned char chn);
|
||||||
void (*hotplug)(void);
|
void (*hotplug)(unsigned char chn);
|
||||||
void (*getReport)(gamepad_data *dst);
|
void (*getReport)(unsigned char chn, gamepad_data *dst);
|
||||||
void (*setVibration)(char enable);
|
void (*setVibration)(unsigned char chn, char enable);
|
||||||
char (*probe)(void); /* return true if found */
|
char (*probe)(unsigned char chn); /* return true if found */
|
||||||
} Gamepad;
|
} Gamepad;
|
||||||
|
|
||||||
/* What was most recently read from the controller */
|
/* 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;
|
static volatile unsigned char cmdbuf_len = 0;
|
||||||
|
|
||||||
/*** Get/Set report called from interrupt context! */
|
/*** 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");
|
// printf("Get data\n");
|
||||||
if (state == STATE_COMMAND_DONE) {
|
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! */
|
/*** 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
|
#ifdef DEBUG
|
||||||
int i;
|
int i;
|
||||||
@ -74,7 +74,7 @@ uint8_t hiddata_set_report(const struct usb_request *rq, const uint8_t *dat, uin
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hiddata_processCommandBuffer(void)
|
static void hiddata_processCommandBuffer(struct hiddata_ops *ops)
|
||||||
{
|
{
|
||||||
unsigned char channel;
|
unsigned char channel;
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
@ -114,7 +114,9 @@ static void hiddata_processCommandBuffer(void)
|
|||||||
break;
|
break;
|
||||||
case RQ_GCN64_SUSPEND_POLLING:
|
case RQ_GCN64_SUSPEND_POLLING:
|
||||||
// CMD: RQ, PARAM
|
// CMD: RQ, PARAM
|
||||||
g_polling_suspended = cmdbuf[1];
|
if (ops && ops->suspendPolling) {
|
||||||
|
ops->suspendPolling(cmdbuf[1]);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case RQ_GCN64_GET_VERSION:
|
case RQ_GCN64_GET_VERSION:
|
||||||
// CMD: RQ
|
// CMD: RQ
|
||||||
@ -135,7 +137,9 @@ static void hiddata_processCommandBuffer(void)
|
|||||||
case RQ_GCN64_SET_VIBRATION:
|
case RQ_GCN64_SET_VIBRATION:
|
||||||
// CMD : RQ, CHN, Vibrate
|
// CMD : RQ, CHN, Vibrate
|
||||||
// Answer: RQ, CHN, Vibrate
|
// Answer: RQ, CHN, Vibrate
|
||||||
usbpad_forceVibrate(cmdbuf[2]);
|
if (ops && ops->forceVibration) {
|
||||||
|
ops->forceVibration(cmdbuf[1], cmdbuf[2]);
|
||||||
|
}
|
||||||
cmdbuf_len = 3;
|
cmdbuf_len = 3;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -151,7 +155,7 @@ static void hiddata_processCommandBuffer(void)
|
|||||||
state = STATE_COMMAND_DONE;
|
state = STATE_COMMAND_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void hiddata_doTask(void)
|
void hiddata_doTask(struct hiddata_ops *ops)
|
||||||
{
|
{
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
@ -161,7 +165,7 @@ void hiddata_doTask(void)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_NEW_COMMAND:
|
case STATE_NEW_COMMAND:
|
||||||
hiddata_processCommandBuffer();
|
hiddata_processCommandBuffer(ops);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_COMMAND_DONE:
|
case STATE_COMMAND_DONE:
|
||||||
|
11
hiddata.h
11
hiddata.h
@ -4,9 +4,14 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
|
|
||||||
uint16_t hiddata_get_report(struct usb_request *rq, const uint8_t **dat);
|
struct hiddata_ops {
|
||||||
uint8_t hiddata_set_report(const struct usb_request *rq, const uint8_t *dat, uint16_t len);
|
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
|
#endif
|
||||||
|
223
main.c
223
main.c
@ -38,6 +38,8 @@
|
|||||||
#include "intervaltimer.h"
|
#include "intervaltimer.h"
|
||||||
#include "requests.h"
|
#include "requests.h"
|
||||||
|
|
||||||
|
#define NUM_PLAYERS 2
|
||||||
|
|
||||||
#define GCN64_USB_PID 0x001D
|
#define GCN64_USB_PID 0x001D
|
||||||
#define N64_USB_PID 0x0020
|
#define N64_USB_PID 0x0020
|
||||||
#define GC_USB_PID 0x0021
|
#define GC_USB_PID 0x0021
|
||||||
@ -58,6 +60,22 @@ struct cfg0 {
|
|||||||
struct usb_interface_descriptor interface_admin;
|
struct usb_interface_descriptor interface_admin;
|
||||||
struct usb_hid_descriptor hid_data;
|
struct usb_hid_descriptor hid_data;
|
||||||
struct usb_endpoint_descriptor ep2_in;
|
struct usb_endpoint_descriptor ep2_in;
|
||||||
|
|
||||||
|
#if NUM_PLAYERS >= 2
|
||||||
|
struct usb_interface_descriptor interface_p2;
|
||||||
|
struct usb_hid_descriptor hid_p2;
|
||||||
|
struct usb_endpoint_descriptor ep3_in;
|
||||||
|
#endif
|
||||||
|
#if NUM_PLAYERS >= 3
|
||||||
|
struct usb_interface_descriptor interface_p3;
|
||||||
|
struct usb_hid_descriptor hid_p3;
|
||||||
|
struct usb_endpoint_descriptor ep4_in;
|
||||||
|
#endif
|
||||||
|
#if NUM_PLAYERS >= 4
|
||||||
|
struct usb_interface_descriptor interface_p4;
|
||||||
|
struct usb_hid_descriptor hid_p4;
|
||||||
|
struct usb_endpoint_descriptor ep5_in;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct cfg0 cfg0 PROGMEM = {
|
static const struct cfg0 cfg0 PROGMEM = {
|
||||||
@ -65,13 +83,13 @@ static const struct cfg0 cfg0 PROGMEM = {
|
|||||||
.bLength = sizeof(struct usb_configuration_descriptor),
|
.bLength = sizeof(struct usb_configuration_descriptor),
|
||||||
.bDescriptorType = CONFIGURATION_DESCRIPTOR,
|
.bDescriptorType = CONFIGURATION_DESCRIPTOR,
|
||||||
.wTotalLength = sizeof(cfg0), // includes all descriptors returned together
|
.wTotalLength = sizeof(cfg0), // includes all descriptors returned together
|
||||||
.bNumInterfaces = 2,
|
.bNumInterfaces = NUM_PLAYERS + 1, // one interface per player + one management interface
|
||||||
.bConfigurationValue = 1,
|
.bConfigurationValue = 1,
|
||||||
.bmAttributes = CFG_DESC_ATTR_RESERVED, // set Self-powred and remote-wakeup here if needed.
|
.bmAttributes = CFG_DESC_ATTR_RESERVED, // set Self-powred and remote-wakeup here if needed.
|
||||||
.bMaxPower = 25, // for 50mA
|
.bMaxPower = 25, // for 50mA
|
||||||
},
|
},
|
||||||
|
|
||||||
// Main interface, HID
|
// Main interface, HID (player 1)
|
||||||
.interface = {
|
.interface = {
|
||||||
.bLength = sizeof(struct usb_interface_descriptor),
|
.bLength = sizeof(struct usb_interface_descriptor),
|
||||||
.bDescriptorType = INTERFACE_DESCRIPTOR,
|
.bDescriptorType = INTERFACE_DESCRIPTOR,
|
||||||
@ -96,7 +114,7 @@ static const struct cfg0 cfg0 PROGMEM = {
|
|||||||
.bDescriptorType = ENDPOINT_DESCRIPTOR,
|
.bDescriptorType = ENDPOINT_DESCRIPTOR,
|
||||||
.bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 1, // 0x81
|
.bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 1, // 0x81
|
||||||
.bmAttributes = TRANSFER_TYPE_INT,
|
.bmAttributes = TRANSFER_TYPE_INT,
|
||||||
.wMaxPacketsize = 32,
|
.wMaxPacketsize = 16,
|
||||||
.bInterval = LS_FS_INTERVAL_MS(1),
|
.bInterval = LS_FS_INTERVAL_MS(1),
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -129,6 +147,37 @@ static const struct cfg0 cfg0 PROGMEM = {
|
|||||||
.bInterval = LS_FS_INTERVAL_MS(1),
|
.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 = {
|
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 = {
|
static struct usb_parameters usb_params = {
|
||||||
.flags = USB_PARAM_FLAG_CONFDESC_PROGMEM |
|
.flags = USB_PARAM_FLAG_CONFDESC_PROGMEM |
|
||||||
USB_PARAM_FLAG_REPORTDESC_PROGMEM,
|
USB_PARAM_FLAG_REPORTDESC_PROGMEM,
|
||||||
@ -159,13 +218,13 @@ static struct usb_parameters usb_params = {
|
|||||||
.num_strings = NUM_USB_STRINGS,
|
.num_strings = NUM_USB_STRINGS,
|
||||||
.strings = g_usb_strings,
|
.strings = g_usb_strings,
|
||||||
|
|
||||||
.n_hid_interfaces = 2,
|
.n_hid_interfaces = 1 + NUM_PLAYERS,
|
||||||
.hid_params = {
|
.hid_params = {
|
||||||
[0] = {
|
[0] = {
|
||||||
.reportdesc = gcn64_usbHidReportDescriptor,
|
.reportdesc = gcn64_usbHidReportDescriptor,
|
||||||
.reportdesc_len = sizeof(gcn64_usbHidReportDescriptor),
|
.reportdesc_len = sizeof(gcn64_usbHidReportDescriptor),
|
||||||
.getReport = usbpad_hid_get_report,
|
.getReport = _usbpad_hid_get_report,
|
||||||
.setReport = usbpad_hid_set_report,
|
.setReport = _usbpad_hid_set_report,
|
||||||
},
|
},
|
||||||
[1] = {
|
[1] = {
|
||||||
.reportdesc = dataHidReport,
|
.reportdesc = dataHidReport,
|
||||||
@ -173,6 +232,14 @@ static struct usb_parameters usb_params = {
|
|||||||
.getReport = hiddata_get_report,
|
.getReport = hiddata_get_report,
|
||||||
.setReport = hiddata_set_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;
|
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)
|
switch (current_pad_type)
|
||||||
{
|
{
|
||||||
@ -269,7 +334,6 @@ void eeprom_app_ready(void)
|
|||||||
g_usb_strings[USB_STRING_SERIAL_IDX] = serial_from_eeprom;
|
g_usb_strings[USB_STRING_SERIAL_IDX] = serial_from_eeprom;
|
||||||
}
|
}
|
||||||
|
|
||||||
char g_polling_suspended = 0;
|
|
||||||
|
|
||||||
void pollDelay(void)
|
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_WAIT_POLLTIME 0
|
||||||
#define STATE_POLL_PAD 1
|
#define STATE_POLL_PAD 1
|
||||||
#define STATE_WAIT_INTERRUPT_READY 2
|
#define STATE_WAIT_INTERRUPT_READY 2
|
||||||
#define STATE_TRANSMIT 3
|
#define STATE_TRANSMIT 3
|
||||||
|
#define STATE_WAIT_INTERRUPT_READY_P2 4
|
||||||
|
#define STATE_TRANSMIT_P2 5
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
Gamepad *pad = NULL;
|
Gamepad *pads[NUM_PLAYERS] = { };
|
||||||
gamepad_data pad_data;
|
gamepad_data pad_data;
|
||||||
unsigned char gamepad_vibrate = 0;
|
unsigned char gamepad_vibrate = 0;
|
||||||
unsigned char state = STATE_WAIT_POLLTIME;
|
unsigned char state = STATE_WAIT_POLLTIME;
|
||||||
int error_count=0;
|
int error_count[NUM_PLAYERS] = { };
|
||||||
|
int i;
|
||||||
|
int channel;
|
||||||
|
|
||||||
hwinit();
|
hwinit();
|
||||||
usart1_init();
|
usart1_init();
|
||||||
eeprom_init();
|
eeprom_init();
|
||||||
intervaltimer_init();
|
intervaltimer_init();
|
||||||
|
|
||||||
usbpad_init();
|
for (i=0; i<NUM_PLAYERS; i++) {
|
||||||
|
usbpad_init(&usbpads[i]);
|
||||||
|
}
|
||||||
|
|
||||||
switch (g_eeprom_data.cfg.mode)
|
switch (g_eeprom_data.cfg.mode)
|
||||||
{
|
{
|
||||||
@ -321,10 +411,10 @@ int main(void)
|
|||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
static char last_v = 0;
|
static char last_v[NUM_PLAYERS] = { };
|
||||||
|
|
||||||
usb_doTasks();
|
usb_doTasks();
|
||||||
hiddata_doTask();
|
hiddata_doTask(&hiddata_ops);
|
||||||
|
|
||||||
switch(state)
|
switch(state)
|
||||||
{
|
{
|
||||||
@ -338,62 +428,89 @@ int main(void)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_POLL_PAD:
|
case STATE_POLL_PAD:
|
||||||
/* Try to auto-detect controller if none*/
|
for (channel=0; channel<NUM_PLAYERS; channel++)
|
||||||
if (!pad) {
|
{
|
||||||
pad = detectPad();
|
/* Try to auto-detect controller if none*/
|
||||||
if (pad && (pad->hotplug)) {
|
if (!pads[channel]) {
|
||||||
// For gamecube, this make sure the next
|
pads[channel] = detectPad(channel);
|
||||||
// analog values we read become the center
|
if (pads[channel] && (pads[channel]->hotplug)) {
|
||||||
// reference.
|
// For gamecube, this make sure the next
|
||||||
pad->hotplug();
|
// analog values we read become the center
|
||||||
|
// reference.
|
||||||
|
pads[channel]->hotplug(channel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (pad) {
|
/* Read from the pad by calling update */
|
||||||
if (pad->update()) {
|
if (pads[channel]) {
|
||||||
error_count++;
|
if (pads[channel]->update(channel)) {
|
||||||
if (error_count > MAX_READ_ERRORS) {
|
error_count[channel]++;
|
||||||
pad = NULL;
|
if (error_count[channel] > MAX_READ_ERRORS) {
|
||||||
state = STATE_WAIT_POLLTIME;
|
pads[channel] = NULL;
|
||||||
error_count = 0;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} 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;
|
break;
|
||||||
|
|
||||||
case STATE_WAIT_INTERRUPT_READY:
|
case STATE_WAIT_INTERRUPT_READY:
|
||||||
if (usb_interruptReady()) {
|
if (usb_interruptReady_ep1()) {
|
||||||
state = STATE_TRANSMIT;
|
state = STATE_TRANSMIT;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_TRANSMIT:
|
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;
|
state = STATE_WAIT_POLLTIME;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gamepad_vibrate = usbpad_mustVibrate();
|
for (channel=0; channel < NUM_PLAYERS; channel++) {
|
||||||
if (last_v != gamepad_vibrate) {
|
gamepad_vibrate = usbpad_mustVibrate(&usbpads[channel]);
|
||||||
if (pad && pad->setVibration) {
|
if (last_v[channel] != gamepad_vibrate) {
|
||||||
pad->setVibration(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
|
#undef BUTTON_A_RUMBLE_TEST
|
||||||
|
|
||||||
/*********** prototypes *************/
|
/*********** prototypes *************/
|
||||||
static void n64Init(void);
|
static void n64Init(unsigned char chn);
|
||||||
static char n64Update(void);
|
static char n64Update(unsigned char chn);
|
||||||
static char n64Changed(void);
|
static char n64Changed(unsigned char chn);
|
||||||
static void n64GetReport(gamepad_data *dst);
|
static void n64GetReport(unsigned char chn, gamepad_data *dst);
|
||||||
static void n64SetVibration(char enable);
|
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
|
#ifdef BUTTON_A_RUMBLE_TEST
|
||||||
static char force_rumble = 0;
|
static char force_rumble[GAMEPAD_MAX_CHANNELS] = { };
|
||||||
#endif
|
#endif
|
||||||
|
static unsigned char n64_rumble_state[GAMEPAD_MAX_CHANNELS] = { };
|
||||||
|
|
||||||
static void n64Init(void)
|
unsigned char tmpdata[40]; // Shared between channels
|
||||||
{
|
|
||||||
n64Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
#define RSTATE_INIT 0
|
#define RSTATE_UNAVAILABLE 0
|
||||||
#define RSTATE_OFF 1
|
#define RSTATE_OFF 1
|
||||||
#define RSTATE_TURNON 2
|
#define RSTATE_TURNON 2
|
||||||
#define RSTATE_ON 3
|
#define RSTATE_ON 3
|
||||||
#define RSTATE_TURNOFF 4
|
#define RSTATE_TURNOFF 4
|
||||||
#define RSTATE_UNAVAILABLE 5
|
#define RSTATE_INIT 5
|
||||||
static unsigned char n64_rumble_state = RSTATE_UNAVAILABLE;
|
|
||||||
unsigned char tmpdata[40];
|
|
||||||
|
|
||||||
static char initRumble(void)
|
static void n64Init(unsigned char chn)
|
||||||
|
{
|
||||||
|
n64Update(chn);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char initRumble(unsigned char chn)
|
||||||
{
|
{
|
||||||
int count;
|
int count;
|
||||||
unsigned char data[4];
|
unsigned char data[4];
|
||||||
@ -60,14 +61,14 @@ static char initRumble(void)
|
|||||||
tmpdata[2] = 0x01;
|
tmpdata[2] = 0x01;
|
||||||
memset(tmpdata+3, 0x80, 32);
|
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)
|
if (count == 1)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char controlRumble(char enable)
|
static char controlRumble(unsigned char chn, char enable)
|
||||||
{
|
{
|
||||||
int count;
|
int count;
|
||||||
unsigned char data[4];
|
unsigned char data[4];
|
||||||
@ -76,14 +77,14 @@ static char controlRumble(char enable)
|
|||||||
tmpdata[1] = 0xc0;
|
tmpdata[1] = 0xc0;
|
||||||
tmpdata[2] = 0x1b;
|
tmpdata[2] = 0x1b;
|
||||||
memset(tmpdata+3, enable ? 0x01 : 0x00, 32);
|
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)
|
if (count == 1)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char n64Update(void)
|
static char n64Update(unsigned char chn)
|
||||||
{
|
{
|
||||||
unsigned char count;
|
unsigned char count;
|
||||||
unsigned char x,y;
|
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.
|
* Bit 1 tells is if there was something connected that has been removed.
|
||||||
*/
|
*/
|
||||||
tmpdata[0] = N64_GET_CAPABILITIES;
|
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) {
|
if (count != N64_CAPS_REPLY_LENGTH) {
|
||||||
// a failed read could mean the pack or controller was gone. Init
|
// a failed read could mean the pack or controller was gone. Init
|
||||||
// will be necessary next time we detect a pack is present.
|
// will be necessary next time we detect a pack is present.
|
||||||
n64_rumble_state = RSTATE_INIT;
|
n64_rumble_state[chn] = RSTATE_INIT;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Detect when a pack becomes present and schedule initialisation when it happens. */
|
/* Detect when a pack becomes present and schedule initialisation when it happens. */
|
||||||
if ((caps[2] & 0x01) && (n64_rumble_state == RSTATE_UNAVAILABLE)) {
|
if ((caps[2] & 0x01) && (n64_rumble_state[chn] == RSTATE_UNAVAILABLE)) {
|
||||||
n64_rumble_state = RSTATE_INIT;
|
n64_rumble_state[chn] = RSTATE_INIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Detect when a pack is removed. */
|
/* Detect when a pack is removed. */
|
||||||
if (!(caps[2] & 0x01) || (caps[2] & 0x02) ) {
|
if (!(caps[2] & 0x01) || (caps[2] & 0x02) ) {
|
||||||
n64_rumble_state = RSTATE_UNAVAILABLE;
|
n64_rumble_state[chn] = RSTATE_UNAVAILABLE;
|
||||||
}
|
}
|
||||||
#ifdef BUTTON_A_RUMBLE_TEST
|
#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]);
|
//printf("Caps: %02x %02x %02x\r\n", caps[0], caps[1], caps[2]);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
switch (n64_rumble_state)
|
switch (n64_rumble_state[chn])
|
||||||
{
|
{
|
||||||
case RSTATE_INIT:
|
case RSTATE_INIT:
|
||||||
/* Retry until the controller answers with a full byte. */
|
/* Retry until the controller answers with a full byte. */
|
||||||
if (initRumble() != 0) {
|
if (initRumble(chn) != 0) {
|
||||||
if (initRumble() != 0) {
|
if (initRumble(chn) != 0) {
|
||||||
n64_rumble_state = RSTATE_UNAVAILABLE;
|
n64_rumble_state[chn] = RSTATE_UNAVAILABLE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (must_rumble) {
|
if (must_rumble[chn]) {
|
||||||
controlRumble(1);
|
controlRumble(chn, 1);
|
||||||
n64_rumble_state = RSTATE_ON;
|
n64_rumble_state[chn] = RSTATE_ON;
|
||||||
} else {
|
} else {
|
||||||
controlRumble(0);
|
controlRumble(chn, 0);
|
||||||
n64_rumble_state = RSTATE_OFF;
|
n64_rumble_state[chn] = RSTATE_OFF;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RSTATE_TURNON:
|
case RSTATE_TURNON:
|
||||||
if (0 == controlRumble(1)) {
|
if (0 == controlRumble(chn, 1)) {
|
||||||
n64_rumble_state = RSTATE_ON;
|
n64_rumble_state[chn] = RSTATE_ON;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RSTATE_TURNOFF:
|
case RSTATE_TURNOFF:
|
||||||
if (0 == controlRumble(0)) {
|
if (0 == controlRumble(chn, 0)) {
|
||||||
n64_rumble_state = RSTATE_OFF;
|
n64_rumble_state[chn] = RSTATE_OFF;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RSTATE_ON:
|
case RSTATE_ON:
|
||||||
if (!must_rumble) {
|
if (!must_rumble[chn]) {
|
||||||
controlRumble(0);
|
controlRumble(chn, 0);
|
||||||
n64_rumble_state = RSTATE_OFF;
|
n64_rumble_state[chn] = RSTATE_OFF;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RSTATE_OFF:
|
case RSTATE_OFF:
|
||||||
if (must_rumble) {
|
if (must_rumble[chn]) {
|
||||||
controlRumble(1);
|
controlRumble(chn, 1);
|
||||||
n64_rumble_state = RSTATE_ON;
|
n64_rumble_state[chn] = RSTATE_ON;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpdata[0] = N64_GET_STATUS;
|
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) {
|
if (count != N64_GET_STATUS_REPLY_LENGTH) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -206,9 +207,9 @@ static char n64Update(void)
|
|||||||
|
|
||||||
#ifdef BUTTON_A_RUMBLE_TEST
|
#ifdef BUTTON_A_RUMBLE_TEST
|
||||||
if (btns1 & 0x80) {
|
if (btns1 & 0x80) {
|
||||||
force_rumble = 1;
|
force_rumble[chn] = 1;
|
||||||
} else {
|
} else {
|
||||||
force_rumble = 0;
|
force_rumble[chn] = 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -250,7 +251,7 @@ static char n64Update(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char n64Probe(void)
|
static char n64Probe(unsigned char chn)
|
||||||
{
|
{
|
||||||
int count;
|
int count;
|
||||||
char i;
|
char i;
|
||||||
@ -267,14 +268,14 @@ static char n64Probe(void)
|
|||||||
* Bit 1 tells is if there was something connected that has been removed.
|
* 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++)
|
for (i=0; i<15; i++)
|
||||||
{
|
{
|
||||||
_delay_ms(30);
|
_delay_ms(30);
|
||||||
|
|
||||||
tmp = N64_GET_CAPABILITIES;
|
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) {
|
if (count == N64_CAPS_REPLY_LENGTH) {
|
||||||
return 1;
|
return 1;
|
||||||
@ -283,12 +284,12 @@ static char n64Probe(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char n64Changed(void)
|
static char n64Changed(unsigned char chn)
|
||||||
{
|
{
|
||||||
return memcmp(&last_built_report, &last_sent_report, sizeof(gamepad_data));
|
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)
|
if (dst)
|
||||||
memcpy(dst, &last_built_report, sizeof(gamepad_data));
|
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));
|
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 = {
|
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) {
|
if (g_params->hid_params[rq->wIndex].getReport) {
|
||||||
const unsigned char *data;
|
const unsigned char *data;
|
||||||
uint16_t len;
|
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) {
|
if (len) {
|
||||||
buf2EP(0, data, len, rq->wLength, 0);
|
buf2EP(0, data, len, rq->wLength, 0);
|
||||||
}
|
}
|
||||||
@ -513,7 +515,9 @@ static void handleDataPacket(const struct usb_request *rq, uint8_t *dat, uint16_
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (g_params->hid_params[rq->wIndex].setReport) {
|
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);
|
UECONX |= (1<<STALLRQ);
|
||||||
} else {
|
} else {
|
||||||
// xmit status
|
// xmit status
|
||||||
|
5
usb.h
5
usb.h
@ -176,8 +176,9 @@ struct usb_hid_parameters {
|
|||||||
const unsigned char *reportdesc;
|
const unsigned char *reportdesc;
|
||||||
|
|
||||||
// Warning: Called from interrupt handler. Implement accordingly.
|
// Warning: Called from interrupt handler. Implement accordingly.
|
||||||
uint16_t (*getReport)(struct usb_request *rq, const uint8_t **dat);
|
void *ctx;
|
||||||
uint8_t (*setReport)(const struct usb_request *rq, const uint8_t *dat, uint16_t len);
|
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 {
|
struct usb_parameters {
|
||||||
|
134
usbpad.c
134
usbpad.c
@ -16,6 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
#include "gamepads.h"
|
#include "gamepads.h"
|
||||||
#include "usbpad.h"
|
#include "usbpad.h"
|
||||||
@ -24,7 +25,6 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#define REPORT_ID 1
|
#define REPORT_ID 1
|
||||||
#define REPORT_SIZE 15
|
|
||||||
|
|
||||||
// Output Report IDs for various functions
|
// Output Report IDs for various functions
|
||||||
#define REPORT_SET_EFFECT 0x01
|
#define REPORT_SET_EFFECT 0x01
|
||||||
@ -48,28 +48,17 @@
|
|||||||
#define PID_SIMULTANEOUS_MAX 3
|
#define PID_SIMULTANEOUS_MAX 3
|
||||||
#define PID_BLOCK_LOAD_REPORT 2
|
#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
|
void usbpad_init(struct usbpad *pad)
|
||||||
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()
|
|
||||||
{
|
{
|
||||||
buildIdleReport(gamepad_report0);
|
memset(pad, 0, sizeof(struct usbpad));
|
||||||
|
buildIdleReport(pad->gamepad_report0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int usbpad_getReportSize(void)
|
int usbpad_getReportSize(void)
|
||||||
{
|
{
|
||||||
return REPORT_SIZE;
|
return USBPAD_REPORT_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int16_t minmax(int16_t input, int16_t min, int16_t max)
|
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;
|
dstbuf[1] = buttons >> 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void buildIdleReport(unsigned char dstbuf[REPORT_SIZE])
|
static void buildIdleReport(unsigned char dstbuf[USBPAD_REPORT_SIZE])
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -105,7 +94,7 @@ static void buildIdleReport(unsigned char dstbuf[REPORT_SIZE])
|
|||||||
dstbuf[14] = 0;
|
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;
|
int16_t xval,yval,cxval,cyval,ltrig,rtrig;
|
||||||
uint16_t buttons;
|
uint16_t buttons;
|
||||||
@ -174,7 +163,7 @@ static void buildReportFromGC(const gc_pad_data *gc_data, unsigned char dstbuf[R
|
|||||||
btnsToReport(buttons, dstbuf+13);
|
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;
|
int16_t xval, yval;
|
||||||
uint16_t buttons;
|
uint16_t buttons;
|
||||||
@ -201,22 +190,22 @@ static void buildReportFromN64(const n64_pad_data *n64_data, unsigned char dstbu
|
|||||||
btnsToReport(buttons, dstbuf+13);
|
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
|
/* Always start with an idle report. Specific report builders can just
|
||||||
* simply ignore unused parts */
|
* simply ignore unused parts */
|
||||||
buildIdleReport(gamepad_report0);
|
buildIdleReport(pad->gamepad_report0);
|
||||||
|
|
||||||
if (pad_data)
|
if (pad_data)
|
||||||
{
|
{
|
||||||
switch (pad_data->pad_type)
|
switch (pad_data->pad_type)
|
||||||
{
|
{
|
||||||
case PAD_TYPE_N64:
|
case PAD_TYPE_N64:
|
||||||
buildReportFromN64(&pad_data->n64, gamepad_report0);
|
buildReportFromN64(&pad_data->n64, pad->gamepad_report0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PAD_TYPE_GAMECUBE:
|
case PAD_TYPE_GAMECUBE:
|
||||||
buildReportFromGC(&pad_data->gc, gamepad_report0);
|
buildReportFromGC(&pad_data->gc, pad->gamepad_report0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!vibration_on) {
|
if (!pad->vibration_on) {
|
||||||
gamepad_vibrate = 0;
|
pad->gamepad_vibrate = 0;
|
||||||
} else {
|
} else {
|
||||||
if (constant_force > 0x7f) {
|
if (pad->constant_force > 0x7f) {
|
||||||
gamepad_vibrate = 1;
|
pad->gamepad_vibrate = 1;
|
||||||
} else if (periodic_magnitude > 0x7f) {
|
} else if (pad->periodic_magnitude > 0x7f) {
|
||||||
gamepad_vibrate = 1;
|
pad->gamepad_vibrate = 1;
|
||||||
} else {
|
} 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);
|
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
|
if (report_id == 1) { // Joystick
|
||||||
// report_id = rq->wValue & 0xff
|
// report_id = rq->wValue & 0xff
|
||||||
// interface = rq->wIndex
|
// interface = rq->wIndex
|
||||||
*dat = gamepad_report0;
|
*dat = pad->gamepad_report0;
|
||||||
printf_P(PSTR("Get joy report\r\n"));
|
printf_P(PSTR("Get joy report\r\n"));
|
||||||
return REPORT_SIZE;
|
return USBPAD_REPORT_SIZE
|
||||||
|
;
|
||||||
} else if (report_id == 2) { // 2 : ES playing
|
} else if (report_id == 2) { // 2 : ES playing
|
||||||
hid_report_data[0] = report_id;
|
pad->hid_report_data[0] = report_id;
|
||||||
hid_report_data[1] = 0;
|
pad->hid_report_data[1] = 0;
|
||||||
hid_report_data[2] = _FFB_effect_index;
|
pad->hid_report_data[2] = pad->_FFB_effect_index;
|
||||||
printf_P(PSTR("ES playing\r\n"));
|
printf_P(PSTR("ES playing\r\n"));
|
||||||
*dat = hid_report_data;
|
*dat = pad->hid_report_data;
|
||||||
return 3;
|
return 3;
|
||||||
} else {
|
} else {
|
||||||
printf_P(PSTR("Get input report %d ??\r\n"), rq->wValue & 0xff);
|
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:
|
case HID_REPORT_TYPE_FEATURE:
|
||||||
if (report_id == PID_BLOCK_LOAD_REPORT) {
|
if (report_id == PID_BLOCK_LOAD_REPORT) {
|
||||||
hid_report_data[0] = report_id;
|
pad->hid_report_data[0] = report_id;
|
||||||
hid_report_data[1] = 0x1; // Effect block index
|
pad->hid_report_data[1] = 0x1; // Effect block index
|
||||||
hid_report_data[2] = 0x1; // (1: success, 2: oom, 3: load error)
|
pad->hid_report_data[2] = 0x1; // (1: success, 2: oom, 3: load error)
|
||||||
hid_report_data[3] = 10;
|
pad->hid_report_data[3] = 10;
|
||||||
hid_report_data[4] = 10;
|
pad->hid_report_data[4] = 10;
|
||||||
printf_P(PSTR("block load\r\n"));
|
printf_P(PSTR("block load\r\n"));
|
||||||
*dat = hid_report_data;
|
*dat = pad->hid_report_data;
|
||||||
return 5;
|
return 5;
|
||||||
}
|
}
|
||||||
else if (report_id == PID_SIMULTANEOUS_MAX) {
|
else if (report_id == PID_SIMULTANEOUS_MAX) {
|
||||||
hid_report_data[0] = report_id;
|
pad->hid_report_data[0] = report_id;
|
||||||
// ROM Effect Block count
|
// ROM Effect Block count
|
||||||
hid_report_data[1] = 0x1;
|
pad->hid_report_data[1] = 0x1;
|
||||||
hid_report_data[2] = 0x1;
|
pad->hid_report_data[2] = 0x1;
|
||||||
// PID pool move report?
|
// PID pool move report?
|
||||||
hid_report_data[3] = 0xff;
|
pad->hid_report_data[3] = 0xff;
|
||||||
hid_report_data[4] = 1;
|
pad->hid_report_data[4] = 1;
|
||||||
printf_P(PSTR("simultaneous max\r\n"));
|
printf_P(PSTR("simultaneous max\r\n"));
|
||||||
*dat = hid_report_data;
|
*dat = pad->hid_report_data;
|
||||||
return 5;
|
return 5;
|
||||||
}
|
}
|
||||||
else if (report_id == REPORT_CREATE_EFFECT) {
|
else if (report_id == REPORT_CREATE_EFFECT) {
|
||||||
hid_report_data[0] = report_id;
|
pad->hid_report_data[0] = report_id;
|
||||||
hid_report_data[1] = 1;
|
pad->hid_report_data[1] = 1;
|
||||||
printf_P(PSTR("create effect\r\n"));
|
printf_P(PSTR("create effect\r\n"));
|
||||||
*dat = hid_report_data;
|
*dat = pad->hid_report_data;
|
||||||
return 2;
|
return 2;
|
||||||
} else {
|
} else {
|
||||||
printf_P(PSTR("Unknown feature %d\r\n"), rq->wValue & 0xff);
|
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;
|
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) {
|
if (len < 1) {
|
||||||
printf_P(PSTR("shrt\n"));
|
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;
|
break;
|
||||||
case REPORT_DISABLE_ACTUATORS:
|
case REPORT_DISABLE_ACTUATORS:
|
||||||
printf_P(PSTR("disable actuators\r\n"));
|
printf_P(PSTR("disable actuators\r\n"));
|
||||||
periodic_magnitude = 0;
|
pad->periodic_magnitude = 0;
|
||||||
constant_force = 0;
|
pad->constant_force = 0;
|
||||||
vibration_on = 0;
|
pad->vibration_on = 0;
|
||||||
break;
|
break;
|
||||||
case REPORT_PID_POOL:
|
case REPORT_PID_POOL:
|
||||||
printf_P(PSTR("pid pool\r\n"));
|
printf_P(PSTR("pid pool\r\n"));
|
||||||
break;
|
break;
|
||||||
case REPORT_SET_EFFECT:
|
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]);
|
printf_P(PSTR("set effect %d\r\n"), data[1]);
|
||||||
break;
|
break;
|
||||||
case REPORT_SET_PERIODIC:
|
case REPORT_SET_PERIODIC:
|
||||||
periodic_magnitude = data[2];
|
pad->periodic_magnitude = data[2];
|
||||||
printf_P(PSTR("periodic mag: %d"), data[2]);
|
printf_P(PSTR("periodic mag: %d"), data[2]);
|
||||||
break;
|
break;
|
||||||
case REPORT_SET_CONSTANT_FORCE:
|
case REPORT_SET_CONSTANT_FORCE:
|
||||||
if (data[1] == 1) {
|
if (data[1] == 1) {
|
||||||
constant_force = data[2];
|
pad->constant_force = data[2];
|
||||||
printf_P(PSTR("Constant force %d\r\n"), data[2]);
|
printf_P(PSTR("Constant force %d\r\n"), data[2]);
|
||||||
}
|
}
|
||||||
break;
|
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 1 : bit 7=rom flag, bits 6-0=effect block index
|
||||||
* Byte 2 : Effect operation
|
* Byte 2 : Effect operation
|
||||||
* Byte 3 : Loop count */
|
* 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);
|
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:
|
case EFFECT_OP_START:
|
||||||
printf_P(PSTR("Start\r\n"));
|
printf_P(PSTR("Start\r\n"));
|
||||||
vibration_on = 1;
|
pad->vibration_on = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EFFECT_OP_START_SOLO:
|
case EFFECT_OP_START_SOLO:
|
||||||
printf_P(PSTR("Start solo\r\n"));
|
printf_P(PSTR("Start solo\r\n"));
|
||||||
vibration_on = 1;
|
pad->vibration_on = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EFFECT_OP_STOP:
|
case EFFECT_OP_STOP:
|
||||||
printf_P(PSTR("Stop\r\n"));
|
printf_P(PSTR("Stop\r\n"));
|
||||||
vibration_on = 0;
|
pad->vibration_on = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -424,7 +414,7 @@ uint8_t usbpad_hid_set_report(const struct usb_request *rq, const uint8_t *data,
|
|||||||
switch(data[0])
|
switch(data[0])
|
||||||
{
|
{
|
||||||
case REPORT_CREATE_EFFECT:
|
case REPORT_CREATE_EFFECT:
|
||||||
_FFB_effect_index = data[1];
|
pad->_FFB_effect_index = data[1];
|
||||||
printf_P(PSTR("create effect %d\n"), data[1]);
|
printf_P(PSTR("create effect %d\n"), data[1]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
30
usbpad.h
30
usbpad.h
@ -4,16 +4,32 @@
|
|||||||
#include "gamepads.h"
|
#include "gamepads.h"
|
||||||
#include "usb.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);
|
int usbpad_getReportSize(void);
|
||||||
unsigned char *usbpad_getReportBuffer(void);
|
unsigned char *usbpad_getReportBuffer(struct usbpad *pad);
|
||||||
|
|
||||||
void usbpad_update(const gamepad_data *pad_data);
|
void usbpad_update(struct usbpad *pad, const gamepad_data *pad_data);
|
||||||
char usbpad_mustVibrate(void);
|
char usbpad_mustVibrate(struct usbpad *pad);
|
||||||
void usbpad_forceVibrate(char force);
|
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);
|
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 usb_request *rq, const uint8_t **dat);
|
uint16_t usbpad_hid_get_report(struct usbpad *pad, struct usb_request *rq, const uint8_t **dat);
|
||||||
|
|
||||||
// For mappings. ID starts at 0.
|
// For mappings. ID starts at 0.
|
||||||
#define USB_BTN(id) (0x0001 << (id))
|
#define USB_BTN(id) (0x0001 << (id))
|
||||||
|
Loading…
Reference in New Issue
Block a user