/* gc_n64_usb : Gamecube or N64 controller to USB firmware Copyright (C) 2007-2016 Raphael Assenat 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 the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include "requests.h" #include "config.h" #include "hiddata.h" #include "usbpad.h" #include "bootloader.h" #include "gcn64_protocol.h" #include "version.h" #include "main.h" // dataHidReport is 63 bytes. Endpoint is 64 bytes. #define CMDBUF_SIZE 64 #define STATE_IDLE 0 #define STATE_NEW_COMMAND 1 // New command in buffer #define STATE_COMMAND_DONE 2 // Result in buffer //#define DEBUG extern char g_polling_suspended; static volatile uint8_t state = STATE_IDLE; 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(void *ctx, struct usb_request *rq, const uint8_t **dat) { // printf("Get data\n"); if (state == STATE_COMMAND_DONE) { *dat = cmdbuf; state = STATE_IDLE; #ifdef DEBUG printf_P(PSTR("hiddata idle, sent %d bytes\r\n"), cmdbuf_len); #endif return cmdbuf_len; } return 0; } /*** Get/Set report called from interrupt context! */ uint8_t hiddata_set_report(void *ctx, const struct usb_request *rq, const uint8_t *dat, uint16_t len) { #ifdef DEBUG int i; printf_P(PSTR("Set data %d\n"), len); for (i=0; i= CMDBUF_SIZE) break; chn = requestCopy[i]; if (chn == 0xff) break; i++; n_tx = requestCopy[i]; i++; n_rx = requestCopy[i]; i++; if (n_tx == 0) continue; if (rx_offset + 1 + n_rx >= CMDBUF_SIZE) { break; } rx = gcn64_transaction(chn, requestCopy + i, n_tx, cmdbuf + rx_offset + 1, n_rx); cmdbuf[rx_offset] = n_rx; if (rx <= 0) { // timeout cmdbuf[rx_offset] |= 0x80; } else if (rx < n_rx) { // less than expected cmdbuf[rx_offset] |= 0x40; } rx_offset += n_rx + 1; i += n_tx; } return 63; } static void hiddata_processCommandBuffer(struct hiddata_ops *ops) { unsigned char channel; #ifdef DEBUG int i; #endif if (cmdbuf_len < 1) { state = STATE_IDLE; return; } // printf("Process cmd 0x%02x\r\n", cmdbuf[0]); switch(cmdbuf[0]) { case RQ_GCN64_ECHO: // Cmd : RQ, data[] // Answer: RQ, data[] break; case RQ_GCN64_JUMP_TO_BOOTLOADER: enterBootLoader(); break; case RQ_RNT_RESET_FIRMWARE: resetFirmware(); break; case RQ_GCN64_RAW_SI_COMMAND: // TODO : Range checking // cmdbuf[] : RQ, CHN, LEN, data[] channel = cmdbuf[1]; if (channel >= NUM_CHANNELS) break; cmdbuf_len = gcn64_transaction(channel, cmdbuf+3, cmdbuf[2], cmdbuf + 3, CMDBUF_SIZE-3); cmdbuf[2] = cmdbuf_len; cmdbuf_len += 3; // Answer: RQ, CHN, LEN, data[] break; case RQ_GCN64_GET_CONFIG_PARAM: // Cmd : RQ, PARAM // Answer: RQ, PARAM, data[] cmdbuf_len = config_getParam(cmdbuf[1], cmdbuf + 2, CMDBUF_SIZE-2); cmdbuf_len += 2; // Datalen + RQ + PARAM break; case RQ_GCN64_SET_CONFIG_PARAM: // Cmd: RQ, PARAM, data[] config_setParam(cmdbuf[1], cmdbuf+2); // Answer: RQ, PARAM cmdbuf_len = 2; break; case RQ_GCN64_SUSPEND_POLLING: // CMD: RQ, PARAM if (ops && ops->suspendPolling) { ops->suspendPolling(cmdbuf[1]); } break; case RQ_GCN64_GET_VERSION: // CMD: RQ // Answer: RQ, version string (zero-terminated) strcpy((char*)(cmdbuf + 1), g_version); cmdbuf_len = 1 + strlen(g_version) + 1; break; case RQ_GCN64_GET_SIGNATURE: strcpy_P((char*)(cmdbuf + 1), g_signature); cmdbuf_len = 1 + strlen_P(g_signature) + 1; break; case RQ_GCN64_GET_CONTROLLER_TYPE: // CMD : RQ, CHN // Answer: RQ, CHN, TYPE channel = cmdbuf[1]; if (channel >= NUM_CHANNELS) break; cmdbuf[2] = current_pad_type[channel]; cmdbuf_len = 3; break; case RQ_GCN64_SET_VIBRATION: // CMD : RQ, CHN, Vibrate // Answer: RQ, CHN, Vibrate if (ops && ops->forceVibration) { ops->forceVibration(cmdbuf[1], cmdbuf[2]); } cmdbuf_len = 3; break; case RQ_GCN64_BLOCK_IO: cmdbuf_len = processBlockIO(); break; case RQ_RNT_GET_SUPPORTED_REQUESTS: cmdbuf[1] = RQ_GCN64_JUMP_TO_BOOTLOADER; cmdbuf[2] = RQ_GCN64_RAW_SI_COMMAND; cmdbuf[3] = RQ_GCN64_GET_CONFIG_PARAM; cmdbuf[4] = RQ_GCN64_SET_CONFIG_PARAM; cmdbuf[5] = RQ_GCN64_SUSPEND_POLLING; cmdbuf[6] = RQ_GCN64_GET_VERSION; cmdbuf[7] = RQ_GCN64_GET_SIGNATURE; cmdbuf[8] = RQ_GCN64_GET_CONTROLLER_TYPE; cmdbuf[9] = RQ_GCN64_SET_VIBRATION; cmdbuf[10] = RQ_GCN64_BLOCK_IO; cmdbuf[11] = RQ_RNT_GET_SUPPORTED_CFG_PARAMS; cmdbuf[12] = RQ_RNT_GET_SUPPORTED_MODES; cmdbuf[13] = RQ_RNT_GET_SUPPORTED_REQUESTS; cmdbuf_len = 14; break; case RQ_RNT_GET_SUPPORTED_CFG_PARAMS: cmdbuf_len = 1 + config_getSupportedParams(cmdbuf + 1); break; case RQ_RNT_GET_SUPPORTED_MODES: cmdbuf_len = 1; if (ops && ops->getSupportedModes) { cmdbuf_len += ops->getSupportedModes(cmdbuf + 1); } break; } #ifdef DEBUG printf("Pending data %d\n", cmdbuf_len); for (i=0; i