1
0
mirror of https://github.com/raphnet/gc_n64_usb-v3 synced 2024-12-21 06:48:52 -05:00

Channels for raw SI commands

Configurable poll interval
This commit is contained in:
Raphael Assenat 2015-09-09 21:41:41 -04:00
parent fd83e0ae7d
commit 0627841025
14 changed files with 266 additions and 59 deletions

View File

@ -10,10 +10,15 @@ struct eeprom_data_struct g_eeprom_data;
* values. */
void eeprom_app_write_defaults(void)
{
int i;
const char default_serial[SERIAL_NUM_LEN] = { '0','0','0','0','0','1' };
memcpy(g_eeprom_data.cfg.serial, default_serial, SERIAL_NUM_LEN);
g_eeprom_data.cfg.mode = CFG_MODE_STANDARD;
for (i=0; i<NUM_CHANNELS; i++) {
g_eeprom_data.cfg.poll_interval[i] = 5; // 5ms default
}
}
static void config_set_serial(char serial[SERIAL_NUM_LEN])
@ -32,6 +37,24 @@ unsigned char config_getParam(unsigned char param, unsigned char *value, unsigne
case CFG_PARAM_SERIAL:
memcpy(value, g_eeprom_data.cfg.serial, SERIAL_NUM_LEN);
return SERIAL_NUM_LEN;
case CFG_PARAM_POLL_INTERVAL0:
*value = g_eeprom_data.cfg.poll_interval[0];
return 1;
#if NUM_CHANNELS > 1
case CFG_PARAM_POLL_INTERVAL1:
*value = g_eeprom_data.cfg.poll_interval[1];
return 1;
#endif
#if NUM_CHANNELS > 2
case CFG_PARAM_POLL_INTERVAL2:
*value = g_eeprom_data.cfg.poll_interval[2];
return 1;
#endif
#if NUM_CHANNELS > 3
case CFG_PARAM_POLL_INTERVAL3:
*value = g_eeprom_data.cfg.poll_interval[3];
return 1;
#endif
}
return 0;
@ -50,6 +73,24 @@ unsigned char config_setParam(unsigned char param, const unsigned char *value)
case CFG_PARAM_SERIAL:
config_set_serial((char*)value);
return 1;
case CFG_PARAM_POLL_INTERVAL0:
g_eeprom_data.cfg.poll_interval[0] = value[0];
return 1;
#if NUM_CHANNELS > 1
case CFG_PARAM_POLL_INTERVAL1:
g_eeprom_data.cfg.poll_interval[1] = value[0];
return 1;
#endif
#if NUM_CHANNELS > 2
case CFG_PARAM_POLL_INTERVAL2:
g_eeprom_data.cfg.poll_interval[2] = value[0];
return 1;
#endif
#if NUM_CHANNELS > 3
case CFG_PARAM_POLL_INTERVAL3:
g_eeprom_data.cfg.poll_interval[3] = value[0];
return 1;
#endif
}
return 0;

View File

@ -1,10 +1,12 @@
#ifndef _config_h__
#define _config_h__
#define NUM_CHANNELS 4
#define SERIAL_NUM_LEN 6
struct eeprom_cfg {
uint8_t serial[SERIAL_NUM_LEN];
uint8_t mode;
uint8_t poll_interval[NUM_CHANNELS];
};
void eeprom_app_write_defaults(void);

View File

