Move more logic from main to usbpad, implement serial from eeprom

This commit is contained in:
Raphael Assenat 2015-10-17 17:21:22 -04:00
parent 5818aa2175
commit 54196a0d76
6 changed files with 301 additions and 266 deletions

View File

@ -1 +1 @@
OBJS=main.o usb.o usbpad.o mappings.o gcn64_protocol.o n64.o gamecube.o usart1.o bootloader.o eeprom.o config.o hiddata.o OBJS=main.o usb.o usbpad.o mappings.o gcn64_protocol.o n64.o gamecube.o usart1.o bootloader.o eeprom.o config.o hiddata.o usbstrings.o

282
main.c
View File

@ -17,21 +17,13 @@
#include "usbpad.h" #include "usbpad.h"
#include "eeprom.h" #include "eeprom.h"
#include "hiddata.h" #include "hiddata.h"
#include "usbstrings.h"
uint16_t hid_get_report_main(struct usb_request *rq, const uint8_t **dat); /* Those .c files are included rather than linked for we
uint8_t hid_set_report_main(const struct usb_request *rq, const uint8_t *dat, uint16_t len); * want the sizeof() operator to work on the arrays */
uint16_t hid_get_report_data(struct usb_request *rq, const uint8_t **dat);
uint8_t hid_set_report_data(const struct usb_request *rq, const uint8_t *dat, uint16_t len);
#include "reportdesc.c" #include "reportdesc.c"
#include "dataHidReport.c" #include "dataHidReport.c"
const wchar_t *const g_usb_strings[] = {
[0] = L"raphnet technologies", // 1 : Vendor
[1] = L"GC/N64 to USB v3.0", // 2: Product
[2] = L"123456", // 3 : Serial
};
struct cfg0 { struct cfg0 {
struct usb_configuration_descriptor configdesc; struct usb_configuration_descriptor configdesc;
@ -141,7 +133,7 @@ static struct usb_parameters usb_params = {
.devdesc = (PGM_VOID_P)&device_descriptor, .devdesc = (PGM_VOID_P)&device_descriptor,
.configdesc = (PGM_VOID_P)&cfg0, .configdesc = (PGM_VOID_P)&cfg0,
.configdesc_ttllen = sizeof(cfg0), .configdesc_ttllen = sizeof(cfg0),
.num_strings = ARRAY_SIZE(g_usb_strings), .num_strings = NUM_USB_STRINGS,
.strings = g_usb_strings, .strings = g_usb_strings,
.n_hid_interfaces = 2, .n_hid_interfaces = 2,
@ -149,8 +141,8 @@ static struct usb_parameters usb_params = {
[0] = { [0] = {
.reportdesc = gcn64_usbHidReportDescriptor, .reportdesc = gcn64_usbHidReportDescriptor,
.reportdesc_len = sizeof(gcn64_usbHidReportDescriptor), .reportdesc_len = sizeof(gcn64_usbHidReportDescriptor),
.getReport = hid_get_report_main, .getReport = usbpad_hid_get_report,
.setReport = hid_set_report_main, .setReport = usbpad_hid_set_report,
}, },
[1] = { [1] = {
.reportdesc = dataHidReport, .reportdesc = dataHidReport,
@ -213,240 +205,6 @@ void hwinit(void)
PRR1 = 0; PRR1 = 0;
} }
static unsigned char hid_report_data[32];
static unsigned char gamepad_report0[32];
// Output Report IDs for various functions
#define REPORT_SET_EFFECT 0x01
#define REPORT_SET_STATUS 0x02
#define REPORT_SET_PERIODIC 0x04
#define REPORT_SET_CONSTANT_FORCE 0x05
#define REPORT_EFFECT_OPERATION 0x0A
#define REPORT_EFFECT_BLOCK_IDX 0x0B
#define REPORT_DISABLE_ACTUATORS 0x0C
#define REPORT_PID_POOL 0x0D
// Feature reports
#define REPORT_CREATE_EFFECT 0x09
// For the 'Usage Effect Operation' report
#define EFFECT_OP_START 1
#define EFFECT_OP_START_SOLO 2
#define EFFECT_OP_STOP 3
// Feature report
#define PID_SIMULTANEOUS_MAX 3
#define PID_BLOCK_LOAD_REPORT 2
static volatile unsigned char gamepad_vibrate = 0; // output
static unsigned char vibration_on = 0;
static unsigned char constant_force = 0;
static unsigned char magnitude = 0;
static unsigned char _FFB_effect_index;
#define LOOP_MAX 0xFFFF
static unsigned int _loop_count;
static void decideVibration(void)
{
if (!vibration_on) {
gamepad_vibrate = 0;
} else {
if (constant_force > 0x7f) {
gamepad_vibrate = 1;
}
if (magnitude > 0x7f) {
gamepad_vibrate = 1;
}
}
}
uint16_t hid_get_report_main(struct usb_request *rq, const uint8_t **dat)
{
uint8_t report_id = (rq->wValue & 0xff);
// USB HID 1.11 section 7.2.1 Get_Report
// wValue high byte : report type
// wValue low byte : report id
// wIndex Interface
switch (rq->wValue >> 8)
{
case HID_REPORT_TYPE_INPUT:
{
if (report_id == 1) { // Joystick
// report_id = rq->wValue & 0xff
// interface = rq->wIndex
*dat = gamepad_report0;
printf_P(PSTR("Get joy report\r\n"));
return 9;
} 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;
printf_P(PSTR("ES playing\r\n"));
*dat = hid_report_data;
return 3;
} else {
printf_P(PSTR("Get input report %d ??\r\n"), rq->wValue & 0xff);
}
}
break;
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;
printf_P(PSTR("block load\r\n"));
*dat = hid_report_data;
return 5;
}
else if (report_id == PID_SIMULTANEOUS_MAX) {
hid_report_data[0] = report_id;
// ROM Effect Block count
hid_report_data[1] = 0x1;
hid_report_data[2] = 0x1;
// PID pool move report?
hid_report_data[3] = 0xff;
hid_report_data[4] = 1;
printf_P(PSTR("simultaneous max\r\n"));
*dat = hid_report_data;
return 5;
}
else if (report_id == REPORT_CREATE_EFFECT) {
hid_report_data[0] = report_id;
hid_report_data[1] = 1;
printf_P(PSTR("create effect\r\n"));
*dat = hid_report_data;
return 2;
} else {
printf_P(PSTR("Unknown feature %d\r\n"), rq->wValue & 0xff);
}
break;
}
printf_P(PSTR("Unhandled hid get report type=0x%02x, rq=0x%02x, wVal=0x%04x, wLen=0x%04x\r\n"), rq->bmRequestType, rq->bRequest, rq->wValue, rq->wLength);
return 0;
}
uint8_t hid_set_report_main(const struct usb_request *rq, const uint8_t *data, uint16_t len)
{
if (len < 1) {
printf_P(PSTR("shrt\n"));
return -1;
}
if ((rq->wValue >> 8) == HID_REPORT_TYPE_OUTPUT) {
switch(data[0])
{
case REPORT_SET_STATUS:
printf_P(PSTR("eff. set stat 0x%02x 0x%02x\r\n"),data[1],data[2]);
break;
case REPORT_EFFECT_BLOCK_IDX:
printf_P(PSTR("eff. blk. idx %d\r\n"), data[1]);
break;
case REPORT_DISABLE_ACTUATORS:
printf_P(PSTR("disable actuators\r\n"));
break;
case REPORT_PID_POOL:
printf_P(PSTR("pid pool\r\n"));
break;
case REPORT_SET_EFFECT:
_FFB_effect_index = data[1];
printf_P(PSTR("set effect %d\n"), data[1]);
break;
case REPORT_SET_PERIODIC:
magnitude = data[2];
// decideVibration();
printf_P(PSTR("periodic mag: %d"), data[2]);
break;
case REPORT_SET_CONSTANT_FORCE:
if (data[1] == 1) {
constant_force = data[2];
decideVibration();
printf_P(PSTR("Constant force"));
}
break;
case REPORT_EFFECT_OPERATION:
if (len != 4)
return -1;
/* Byte 0 : report ID
* 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;
printf_P(PSTR("EFFECT OP: rom=%s, idx=0x%02x"), data[1] & 0x80 ? "Yes":"No", data[1] & 0x7F);
switch(data[1] & 0x7F) // Effect block index
{
case 1: // constant force
case 3: // square
case 4: // sine
switch (data[2]) // effect operation
{
case EFFECT_OP_START:
printf_P(PSTR("Start\r\n"));
vibration_on = 1;
decideVibration();
break;
case EFFECT_OP_START_SOLO:
printf_P(PSTR("Start solo\r\n"));
vibration_on = 1;
decideVibration();
break;
case EFFECT_OP_STOP:
printf_P(PSTR("Stop\r\n"));
vibration_on = 0;
decideVibration();
break;
}
break;
// TODO : should probably drop these from the descriptor since they are
case 2: // ramp
case 5: // triangle
case 6: // sawtooth up
case 7: // sawtooth down
case 8: // spring
case 9: // damper
case 10: // inertia
case 11: // friction
case 12: // custom force data
printf_P(PSTR("Ununsed effect %d\n"), data[1] & 0x7F);
break;
}
break;
default:
printf_P(PSTR("Set output report 0x%02x\r\n"), data[0]);
}
}
else if ((rq->wValue >> 8) == HID_REPORT_TYPE_FEATURE) {
switch(data[0])
{
case REPORT_CREATE_EFFECT:
_FFB_effect_index = data[1];
printf_P(PSTR("create effect %d\n"), data[1]);
break;
default:
printf_P(PSTR("What?\n"));
}
}
else {
printf_P(PSTR("impossible\n"));
}
return 0;
}
#define NUM_PAD_TYPES 2 #define NUM_PAD_TYPES 2
@ -475,7 +233,14 @@ Gamepad *detectPad(void)
void eeprom_app_ready(void) void eeprom_app_ready(void)
{ {
// TODO : Set serial number from configured value static wchar_t serial_from_eeprom[SERIAL_NUM_LEN+1];
int i;
for (i=0; i<SERIAL_NUM_LEN; i++) {
serial_from_eeprom[i] = g_eeprom_data.cfg.serial[i];
}
serial_from_eeprom[i] = 0;
g_usb_strings[USB_STRING_SERIAL_IDX] = serial_from_eeprom;
} }
char g_polling_suspended = 0; char g_polling_suspended = 0;
@ -492,13 +257,14 @@ int main(void)
{ {
Gamepad *pad = NULL; Gamepad *pad = NULL;
gamepad_data pad_data; gamepad_data pad_data;
unsigned char gamepad_vibrate = 0;
hwinit(); hwinit();
usart1_init(); usart1_init();
eeprom_init(); eeprom_init();
/* Init the buffer with idle data */ /* Init the buffer with idle data */
usbpad_buildReport(NULL, gamepad_report0); usbpad_update(NULL);
sei(); sei();
usb_init(&usb_params); usb_init(&usb_params);
@ -524,8 +290,7 @@ int main(void)
pollDelay(); pollDelay();
} }
decideVibration(); gamepad_vibrate = usbpad_mustVibrate();
if (last_v != gamepad_vibrate) { if (last_v != gamepad_vibrate) {
if (pad && pad->setVibration) { if (pad && pad->setVibration) {
pad->setVibration(gamepad_vibrate); pad->setVibration(gamepad_vibrate);
@ -537,17 +302,16 @@ int main(void)
pad->update(); pad->update();
if (pad->changed()) { if (pad->changed()) {
int report_size;
pad->getReport(&pad_data); pad->getReport(&pad_data);
usbpad_buildReport(&pad_data, gamepad_report0); usbpad_update(&pad_data);
report_size = usbpad_getReportSize();
usb_interruptSend(gamepad_report0, report_size); usb_interruptSend(usbpad_getReportBuffer(), usbpad_getReportSize());
} }
} else { } else {
/* Just make sure gamepad_report0 holds valid and /* Just make sure the gamepad state holds valid data
* inactive data for the HID GET_REPORT request */ * to appear inactive (no buttons and axes in neutral) */
usbpad_buildReport(NULL, gamepad_report0); usbpad_update(NULL);
} }
} }

253
usbpad.c
View File

@ -1,4 +1,6 @@
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include "usb.h"
#include "gamepads.h" #include "gamepads.h"
#include "usbpad.h" #include "usbpad.h"
#include "mappings.h" #include "mappings.h"
@ -6,10 +8,44 @@
#define REPORT_ID 1 #define REPORT_ID 1
#define REPORT_SIZE 15 #define REPORT_SIZE 15
// Output Report IDs for various functions
#define REPORT_SET_EFFECT 0x01
#define REPORT_SET_STATUS 0x02
#define REPORT_SET_PERIODIC 0x04
#define REPORT_SET_CONSTANT_FORCE 0x05
#define REPORT_EFFECT_OPERATION 0x0A
#define REPORT_EFFECT_BLOCK_IDX 0x0B
#define REPORT_DISABLE_ACTUATORS 0x0C
#define REPORT_PID_POOL 0x0D
// Feature reports
#define REPORT_CREATE_EFFECT 0x09
// For the 'Usage Effect Operation' report
#define EFFECT_OP_START 1
#define EFFECT_OP_START_SOLO 2
#define EFFECT_OP_STOP 3
// Feature report
#define PID_SIMULTANEOUS_MAX 3
#define PID_BLOCK_LOAD_REPORT 2
void usbpad_init(void)
static volatile unsigned char gamepad_vibrate = 0; // output
static unsigned char vibration_on = 0;
static unsigned char constant_force = 0;
static unsigned char magnitude = 0;
static unsigned char _FFB_effect_index;
#define LOOP_MAX 0xFFFF
static unsigned int _loop_count;
static unsigned char gamepad_report0[32];
static unsigned char hid_report_data[32];
void usbpad_init()
{ {
} }
@ -132,22 +168,22 @@ static void buildReportFromN64(const n64_pad_data *n64_data, unsigned char dstbu
btnsToReport(buttons, dstbuf+13); btnsToReport(buttons, dstbuf+13);
} }
void usbpad_buildReport(const gamepad_data *pad_data, unsigned char dstbuf[REPORT_SIZE]) void usbpad_update(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(dstbuf); buildIdleReport(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, dstbuf); buildReportFromN64(&pad_data->n64, gamepad_report0);
break; break;
case PAD_TYPE_GAMECUBE: case PAD_TYPE_GAMECUBE:
buildReportFromGC(&pad_data->gc, dstbuf); buildReportFromGC(&pad_data->gc, gamepad_report0);
break; break;
default: default:
@ -156,3 +192,210 @@ void usbpad_buildReport(const gamepad_data *pad_data, unsigned char dstbuf[REPOR
} }
} }
char usbpad_mustVibrate(void)
{
if (!vibration_on) {
gamepad_vibrate = 0;
} else {
if (constant_force > 0x7f) {
gamepad_vibrate = 1;
}
if (magnitude > 0x7f) {
gamepad_vibrate = 1;
}
}
return gamepad_vibrate;
}
unsigned char *usbpad_getReportBuffer(void)
{
return gamepad_report0;
}
uint16_t usbpad_hid_get_report(struct usb_request *rq, const uint8_t **dat)
{
uint8_t report_id = (rq->wValue & 0xff);
// USB HID 1.11 section 7.2.1 Get_Report
// wValue high byte : report type
// wValue low byte : report id
// wIndex Interface
switch (rq->wValue >> 8)
{
case HID_REPORT_TYPE_INPUT:
{
if (report_id == 1) { // Joystick
// report_id = rq->wValue & 0xff
// interface = rq->wIndex
*dat = gamepad_report0;
printf_P(PSTR("Get joy report\r\n"));
return 9;
} 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;
printf_P(PSTR("ES playing\r\n"));
*dat = hid_report_data;
return 3;
} else {
printf_P(PSTR("Get input report %d ??\r\n"), rq->wValue & 0xff);
}
}
break;
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;
printf_P(PSTR("block load\r\n"));
*dat = hid_report_data;
return 5;
}
else if (report_id == PID_SIMULTANEOUS_MAX) {
hid_report_data[0] = report_id;
// ROM Effect Block count
hid_report_data[1] = 0x1;
hid_report_data[2] = 0x1;
// PID pool move report?
hid_report_data[3] = 0xff;
hid_report_data[4] = 1;
printf_P(PSTR("simultaneous max\r\n"));
*dat = hid_report_data;
return 5;
}
else if (report_id == REPORT_CREATE_EFFECT) {
hid_report_data[0] = report_id;
hid_report_data[1] = 1;
printf_P(PSTR("create effect\r\n"));
*dat = hid_report_data;
return 2;
} else {
printf_P(PSTR("Unknown feature %d\r\n"), rq->wValue & 0xff);
}
break;
}
printf_P(PSTR("Unhandled hid get report type=0x%02x, rq=0x%02x, wVal=0x%04x, wLen=0x%04x\r\n"), rq->bmRequestType, rq->bRequest, rq->wValue, rq->wLength);
return 0;
}
uint8_t usbpad_hid_set_report(const struct usb_request *rq, const uint8_t *data, uint16_t len)
{
if (len < 1) {
printf_P(PSTR("shrt\n"));
return -1;
}
if ((rq->wValue >> 8) == HID_REPORT_TYPE_OUTPUT) {
switch(data[0])
{
case REPORT_SET_STATUS:
printf_P(PSTR("eff. set stat 0x%02x 0x%02x\r\n"),data[1],data[2]);
break;
case REPORT_EFFECT_BLOCK_IDX:
printf_P(PSTR("eff. blk. idx %d\r\n"), data[1]);
break;
case REPORT_DISABLE_ACTUATORS:
printf_P(PSTR("disable actuators\r\n"));
break;
case REPORT_PID_POOL:
printf_P(PSTR("pid pool\r\n"));
break;
case REPORT_SET_EFFECT:
_FFB_effect_index = data[1];
printf_P(PSTR("set effect %d\n"), data[1]);
break;
case REPORT_SET_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];
//decideVibration();
printf_P(PSTR("Constant force"));
}
break;
case REPORT_EFFECT_OPERATION:
if (len != 4)
return -1;
/* Byte 0 : report ID
* 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;
printf_P(PSTR("EFFECT OP: rom=%s, idx=0x%02x"), data[1] & 0x80 ? "Yes":"No", data[1] & 0x7F);
switch(data[1] & 0x7F) // Effect block index
{
case 1: // constant force
case 3: // square
case 4: // sine
switch (data[2]) // effect operation
{
case EFFECT_OP_START:
printf_P(PSTR("Start\r\n"));
vibration_on = 1;
// decideVibration();
break;
case EFFECT_OP_START_SOLO:
printf_P(PSTR("Start solo\r\n"));
vibration_on = 1;
// decideVibration();
break;
case EFFECT_OP_STOP:
printf_P(PSTR("Stop\r\n"));
vibration_on = 0;
// decideVibration();
break;
}
break;
// TODO : should probably drop these from the descriptor since they are
case 2: // ramp
case 5: // triangle
case 6: // sawtooth up
case 7: // sawtooth down
case 8: // spring
case 9: // damper
case 10: // inertia
case 11: // friction
case 12: // custom force data
printf_P(PSTR("Ununsed effect %d\n"), data[1] & 0x7F);
break;
}
break;
default:
printf_P(PSTR("Set output report 0x%02x\r\n"), data[0]);
}
}
else if ((rq->wValue >> 8) == HID_REPORT_TYPE_FEATURE) {
switch(data[0])
{
case REPORT_CREATE_EFFECT:
_FFB_effect_index = data[1];
printf_P(PSTR("create effect %d\n"), data[1]);
break;
default:
printf_P(PSTR("What?\n"));
}
}
else {
printf_P(PSTR("impossible\n"));
}
return 0;
}

View File

@ -1,9 +1,17 @@
#ifndef USBPAD_H__ #ifndef USBPAD_H__
#define USBPAD_H__ #define USBPAD_H__
#include "usb.h"
void usbpad_init(void); void usbpad_init(void);
int usbpad_getReportSize(void); int usbpad_getReportSize(void);
void usbpad_buildReport(const gamepad_data *pad_data, unsigned char *dstbuf); unsigned char *usbpad_getReportBuffer(void);
void usbpad_update(const gamepad_data *pad_data);
char usbpad_mustVibrate(void);
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);
// For mappings. ID starts at 0. // For mappings. ID starts at 0.
#define USB_BTN(id) (0x0001 << (id)) #define USB_BTN(id) (0x0001 << (id))

9
usbstrings.c Normal file
View File

@ -0,0 +1,9 @@
#include <stdlib.h> // for wchar_t
#include "usbstrings.h"
const wchar_t *g_usb_strings[] = {
[0] = L"raphnet technologies", // 1 : Vendor
[1] = L"GC/N64 to USB v3.0", // 2: Product
[2] = L"123456", // 3 : Serial
};

11
usbstrings.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef _usbstrings_h__
#define _usbstrings_h__
extern const wchar_t *g_usb_strings[];
#define NUM_USB_STRINGS 3
/* Array indexes (i.e. zero-based0 */
#define USB_STRING_SERIAL_IDX 2
#endif