/* gc_n64_usb : Gamecube or N64 controller to USB firmware Copyright (C) 2007-2021 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 #include #include "gamepads.h" #include "n64.h" #include "gcn64_protocol.h" #include "eeprom.h" #include "main.h" // for num_players #undef BUTTON_A_RUMBLE_TEST /*********** prototypes *************/ static void n64Init(unsigned char chn); static char n64Update(unsigned char chn); static char n64Changed(unsigned char chn); static void n64GetReport(unsigned char chn, gamepad_data *dst); static void n64SetVibration(unsigned char chn, char enable); static char must_rumble[GAMEPAD_MAX_CHANNELS] = { }; #ifdef BUTTON_A_RUMBLE_TEST static char force_rumble[GAMEPAD_MAX_CHANNELS] = { }; #endif static unsigned char n64_rumble_state[GAMEPAD_MAX_CHANNELS] = { }; unsigned char tmpdata[40]; // Shared between channels #define RSTATE_UNAVAILABLE 0 #define RSTATE_OFF 1 #define RSTATE_TURNON 2 #define RSTATE_ON 3 #define RSTATE_TURNOFF 4 #define RSTATE_INIT 5 static void n64Init(unsigned char chn) { n64Update(chn); } static char initRumble(unsigned char chn) { int count; unsigned char data[4]; tmpdata[0] = N64_EXPANSION_WRITE; tmpdata[1] = 0x80; tmpdata[2] = 0x01; memset(tmpdata+3, 0x80, 32); count = gcn64_transaction(chn, tmpdata, 35, data, sizeof(data)); if (count == 1) return 0; return -1; } static char controlRumble(unsigned char chn, char enable) { int count; unsigned char data[4]; tmpdata[0] = N64_EXPANSION_WRITE; tmpdata[1] = 0xc0; tmpdata[2] = 0x1b; memset(tmpdata+3, enable ? 0x01 : 0x00, 32); count = gcn64_transaction(chn, tmpdata, 35, data, sizeof(data)); if (count == 1) return 0; return -1; } static char n64Update(unsigned char chn) { unsigned char count; unsigned char x,y; unsigned char btns1, btns2; unsigned char caps[N64_CAPS_REPLY_LENGTH]; unsigned char status[N64_GET_STATUS_REPLY_LENGTH]; /* Pad answer to N64_GET_CAPABILITIES * * 0x050000 : 0000 0101 0000 0000 0000 0000 : No expansion pack * 0x050001 : 0000 0101 0000 0000 0000 0001 : With expansion pack * 0x050002 : 0000 0101 0000 0000 0000 0010 : Expansion pack removed * * Bit 0 tells us if there is something connected to the expansion port. * Bit 1 tells is if there was something connected that has been removed. */ tmpdata[0] = N64_GET_CAPABILITIES; count = gcn64_transaction(chn, tmpdata, 1, caps, sizeof(caps)); if (count != N64_CAPS_REPLY_LENGTH) { // a failed read could mean the pack or controller was gone. Init // will be necessary next time we detect a pack is present. n64_rumble_state[chn] = RSTATE_INIT; return -1; } /* The brawler 64 wireless gamepad does not like when the get caps command is followed * too closely by the get status command. Without a long pause between the two commands, * it just returns all zeros. */ if (num_players > 1) { // n64Update is called two times on two-player adapters. This // means time lost waiting here doubles. Without this, the controllers // are no longer being polled at the rate set in the gui, which is bad. // (I want this setting to be reliable) // // So on dual port adapters, brawler 64 wireless controllers will be // usable only at intervals >= 4ms. // // TODO: Interleave access like this to allow a higher polling // frequency: Read caps port 1, Read caps port 2, delay, Read status port 1, // read status port 2. (Maybe this could make 3ms intervals work) // if (g_eeprom_data.cfg.poll_interval[0] >= 8) { _delay_ms(2.5); } else if (g_eeprom_data.cfg.poll_interval[0] >= 6) { _delay_ms(1.5); } else if (g_eeprom_data.cfg.poll_interval[0] >= 4) { _delay_ms(1.25); } } else { if (g_eeprom_data.cfg.poll_interval[0] >= 4) { _delay_ms(2.5); } else if (g_eeprom_data.cfg.poll_interval[0] >= 3) { _delay_ms(1.5); } else if (g_eeprom_data.cfg.poll_interval[0] >= 2) { _delay_ms(1.25); // does not work at 1ms } } /* Detect when a pack becomes present and schedule initialisation when it happens. */ if ((caps[2] & 0x01) && (n64_rumble_state[chn] == RSTATE_UNAVAILABLE)) { n64_rumble_state[chn] = RSTATE_INIT; } /* Detect when a pack is removed. */ if (!(caps[2] & 0x01) || (caps[2] & 0x02) ) { n64_rumble_state[chn] = RSTATE_UNAVAILABLE; } #ifdef BUTTON_A_RUMBLE_TEST must_rumble[chn] = force_rumble[chn]; //printf("Caps: %02x %02x %02x\r\n", caps[0], caps[1], caps[2]); #endif switch (n64_rumble_state[chn]) { case RSTATE_INIT: /* Retry until the controller answers with a full byte. */ if (initRumble(chn) != 0) { if (initRumble(chn) != 0) { n64_rumble_state[chn] = RSTATE_UNAVAILABLE; } break; } if (must_rumble[chn]) { controlRumble(chn, 1); n64_rumble_state[chn] = RSTATE_ON; } else { controlRumble(chn, 0); n64_rumble_state[chn] = RSTATE_OFF; } break; case RSTATE_TURNON: if (0 == controlRumble(chn, 1)) { n64_rumble_state[chn] = RSTATE_ON; } break; case RSTATE_TURNOFF: if (0 == controlRumble(chn, 0)) { n64_rumble_state[chn] = RSTATE_OFF; } break; case RSTATE_ON: if (!must_rumble[chn]) { controlRumble(chn, 0); n64_rumble_state[chn] = RSTATE_OFF; } break; case RSTATE_OFF: if (must_rumble[chn]) { controlRumble(chn, 1); n64_rumble_state[chn] = RSTATE_ON; } break; } tmpdata[0] = N64_GET_STATUS; count = gcn64_transaction(chn, tmpdata, 1, status, sizeof(status)); if (count != N64_GET_STATUS_REPLY_LENGTH) { return -1; } /* Bit Function 0 A 1 B 2 Z 3 Start 4 Directional Up 5 Directional Down 6 Directional Left 7 Directional Right 8 unknown (always 0) 9 unknown (always 0) 10 L 11 R 12 C Up 13 C Down 14 C Left 15 C Right 16-23: analog X axis 24-31: analog Y axis */ btns1 = status[0]; btns2 = status[1]; x = status[2]; y = status[3]; #ifdef BUTTON_A_RUMBLE_TEST if (btns1 & 0x80) { force_rumble[chn] = 1; } else { force_rumble[chn] = 0; } #endif last_built_report[chn].pad_type = PAD_TYPE_N64; last_built_report[chn].n64.buttons = (btns1 << 8) | btns2; last_built_report[chn].n64.x = x; last_built_report[chn].n64.y = y; #ifdef PAD_DATA_HAS_RAW /* Copy all the data as-is for the raw field */ last_built_report[chn].n64.raw_data[0] = btns1; last_built_report[chn].n64.raw_data[1] = btns2; last_built_report[chn].n64.raw_data[2] = x; last_built_report[chn].n64.raw_data[3] = y; #endif /* Some cheap non-official controllers * use the full 8 bit range instead of the * normal +-80 observed on official controllers. In * particular, some units (but not all!) produced * by TTX. The symptom is usually "The joystick * left direction does not work". * * So I limit values to the -127 to +127 range, * otherwise it causes problem later * when the sign is inverted. Using 16 bit * signed numbers instead of 8 bit would solve * this, but this is only for cheap, not * even worth using controllers so I don't * care. * * The joystick will now "work" as bad as it would * on a N64, or maybe a little better. This should * help people realise they got what the paid for * instead of suspecting the adapter. */ if (last_built_report[chn].n64.x == -128) last_built_report[chn].n64.x = -127; if (last_built_report[chn].n64.y == -128) last_built_report[chn].n64.y = -127; return 0; } static char n64Probe(unsigned char chn) { int count; char i; unsigned char tmp; unsigned char data[4]; /* Pad answer to N64_GET_CAPABILITIES * * 0x050000 : 0000 0101 0000 0000 0000 0000 : No expansion pack * 0x050001 : 0000 0101 0000 0000 0000 0001 : With expansion pack * 0x050002 : 0000 0101 0000 0000 0000 0010 : Expansion pack removed * * Bit 0 tells us if there is something connected to the expansion port. * Bit 1 tells is if there was something connected that has been removed. */ n64_rumble_state[chn] = RSTATE_UNAVAILABLE; for (i=0; i<15; i++) { _delay_ms(30); tmp = N64_GET_CAPABILITIES; count = gcn64_transaction(chn, &tmp, 1, data, sizeof(data)); if (count == N64_CAPS_REPLY_LENGTH) { return 1; } } return 0; } static char n64Changed(unsigned char chn) { return memcmp(&last_built_report[chn], &last_sent_report[chn], sizeof(gamepad_data)); } static void n64GetReport(unsigned char chn, gamepad_data *dst) { if (dst) memcpy(dst, &last_built_report[chn], sizeof(gamepad_data)); memcpy(&last_sent_report[chn], &last_built_report[chn], sizeof(gamepad_data)); } static void n64SetVibration(unsigned char chn, char enable) { must_rumble[chn] = enable; } static Gamepad N64Gamepad = { .init = n64Init, .update = n64Update, .changed = n64Changed, .getReport = n64GetReport, .probe = n64Probe, .setVibration = n64SetVibration, }; Gamepad *n64GetGamepad(void) { return &N64Gamepad; }