@ -38,9 +38,8 @@ 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)
{
int i;
#ifdef DEBUG
int i;
printf("Set data %d\n", len);
for (i=0; i<len; i++) {
printf("0x%02x ", dat[i]);
@ -57,8 +56,8 @@ uint8_t hiddata_set_report(const struct usb_request *rq, const uint8_t *dat, uin
static void hiddata_processCommandBuffer(void)
{
int i;
int bits;
unsigned char channel;
if (cmdbuf_len < 1) {
state = STATE_IDLE;
@ -73,11 +72,13 @@ static void hiddata_processCommandBuffer(void)
break;
case RQ_GCN64_RAW_SI_COMMAND:
// TODO : Range checking
// cmd : RQ, LEN, data[]
bits = gcn64_transaction(cmdbuf+2, cmdbuf[1]);
// cmdbuf[] : RQ, CHN, LEN, data[]
channel = cmdbuf[1];
bits = gcn64_transaction(cmdbuf+3, cmdbuf[2]);
cmdbuf_len = bits / 8; // The above return a number of bits
gcn64_protocol_getBytes(0, cmdbuf_len, cmdbuf + 2);
cmdbuf_len += 2; // Answer: RQ, LEN, data[]
gcn64_protocol_getBytes(0, cmdbuf_len, cmdbuf + 3);
cmdbuf[2] = cmdbuf_len;
cmdbuf_len += 3; // Answer: RQ, CHN, LEN, data[]
break;
case RQ_GCN64_GET_CONFIG_PARAM:
// Cmd : RQ, PARAM
@ -92,7 +93,8 @@ static void hiddata_processCommandBuffer(void)
cmdbuf_len = 2;
break;
case RQ_GCN64_SUSPEND_POLLING:
g_polling_suspended = 1;
// CMD: RQ, PARAM
g_polling_suspended = cmdbuf[1];
break;
}

13
main.c
View File

@ -480,6 +480,14 @@ void eeprom_app_ready(void)
char g_polling_suspended = 0;
void pollDelay(void)
{
int i;
for (i=0; i<g_eeprom_data.cfg.poll_interval[0]; i++) {
_delay_ms(1);
}
}
int main(void)
{
Gamepad *pad = NULL;
@ -512,7 +520,10 @@ int main(void)
// for polling the controller...
hiddata_doTask();
_delay_ms(5);
if (!g_polling_suspended) {
pollDelay();
}
decideVibration();
if (last_v != gamepad_vibrate) {

View File

@ -14,5 +14,10 @@
#define CFG_PARAM_SERIAL 0x01
#define CFG_PARAM_POLL_INTERVAL0 0x10
#define CFG_PARAM_POLL_INTERVAL1 0x11
#define CFG_PARAM_POLL_INTERVAL2 0x12
#define CFG_PARAM_POLL_INTERVAL3 0x13
#endif

View File

@ -8,7 +8,7 @@ PREFIX=/usr/local
PROG=gcn64ctl
OBJS=main.o gcn64.o mempak.o
OBJS=main.o gcn64.o mempak.o gcn64lib.o
.PHONY : clean install

View File

@ -35,6 +35,5 @@ int gcn64_poll_result(gcn64_hdl_t hdl, unsigned char *cmd, int cmdlen);
int gcn64_exchange(gcn64_hdl_t hdl, unsigned char *outcmd, int outlen, unsigned char *result, int result_max);
#endif // _gcn64_h__

BIN
tool/gcn64ctl Executable file

Binary file not shown.

77
tool/gcn64lib.c Normal file
View File

@ -0,0 +1,77 @@
#include <string.h>
#include "gcn64lib.h"
#include "../requests.h"
int gcn64lib_getConfig(gcn64_hdl_t hdl, unsigned char param, unsigned char *rx, unsigned char rx_max)
{
unsigned char cmd[2];
int n;
cmd[0] = RQ_GCN64_GET_CONFIG_PARAM;
cmd[1] = param;
n = gcn64_exchange(hdl, cmd, 2, rx, rx_max);
if (n<2)
return n;
n -= 2;
// Drop the leading CMD and PARAM
if (n) {
memmove(rx, rx+2, n);
}
return n;
}
int gcn64lib_setConfig(gcn64_hdl_t hdl, unsigned char param, unsigned char *data, unsigned char len)
{
unsigned char cmd[2 + len];
int n;
cmd[0] = RQ_GCN64_SET_CONFIG_PARAM;
cmd[1] = param;
memcpy(cmd + 2, data, len);
n = gcn64_exchange(hdl, cmd, 2 + len, cmd, sizeof(cmd));
if (n<0)
return n;
return 0;
}
int gcn64lib_suspendPolling(gcn64_hdl_t hdl, unsigned char suspend)
{
unsigned char cmd[2];
int n;
cmd[0] = RQ_GCN64_SUSPEND_POLLING;
cmd[1] = suspend;
n = gcn64_exchange(hdl, cmd, 2, cmd, sizeof(cmd));
if (n<0)
return n;
return 0;
}
int gcn64lib_rawSiCommand(gcn64_hdl_t hdl, unsigned char channel, unsigned char *tx, unsigned char tx_len, unsigned char *rx, unsigned char max_rx)
{
unsigned char cmd[3 + tx_len];
int cmdlen, rx_len, n;
cmd[0] = RQ_GCN64_RAW_SI_COMMAND;
cmd[1] = channel;
cmd[2] = tx_len;
memcpy(cmd+3, tx, tx_len);
cmdlen = 3 + tx_len;
n = gcn64_exchange(hdl, cmd, cmdlen, rx, max_rx);
if (n<0)
return n;
rx_len = rx[2];
memmove(rx, rx + 3, rx_len);
return rx_len;
}

11
tool/gcn64lib.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef _gcn64_lib_h__
#define _gcn64_lib_h__
#include "gcn64.h"
int gcn64lib_suspendPolling(gcn64_hdl_t hdl, unsigned char suspend);
int gcn64lib_setConfig(gcn64_hdl_t hdl, unsigned char param, unsigned char *data, unsigned char len);
int gcn64lib_getConfig(gcn64_hdl_t hdl, unsigned char param, unsigned char *rx, unsigned char rx_max);
int gcn64lib_rawSiCommand(gcn64_hdl_t hdl, unsigned char channel, unsigned char *tx, unsigned char tx_len, unsigned char *rx, unsigned char max_rx);
#endif // _gcn64_lib_h__

5
tool/gcn64utils.h Normal file
View File

@ -0,0 +1,5 @@
#ifndef _gcn64_utils_h__
#define _gcn64_utils_h__
#endif // _gcn64_utils_h__

View File

@ -26,6 +26,7 @@
#include "version.h"
#include "gcn64.h"
#include "gcn64lib.h"
#include "mempak.h"
#include "../requests.h"
#include "../gcn64_protocol.h"
@ -40,15 +41,18 @@ static void printUsage(void)
printf(" -l, --list List devices\n");
printf(" -s serial Operate on specified device (required unless -f is specified)\n");
printf(" -f, --force If no serial is specified, use first device detected.\n");
printf(" -o, --outfile Output file for read operations (eg: --n64-mempak-dump)\n");
printf(" -o, --outfile file Output file for read operations (eg: --n64-mempak-dump)\n");
printf("\n");
printf("Configuration commands:\n");
printf(" --set_serial serial Assign a new device serial number\n");
printf(" --get_serial Read serial from eeprom\n");
printf(" --set_poll_rate ms Set time between controller polls in milliseconds\n");
printf(" --get_poll_rate Read configured poll rate\n");
printf("\n");
printf("Advanced commands:\n");
printf(" --bootloader Re-enumerate in bootloader mode\n");
printf(" --suspend_polling Stop polling controller\n");
printf(" --suspend_polling Stop polling the controller\n");
printf(" --resume_polling Re-start polling the controller\n");
printf("\n");
printf("Raw controller commands:\n");
printf(" --n64_getstatus Read N64 controller status now\n");
@ -58,6 +62,17 @@ static void printUsage(void)
printf(" --n64_mempak_dump Dump N64 mempak contents (Use with --outfile to write to file)\n");
}
static void printHexBuf(unsigned char *buf, int n)
{
int i;
for (i=0; i<n; i++) {
printf("%02x ", buf[i]);
}
printf("\n");
}
#define OPT_OUTFILE 'o'
#define OPT_SET_SERIAL 257
#define OPT_GET_SERIAL 258
@ -68,6 +83,9 @@ static void printUsage(void)
#define OPT_N64_MEMPAK_DUMP 304
#define OPT_N64_GETCAPS 305
#define OPT_SUSPEND_POLLING 306
#define OPT_RESUME_POLLING 307
#define OPT_SET_POLL_INTERVAL 308
#define OPT_GET_POLL_INTERVAL 309
struct option longopts[] = {
{ "help", 0, NULL, 'h' },
@ -82,7 +100,10 @@ struct option longopts[] = {
{ "n64_getcaps", 0, NULL, OPT_N64_GETCAPS },
{ "n64_mempak_dump", 0, NULL, OPT_N64_MEMPAK_DUMP },
{ "suspend_polling", 0, NULL, OPT_SUSPEND_POLLING },
{ "resume_polling", 0, NULL, OPT_RESUME_POLLING },
{ "outfile", 1, NULL, OPT_OUTFILE },
{ "set_poll_rate", 1, NULL, OPT_SET_POLL_INTERVAL },
{ "get_poll_rate", 0, NULL, OPT_GET_POLL_INTERVAL },
{ },
};
@ -209,68 +230,83 @@ int main(int argc, char **argv)
unsigned char cmd[64] = { };
int n;
int cmdlen = 0;
int do_exchange = 0;
switch (opt)
{
case OPT_SET_POLL_INTERVAL:
cmd[0] = atoi(optarg);
printf("Setting poll interval to %d ms\n", cmd[0]);
gcn64lib_setConfig(hdl, CFG_PARAM_POLL_INTERVAL0, cmd, 1);
break;
case OPT_GET_POLL_INTERVAL:
n = gcn64lib_getConfig(hdl, CFG_PARAM_POLL_INTERVAL0, cmd, sizeof(cmd));
if (n == 1) {
printf("Poll interval: %d ms\n", cmd[0]);
}
break;
case OPT_SET_SERIAL:
printf("Setting serial...");
if (strlen(optarg) != 6) {
fprintf(stderr, "Serial number must be 6 characters\n");
return -1;
}
cmd[0] = RQ_GCN64_SET_CONFIG_PARAM;
cmd[1] = CFG_PARAM_SERIAL;
memcpy(cmd + 2, optarg, 6);
cmdlen = 8;
gcn64lib_setConfig(hdl, CFG_PARAM_SERIAL, (void*)optarg, 6);
break;
case OPT_GET_SERIAL:
cmd[0] = RQ_GCN64_GET_CONFIG_PARAM;
cmd[1] = CFG_PARAM_SERIAL;
cmdlen = 2;
n = gcn64lib_getConfig(hdl, CFG_PARAM_SERIAL, cmd, sizeof(cmd));
if (n==6) {
cmd[6] = 0;
printf("Serial: %s\n", cmd);
}
break;
case OPT_BOOTLOADER:
printf("Sending 'jump to bootloader' command...");
cmd[0] = RQ_GCN64_JUMP_TO_BOOTLOADER;
cmdlen = 1;
do_exchange = 1;
break;
case OPT_SUSPEND_POLLING:
cmd[0] = RQ_GCN64_SUSPEND_POLLING;
cmdlen = 1;
gcn64lib_suspendPolling(hdl, 1);
break;
case OPT_RESUME_POLLING:
gcn64lib_suspendPolling(hdl, 0);
break;
case OPT_N64_GETSTATUS:
cmd[0] = RQ_GCN64_RAW_SI_COMMAND;
cmd[1] = 0x01; // Length of SI command
cmd[2] = N64_GET_STATUS; // N64 GET status
cmdlen = 3;
break;
case OPT_GC_GETSTATUS:
cmd[0] = RQ_GCN64_RAW_SI_COMMAND;
cmd[1] = 0x03; // Length of SI command
cmd[2] = GC_GETSTATUS1;
cmd[3] = GC_GETSTATUS2;
cmd[4] = GC_GETSTATUS3(0);
cmdlen = 5;
cmd[0] = N64_GET_STATUS;
n = gcn64lib_rawSiCommand(hdl, 0, cmd, 1, cmd, sizeof(cmd));
if (n >= 0) {
printf("N64 Get status[%d]: ", n);
printHexBuf(cmd, n);
}
break;
case OPT_GC_GETSTATUS_RUMBLE:
cmd[0] = RQ_GCN64_RAW_SI_COMMAND;
cmd[1] = 0x03; // Length of SI command
cmd[2] = GC_GETSTATUS1;
cmd[3] = GC_GETSTATUS2;
cmd[4] = GC_GETSTATUS3(0);
cmdlen = 5;
case OPT_GC_GETSTATUS:
cmd[0] = GC_GETSTATUS1;
cmd[1] = GC_GETSTATUS2;
cmd[2] = GC_GETSTATUS3(opt == OPT_GC_GETSTATUS_RUMBLE);
n = gcn64lib_rawSiCommand(hdl, 0, cmd, 3, cmd, sizeof(cmd));
if (n >= 0) {
printf("GC Get status[%d]: ", n);
printHexBuf(cmd, n);
}
break;
case OPT_N64_GETCAPS:
cmd[0] = RQ_GCN64_RAW_SI_COMMAND;
cmd[1] = 0x01; // Length of SI command
cmd[2] = N64_GET_CAPABILITIES; // N64 GET status
cmdlen = 3;
cmd[0] = N64_GET_CAPABILITIES;
n = gcn64lib_rawSiCommand(hdl, 0, cmd, 1, cmd, sizeof(cmd));
if (n >= 0) {
printf("N64 Get caps[%d]: ", n);
printHexBuf(cmd, n);
}
break;
case OPT_N64_MEMPAK_DUMP:
@ -282,7 +318,7 @@ int main(int argc, char **argv)
break;
}
if (cmd[0]) {
if (do_exchange) {
int i;
n = gcn64_exchange(hdl, cmd, cmdlen, cmd, sizeof(cmd));
if (n<0)

View File

@ -3,6 +3,7 @@
#include <ctype.h>
#include <stdint.h>
#include "gcn64.h"
#include "gcn64lib.h"
#include "mempak.h"
#include "../gcn64_protocol.h"
#include "../requests.h"
@ -122,30 +123,42 @@ int mempak_writeBlock(gcn64_hdl_t hdl, unsigned short addr, unsigned char data[3
int mempak_readBlock(gcn64_hdl_t hdl, unsigned short addr, unsigned char dst[32])
{
unsigned char cmd[64];
int cmdlen;
//int cmdlen;
int n;
uint16_t addr_crc;
unsigned char crc;
addr_crc = __calc_address_crc(addr);
cmd[0] = N64_EXPANSION_READ;
cmd[1] = addr_crc>>8; // Address high byte
cmd[2] = addr_crc&0xff; // Address low byte
/*
cmd[0] = RQ_GCN64_RAW_SI_COMMAND;
cmd[1] = 3;
cmd[2] = N64_EXPANSION_READ;
cmd[3] = addr_crc>>8; // Address high byte
cmd[4] = addr_crc&0xff; // Address low byte
cmdlen = 5;
*/
//printf("Addr 0x%04x with crc -> 0x%04x\n", addr, addr_crc);
n = gcn64_exchange(hdl, cmd, cmdlen, cmd, sizeof(cmd));
if (n != 35)
n = gcn64lib_rawSiCommand(hdl, 0, cmd, 3, cmd, sizeof(cmd));
if (n != 33) {
printf("Hey! %d\n", n);
return -1;
}
memcpy(dst, cmd + 2, 0x20);
// n = gcn64_exchange(hdl, cmd, cmdlen, cmd, sizeof(cmd));
// if (n != 35)
// return -1;
//memcpy(dst, cmd + 2, 0x20);
memcpy(dst, cmd, 0x20);
crc = __calc_data_crc(dst);
if (crc != cmd[34]) {
if (crc != cmd[32]) {
fprintf(stderr, "Bad CRC reading address 0x%04x\n", addr);
return -1;
}
@ -184,7 +197,7 @@ int mempak_readAll(gcn64_hdl_t hdl, unsigned char dstbuf[0x8000])
return 0;
}
void mempak_dump(gcn64_hdl_t hdl)
int mempak_dump(gcn64_hdl_t hdl)
{
unsigned char cardbuf[0x8000];
int i,j;
@ -193,7 +206,10 @@ void mempak_dump(gcn64_hdl_t hdl)
mempak_init(hdl);
printf("Reading card...\n");
mempak_readAll(hdl, cardbuf);
i = mempak_readAll(hdl, cardbuf);
if (i<0) {
return i;
}
for (i=0; i<DUMP_SIZE; i+=0x20) {
printf("%04x: ", i);
@ -207,6 +223,8 @@ void mempak_dump(gcn64_hdl_t hdl)
}
printf("\n");
}
return 0;
}
#define NUM_COPIES 4

View File

@ -1,5 +1,5 @@
void mempak_dump(gcn64_hdl_t hdl);
int mempak_dump(gcn64_hdl_t hdl);
int mempak_readBlock(gcn64_hdl_t hdl, unsigned short addr, unsigned char dst[32]);
int mempak_dumpToFile(gcn64_hdl_t hdl, const char *out_filename);