mirror of https://github.com/PCSX2/pcsx2.git
468 lines
15 KiB
C++
468 lines
15 KiB
C++
/* OnePAD
|
|
* Copyright (C) 2016
|
|
*
|
|
* Based on LilyPad
|
|
* Copyright (C) 2002-2014 PCSX2 Dev Team/ChickenLiver
|
|
*
|
|
* 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 2 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, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include "state_management.h"
|
|
|
|
// Typical packet response on the bus
|
|
static const u8 ConfigExit[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
static const u8 noclue[7] = {0x5A, 0x00, 0x00, 0x02, 0x00, 0x00, 0x5A};
|
|
static const u8 setMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
static const u8 queryModelDS2[7] = {0x5A, 0x03, 0x02, 0x00, 0x02, 0x01, 0x00};
|
|
static const u8 queryModelDS1[7] = {0x5A, 0x01, 0x02, 0x00, 0x02, 0x01, 0x00};
|
|
static const u8 queryComb[7] = {0x5A, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00};
|
|
static const u8 queryMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
static const u8 setNativeMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A};
|
|
|
|
static u8 queryMaskMode[7] = {0x5A, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x5A};
|
|
|
|
static const u8 queryAct[2][7] = {
|
|
{0x5A, 0x00, 0x00, 0x01, 0x02, 0x00, 0x0A},
|
|
{0x5A, 0x00, 0x00, 0x01, 0x01, 0x01, 0x14}};
|
|
|
|
QueryInfo query;
|
|
Pad pads[2][4];
|
|
int slots[2] = {0, 0};
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// QueryInfo implementation
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void QueryInfo::reset()
|
|
{
|
|
port = 0;
|
|
slot = 0;
|
|
lastByte = 1;
|
|
currentCommand = 0;
|
|
numBytes = 0;
|
|
queryDone = 1;
|
|
memset(response, 0xF3, sizeof(response));
|
|
}
|
|
|
|
u8 QueryInfo::start_poll(int _port)
|
|
{
|
|
if (port > 1) {
|
|
reset();
|
|
return 0;
|
|
}
|
|
|
|
queryDone = 0;
|
|
port = _port;
|
|
slot = slots[port];
|
|
numBytes = 2;
|
|
lastByte = 0;
|
|
|
|
return 0xFF;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Pad implementation
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void Pad::set_mode(int _mode)
|
|
{
|
|
mode = _mode;
|
|
|
|
#if 0
|
|
fprintf(stdout, "OnePad: set new pad mode=");
|
|
if (mode == MODE_DIGITAL)
|
|
fprintf(stdout, "DIGITAL\n");
|
|
else if (mode == MODE_ANALOG)
|
|
fprintf(stdout, "ANALOG\n");
|
|
else if (mode == MODE_DS2_NATIVE)
|
|
fprintf(stdout, "DS2 NATIVE\n");
|
|
else
|
|
fprintf(stdout, "??? 0x%x\n", mode);
|
|
#endif
|
|
}
|
|
|
|
void Pad::set_vibrate(int motor, u8 val)
|
|
{
|
|
nextVibrate[motor] = val;
|
|
}
|
|
|
|
void Pad::reset_vibrate()
|
|
{
|
|
set_vibrate(0, 0);
|
|
set_vibrate(1, 0);
|
|
memset(vibrate, 0xFF, sizeof(vibrate));
|
|
vibrate[0] = 0x5A;
|
|
}
|
|
|
|
void Pad::reset()
|
|
{
|
|
memset(this, 0, sizeof(PadFreezeData));
|
|
|
|
set_mode(MODE_DIGITAL);
|
|
umask[0] = umask[1] = 0xFF;
|
|
|
|
// Sets up vibrate variable.
|
|
reset_vibrate();
|
|
}
|
|
|
|
void Pad::rumble(int port)
|
|
{
|
|
for (int motor = 0; motor < 2; motor++) {
|
|
// TODO: Probably be better to send all of these at once.
|
|
if (nextVibrate[motor] | currentVibrate[motor]) {
|
|
currentVibrate[motor] = nextVibrate[motor];
|
|
|
|
GamePad::DoRumble(motor, port);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Pad::stop_vibrate_all()
|
|
{
|
|
#if 0
|
|
for (int i=0; i<8; i++) {
|
|
SetVibrate(i&1, i>>1, 0, 0);
|
|
SetVibrate(i&1, i>>1, 1, 0);
|
|
}
|
|
#endif
|
|
// FIXME equivalent ?
|
|
for (int port = 0; port < 2; port++)
|
|
for (int slot = 0; slot < 4; slot++)
|
|
pads[port][slot].reset_vibrate();
|
|
}
|
|
|
|
void Pad::reset_all()
|
|
{
|
|
for (int port = 0; port < 2; port++)
|
|
for (int slot = 0; slot < 4; slot++)
|
|
pads[port][slot].reset();
|
|
}
|
|
|
|
void Pad::rumble_all()
|
|
{
|
|
for (int port = 0; port < 2; port++)
|
|
for (int slot = 0; slot < 4; slot++)
|
|
pads[port][slot].rumble(port);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Pad implementation
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
inline bool IsDualshock2()
|
|
{
|
|
// FIXME
|
|
#if 0
|
|
return config.padConfigs[query.port][query.slot].type == Dualshock2Pad ||
|
|
(config.padConfigs[query.port][query.slot].type == GuitarPad && config.GH2);
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
u8 pad_start_poll(u8 pad)
|
|
{
|
|
return query.start_poll(pad - 1);
|
|
}
|
|
|
|
u8 pad_poll(u8 value)
|
|
{
|
|
if (query.lastByte + 1 >= query.numBytes) {
|
|
return 0;
|
|
}
|
|
if (query.lastByte && query.queryDone) {
|
|
return query.response[++query.lastByte];
|
|
}
|
|
|
|
Pad *pad = &pads[query.port][query.slot];
|
|
|
|
if (query.lastByte == 0) {
|
|
query.lastByte++;
|
|
query.currentCommand = value;
|
|
|
|
switch (value) {
|
|
case CMD_CONFIG_MODE:
|
|
if (pad->config) {
|
|
// In config mode. Might not actually be leaving it.
|
|
query.set_result(ConfigExit);
|
|
return 0xF3;
|
|
}
|
|
// fallthrough on purpose (but I don't know why)
|
|
|
|
case CMD_READ_DATA_AND_VIBRATE: {
|
|
query.response[2] = 0x5A;
|
|
#if 0
|
|
int i;
|
|
Update(query.port, query.slot);
|
|
ButtonSum *sum = &pad->sum;
|
|
|
|
u8 b1 = 0xFF, b2 = 0xFF;
|
|
for (i = 0; i<4; i++) {
|
|
b1 -= (sum->buttons[i] > 0) << i;
|
|
}
|
|
for (i = 0; i<8; i++) {
|
|
b2 -= (sum->buttons[i+4] > 0) << i;
|
|
}
|
|
#endif
|
|
|
|
// FIXME
|
|
#if 0
|
|
if (config.padConfigs[query.port][query.slot].type == GuitarPad && !config.GH2) {
|
|
sum->buttons[15] = 255;
|
|
// Not sure about this. Forces wammy to be from 0 to 0x7F.
|
|
// if (sum->sticks[2].vert > 0) sum->sticks[2].vert = 0;
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
for (i = 4; i<8; i++) {
|
|
b1 -= (sum->buttons[i+8] > 0) << i;
|
|
}
|
|
#endif
|
|
|
|
// FIXME
|
|
#if 0
|
|
//Left, Right and Down are always pressed on Pop'n Music controller.
|
|
if (config.padConfigs[query.port][query.slot].type == PopnPad)
|
|
b1=b1 & 0x1f;
|
|
#endif
|
|
|
|
uint16_t buttons = key_status->get(query.port);
|
|
|
|
query.numBytes = 5;
|
|
|
|
query.response[3] = (buttons >> 8) & 0xFF;
|
|
query.response[4] = (buttons >> 0) & 0xFF;
|
|
|
|
if (pad->mode != MODE_DIGITAL) { // ANALOG || DS2 native
|
|
query.numBytes = 9;
|
|
|
|
query.response[5] = key_status->get(query.port, PAD_R_RIGHT);
|
|
query.response[6] = key_status->get(query.port, PAD_R_UP);
|
|
query.response[7] = key_status->get(query.port, PAD_L_RIGHT);
|
|
query.response[8] = key_status->get(query.port, PAD_L_UP);
|
|
|
|
if (pad->mode != MODE_ANALOG) { // DS2 native
|
|
query.numBytes = 21;
|
|
|
|
query.response[9] = !test_bit(buttons, 13) ? key_status->get(query.port, PAD_RIGHT) : 0;
|
|
query.response[10] = !test_bit(buttons, 15) ? key_status->get(query.port, PAD_LEFT) : 0;
|
|
query.response[11] = !test_bit(buttons, 12) ? key_status->get(query.port, PAD_UP) : 0;
|
|
query.response[12] = !test_bit(buttons, 14) ? key_status->get(query.port, PAD_DOWN) : 0;
|
|
|
|
query.response[13] = !test_bit(buttons, 4) ? key_status->get(query.port, PAD_TRIANGLE) : 0;
|
|
query.response[14] = !test_bit(buttons, 5) ? key_status->get(query.port, PAD_CIRCLE) : 0;
|
|
query.response[15] = !test_bit(buttons, 6) ? key_status->get(query.port, PAD_CROSS) : 0;
|
|
query.response[16] = !test_bit(buttons, 7) ? key_status->get(query.port, PAD_SQUARE) : 0;
|
|
query.response[17] = !test_bit(buttons, 2) ? key_status->get(query.port, PAD_L1) : 0;
|
|
query.response[18] = !test_bit(buttons, 3) ? key_status->get(query.port, PAD_R1) : 0;
|
|
query.response[19] = !test_bit(buttons, 0) ? key_status->get(query.port, PAD_L2) : 0;
|
|
query.response[20] = !test_bit(buttons, 1) ? key_status->get(query.port, PAD_R2) : 0;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
query.response[3] = b1;
|
|
query.response[4] = b2;
|
|
|
|
query.numBytes = 5;
|
|
if (pad->mode != MODE_DIGITAL) {
|
|
query.response[5] = Cap((sum->sticks[0].horiz+255)/2);
|
|
query.response[6] = Cap((sum->sticks[0].vert+255)/2);
|
|
query.response[7] = Cap((sum->sticks[1].horiz+255)/2);
|
|
query.response[8] = Cap((sum->sticks[1].vert+255)/2);
|
|
|
|
query.numBytes = 9;
|
|
if (pad->mode != MODE_ANALOG) {
|
|
// Good idea? No clue.
|
|
//query.response[3] &= pad->mask[0];
|
|
//query.response[4] &= pad->mask[1];
|
|
|
|
// No need to cap these, already done int CapSum().
|
|
query.response[9] = (unsigned char)sum->buttons[13]; //D-pad right
|
|
query.response[10] = (unsigned char)sum->buttons[15]; //D-pad left
|
|
query.response[11] = (unsigned char)sum->buttons[12]; //D-pad up
|
|
query.response[12] = (unsigned char)sum->buttons[14]; //D-pad down
|
|
|
|
query.response[13] = (unsigned char) sum->buttons[8];
|
|
query.response[14] = (unsigned char) sum->buttons[9];
|
|
query.response[15] = (unsigned char) sum->buttons[10];
|
|
query.response[16] = (unsigned char) sum->buttons[11];
|
|
query.response[17] = (unsigned char) sum->buttons[6];
|
|
query.response[18] = (unsigned char) sum->buttons[7];
|
|
query.response[19] = (unsigned char) sum->buttons[4];
|
|
query.response[20] = (unsigned char) sum->buttons[5];
|
|
query.numBytes = 21;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
query.lastByte = 1;
|
|
return pad->mode;
|
|
|
|
case CMD_SET_VREF_PARAM:
|
|
query.set_final_result(noclue);
|
|
break;
|
|
|
|
case CMD_QUERY_DS2_ANALOG_MODE:
|
|
// Right? Wrong? No clue.
|
|
if (pad->mode == MODE_DIGITAL) {
|
|
queryMaskMode[1] = queryMaskMode[2] = queryMaskMode[3] = 0;
|
|
queryMaskMode[6] = 0x00;
|
|
} else {
|
|
queryMaskMode[1] = pad->umask[0];
|
|
queryMaskMode[2] = pad->umask[1];
|
|
queryMaskMode[3] = 0x03;
|
|
// Not entirely sure about this.
|
|
//queryMaskMode[3] = 0x01 | (pad->mode == MODE_DS2_NATIVE)*2;
|
|
queryMaskMode[6] = 0x5A;
|
|
}
|
|
query.set_final_result(queryMaskMode);
|
|
break;
|
|
|
|
case CMD_SET_MODE_AND_LOCK:
|
|
query.set_result(setMode);
|
|
pad->reset_vibrate();
|
|
break;
|
|
|
|
case CMD_QUERY_MODEL_AND_MODE:
|
|
if (IsDualshock2()) {
|
|
query.set_final_result(queryModelDS2);
|
|
} else {
|
|
query.set_final_result(queryModelDS1);
|
|
}
|
|
// Not digital mode.
|
|
query.response[5] = (pad->mode & 0xF) != 1;
|
|
break;
|
|
|
|
case CMD_QUERY_ACT:
|
|
query.set_result(queryAct[0]);
|
|
break;
|
|
|
|
case CMD_QUERY_COMB:
|
|
query.set_final_result(queryComb);
|
|
break;
|
|
|
|
case CMD_QUERY_MODE:
|
|
query.set_result(queryMode);
|
|
break;
|
|
|
|
case CMD_VIBRATION_TOGGLE:
|
|
memcpy(query.response + 2, pad->vibrate, 7);
|
|
query.numBytes = 9;
|
|
//query.set_result(pad->vibrate); // warning copy 7b not 8 (but it is really important?)
|
|
pad->reset_vibrate();
|
|
break;
|
|
|
|
case CMD_SET_DS2_NATIVE_MODE:
|
|
if (IsDualshock2()) {
|
|
query.set_result(setNativeMode);
|
|
} else {
|
|
query.set_final_result(setNativeMode);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
query.numBytes = 0;
|
|
query.queryDone = 1;
|
|
break;
|
|
}
|
|
|
|
return 0xF3;
|
|
|
|
} else {
|
|
query.lastByte++;
|
|
|
|
switch (query.currentCommand) {
|
|
case CMD_READ_DATA_AND_VIBRATE:
|
|
if (query.lastByte == pad->vibrateI[0])
|
|
pad->set_vibrate(1, 255 * (value & 1));
|
|
else if (query.lastByte == pad->vibrateI[1])
|
|
pad->set_vibrate(0, value);
|
|
|
|
break;
|
|
|
|
case CMD_CONFIG_MODE:
|
|
if (query.lastByte == 3) {
|
|
query.queryDone = 1;
|
|
pad->config = value;
|
|
}
|
|
break;
|
|
|
|
case CMD_SET_MODE_AND_LOCK:
|
|
if (query.lastByte == 3 && value < 2) {
|
|
pad->set_mode(value ? MODE_ANALOG : MODE_DIGITAL);
|
|
} else if (query.lastByte == 4) {
|
|
if (value == 3)
|
|
pad->modeLock = 3;
|
|
else
|
|
pad->modeLock = 0;
|
|
|
|
query.queryDone = 1;
|
|
}
|
|
break;
|
|
|
|
case CMD_QUERY_ACT:
|
|
if (query.lastByte == 3) {
|
|
if (value < 2)
|
|
query.set_result(queryAct[value]);
|
|
// bunch of 0's
|
|
// else query.set_result(setMode);
|
|
query.queryDone = 1;
|
|
}
|
|
break;
|
|
|
|
case CMD_QUERY_MODE:
|
|
if (query.lastByte == 3 && value < 2) {
|
|
query.response[6] = 4 + value * 3;
|
|
query.queryDone = 1;
|
|
}
|
|
// bunch of 0's
|
|
//else data = setMode;
|
|
break;
|
|
|
|
case CMD_VIBRATION_TOGGLE:
|
|
if (query.lastByte >= 3) {
|
|
if (value == 0) {
|
|
pad->vibrateI[0] = (u8)query.lastByte;
|
|
} else if (value == 1) {
|
|
pad->vibrateI[1] = (u8)query.lastByte;
|
|
}
|
|
pad->vibrate[query.lastByte - 2] = value;
|
|
}
|
|
break;
|
|
|
|
case CMD_SET_DS2_NATIVE_MODE:
|
|
if (query.lastByte == 3 || query.lastByte == 4) {
|
|
pad->umask[query.lastByte - 3] = value;
|
|
} else if (query.lastByte == 5) {
|
|
if (!(value & 1))
|
|
pad->set_mode(MODE_DIGITAL);
|
|
else if (!(value & 2))
|
|
pad->set_mode(MODE_ANALOG);
|
|
else
|
|
pad->set_mode(MODE_DS2_NATIVE);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
return query.response[query.lastByte];
|
|
}
|
|
}
|