mirror of https://github.com/PCSX2/pcsx2.git
USB: Add support for Shinkansen DenshaCon
Add support for TCPP20011 controllers, datasheets courtesy of Marc Riera. Tested with a One Handle MasCon for Nintendo Switch as the controller device. Link: https://marcriera.github.io/ddgo-controller-docs/controllers/usb/tcpp20011
This commit is contained in:
parent
edcd1d0f9f
commit
904d6b9c78
|
@ -25,6 +25,15 @@ namespace usb_pad
|
||||||
return "TrainController";
|
return "TrainController";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::span<const char*> TrainDevice::SubTypes() const
|
||||||
|
{
|
||||||
|
static const char* subtypes[] = {
|
||||||
|
TRANSLATE_NOOP("USB", "Type 2"),
|
||||||
|
TRANSLATE_NOOP("USB", "Shinkansen"),
|
||||||
|
};
|
||||||
|
return subtypes;
|
||||||
|
}
|
||||||
|
|
||||||
enum TrainControlID
|
enum TrainControlID
|
||||||
{
|
{
|
||||||
CID_TC_POWER,
|
CID_TC_POWER,
|
||||||
|
@ -46,6 +55,11 @@ namespace usb_pad
|
||||||
};
|
};
|
||||||
|
|
||||||
std::span<const InputBindingInfo> TrainDevice::Bindings(u32 subtype) const
|
std::span<const InputBindingInfo> TrainDevice::Bindings(u32 subtype) const
|
||||||
|
{
|
||||||
|
switch (subtype)
|
||||||
|
{
|
||||||
|
case TRAIN_TYPE2:
|
||||||
|
case TRAIN_SHINKANSEN:
|
||||||
{
|
{
|
||||||
static constexpr const InputBindingInfo bindings[] = {
|
static constexpr const InputBindingInfo bindings[] = {
|
||||||
{"Power", TRANSLATE_NOOP("USB", "Power"), ICON_PF_LEFT_ANALOG_DOWN, InputBindingInfo::Type::Axis, CID_TC_POWER, GenericInputBinding::LeftStickDown},
|
{"Power", TRANSLATE_NOOP("USB", "Power"), ICON_PF_LEFT_ANALOG_DOWN, InputBindingInfo::Type::Axis, CID_TC_POWER, GenericInputBinding::LeftStickDown},
|
||||||
|
@ -64,6 +78,11 @@ namespace usb_pad
|
||||||
|
|
||||||
return bindings;
|
return bindings;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
static void train_handle_reset(USBDevice* dev)
|
static void train_handle_reset(USBDevice* dev)
|
||||||
{
|
{
|
||||||
|
@ -191,8 +210,9 @@ namespace usb_pad
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TrainDeviceState::TrainDeviceState(u32 port_)
|
TrainDeviceState::TrainDeviceState(u32 port_, TrainDeviceTypes type_)
|
||||||
: port(port_)
|
: port(port_)
|
||||||
|
, type(type_)
|
||||||
{
|
{
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
@ -273,8 +293,68 @@ namespace usb_pad
|
||||||
return notches[std::size(notches) - 1].second;
|
return notches[std::size(notches) - 1].second;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrainControlID buttons are laid out in Type 2 ordering, no need to remap.
|
static u8 dct02_power(u8 value)
|
||||||
#define dct01_buttons(buttons) (buttons)
|
{
|
||||||
|
// (N) 0x12 0x24 0x36 0x48 0x5A 0x6C 0x7E 0x90 0xA2 0xB4 0xC6 0xD7 0xE9 0xFB (P13)
|
||||||
|
static std::pair<u8, u8> const notches[] = {
|
||||||
|
// { control_in, emulated_out },
|
||||||
|
{0xF7, 0xFB},
|
||||||
|
{0xE4, 0xE9},
|
||||||
|
{0xD1, 0xD7},
|
||||||
|
{0xBE, 0xC6},
|
||||||
|
{0xAB, 0xB4},
|
||||||
|
{0x98, 0xA2},
|
||||||
|
{0x85, 0x90},
|
||||||
|
{0x72, 0x7E},
|
||||||
|
{0x5F, 0x6C},
|
||||||
|
{0x4C, 0x5A},
|
||||||
|
{0x39, 0x48},
|
||||||
|
{0x26, 0x36},
|
||||||
|
{0x13, 0x24},
|
||||||
|
{0x00, 0x12},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& x : notches)
|
||||||
|
{
|
||||||
|
if (value >= x.first)
|
||||||
|
return x.second;
|
||||||
|
}
|
||||||
|
return notches[std::size(notches) - 1].second;
|
||||||
|
}
|
||||||
|
static u8 dct02_brake(u8 value)
|
||||||
|
{
|
||||||
|
// (NB) 0x1C 0x38 0x54 0x70 0x8B 0xA7 0xC3 0xDF 0xFB (EB)
|
||||||
|
static std::pair<u8, u8> const notches[] = {
|
||||||
|
// { control_in, emulated_out },
|
||||||
|
{0xF8, 0xFB},
|
||||||
|
{0xCA, 0xDF},
|
||||||
|
{0xAE, 0xC3},
|
||||||
|
{0x92, 0xA7},
|
||||||
|
{0x76, 0x8B},
|
||||||
|
{0x5A, 0x70},
|
||||||
|
{0x3E, 0x54},
|
||||||
|
{0x22, 0x38},
|
||||||
|
{0x00, 0x1C},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& x : notches)
|
||||||
|
{
|
||||||
|
if (value >= x.first)
|
||||||
|
return x.second;
|
||||||
|
}
|
||||||
|
return notches[std::size(notches) - 1].second;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define get_ab(buttons) (button_at(buttons, CID_TC_A) | button_at(buttons, CID_TC_B))
|
||||||
|
#define swap_cd(buttons) ((button_at(buttons, CID_TC_C) << 1) | (button_at(buttons, CID_TC_D) >> 1))
|
||||||
|
#define get_ss(buttons) (button_at(buttons, CID_TC_START) | button_at(buttons, CID_TC_SELECT))
|
||||||
|
|
||||||
|
// TrainControlID buttons are laid out in Type 2 ordering, no need to remap.
|
||||||
|
constexpr u8 dct01_buttons(u8 buttons) { return buttons; }
|
||||||
|
constexpr u8 dct02_buttons(u8 buttons)
|
||||||
|
{
|
||||||
|
return ((get_ab(buttons) << 2) | (swap_cd(buttons) >> 2) | get_ss(buttons));
|
||||||
|
}
|
||||||
|
|
||||||
static void train_handle_data(USBDevice* dev, USBPacket* p)
|
static void train_handle_data(USBDevice* dev, USBPacket* p)
|
||||||
{
|
{
|
||||||
|
@ -289,6 +369,10 @@ namespace usb_pad
|
||||||
|
|
||||||
s->UpdateHatSwitch();
|
s->UpdateHatSwitch();
|
||||||
|
|
||||||
|
switch (s->type)
|
||||||
|
{
|
||||||
|
case TRAIN_TYPE2:
|
||||||
|
{
|
||||||
TrainConData_Type2 out = {};
|
TrainConData_Type2 out = {};
|
||||||
out.control = 0x1;
|
out.control = 0x1;
|
||||||
out.brake = dct01_brake(s->data.brake);
|
out.brake = dct01_brake(s->data.brake);
|
||||||
|
@ -297,17 +381,49 @@ namespace usb_pad
|
||||||
out.hat = s->data.hatswitch;
|
out.hat = s->data.hatswitch;
|
||||||
out.buttons = dct01_buttons(s->data.buttons);
|
out.buttons = dct01_buttons(s->data.buttons);
|
||||||
usb_packet_copy(p, &out, sizeof(out));
|
usb_packet_copy(p, &out, sizeof(out));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TRAIN_SHINKANSEN:
|
||||||
|
{
|
||||||
|
TrainConData_Shinkansen out = {};
|
||||||
|
out.brake = dct02_brake(s->data.brake);
|
||||||
|
out.power = dct02_power(s->data.power);
|
||||||
|
out.horn = 0xFF; // Button C doubles as horn, skip.
|
||||||
|
out.hat = s->data.hatswitch;
|
||||||
|
out.buttons = dct02_buttons(s->data.buttons);
|
||||||
|
usb_packet_copy(p, &out, sizeof(out));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
Console.Error("Unhandled TrainController USB_TOKEN_IN pid=%d ep=%u type=%u", p->pid, p->ep->nr, s->type);
|
||||||
|
p->status = USB_RET_IOERROR;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
USBDevice* TrainDevice::CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const
|
USBDevice* TrainDevice::CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const
|
||||||
{
|
{
|
||||||
TrainDeviceState* s = new TrainDeviceState(port);
|
TrainDeviceState* s = new TrainDeviceState(port, static_cast<TrainDeviceTypes>(subtype));
|
||||||
|
|
||||||
s->desc.full = &s->desc_dev;
|
s->desc.full = &s->desc_dev;
|
||||||
s->desc.str = dct01_desc_strings;
|
|
||||||
|
|
||||||
|
switch (subtype)
|
||||||
|
{
|
||||||
|
case TRAIN_TYPE2:
|
||||||
|
s->desc.str = dct01_desc_strings;
|
||||||
if (usb_desc_parse_dev(dct01_dev_descriptor, sizeof(dct01_dev_descriptor), s->desc, s->desc_dev) < 0)
|
if (usb_desc_parse_dev(dct01_dev_descriptor, sizeof(dct01_dev_descriptor), s->desc, s->desc_dev) < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
break;
|
||||||
|
case TRAIN_SHINKANSEN:
|
||||||
|
s->desc.str = dct02_desc_strings;
|
||||||
|
if (usb_desc_parse_dev(dct02_dev_descriptor, sizeof(dct02_dev_descriptor), s->desc, s->desc_dev) < 0)
|
||||||
|
goto fail;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
if (usb_desc_parse_config(taito_denshacon_config_descriptor, sizeof(taito_denshacon_config_descriptor), s->desc_dev) < 0)
|
if (usb_desc_parse_config(taito_denshacon_config_descriptor, sizeof(taito_denshacon_config_descriptor), s->desc_dev) < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,12 @@
|
||||||
|
|
||||||
namespace usb_pad
|
namespace usb_pad
|
||||||
{
|
{
|
||||||
|
enum TrainDeviceTypes
|
||||||
|
{
|
||||||
|
TRAIN_TYPE2, // TCPP20009 or similar
|
||||||
|
TRAIN_SHINKANSEN, // TCPP20011
|
||||||
|
TRAIN_COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
class TrainDevice final : public DeviceProxy
|
class TrainDevice final : public DeviceProxy
|
||||||
{
|
{
|
||||||
|
@ -16,6 +22,7 @@ namespace usb_pad
|
||||||
USBDevice* CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const override;
|
USBDevice* CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const override;
|
||||||
const char* Name() const override;
|
const char* Name() const override;
|
||||||
const char* TypeName() const override;
|
const char* TypeName() const override;
|
||||||
|
std::span<const char*> SubTypes() const override;
|
||||||
float GetBindingValue(const USBDevice* dev, u32 bind_index) const override;
|
float GetBindingValue(const USBDevice* dev, u32 bind_index) const override;
|
||||||
void SetBindingValue(USBDevice* dev, u32 bind_index, float value) const override;
|
void SetBindingValue(USBDevice* dev, u32 bind_index, float value) const override;
|
||||||
std::span<const InputBindingInfo> Bindings(u32 subtype) const override;
|
std::span<const InputBindingInfo> Bindings(u32 subtype) const override;
|
||||||
|
@ -33,11 +40,22 @@ namespace usb_pad
|
||||||
u8 buttons;
|
u8 buttons;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(TrainConData_Type2) == 6);
|
static_assert(sizeof(TrainConData_Type2) == 6);
|
||||||
|
|
||||||
|
struct TrainConData_Shinkansen
|
||||||
|
{
|
||||||
|
u8 brake;
|
||||||
|
u8 power;
|
||||||
|
u8 horn;
|
||||||
|
u8 hat;
|
||||||
|
u8 buttons;
|
||||||
|
u8 pad;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(TrainConData_Shinkansen) == 6);
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
struct TrainDeviceState
|
struct TrainDeviceState
|
||||||
{
|
{
|
||||||
TrainDeviceState(u32 port_);
|
TrainDeviceState(u32 port_, TrainDeviceTypes type_);
|
||||||
~TrainDeviceState();
|
~TrainDeviceState();
|
||||||
|
|
||||||
void Reset();
|
void Reset();
|
||||||
|
@ -48,6 +66,7 @@ namespace usb_pad
|
||||||
USBDescDevice desc_dev{};
|
USBDescDevice desc_dev{};
|
||||||
|
|
||||||
u32 port = 0;
|
u32 port = 0;
|
||||||
|
TrainDeviceTypes type = TRAIN_TYPE2;
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
|
@ -126,4 +145,16 @@ namespace usb_pad
|
||||||
// dct01_dev_descriptor
|
// dct01_dev_descriptor
|
||||||
DEFINE_DCT_DEV_DESCRIPTOR(dct01, 0x04, 0x0004);
|
DEFINE_DCT_DEV_DESCRIPTOR(dct01, 0x04, 0x0004);
|
||||||
|
|
||||||
|
// ---- Shinkansen controller ----
|
||||||
|
|
||||||
|
static const USBDescStrings dct02_desc_strings = {
|
||||||
|
"",
|
||||||
|
"TAITO",
|
||||||
|
"TAITO_DENSYA_CON_T02",
|
||||||
|
"TCPP20011",
|
||||||
|
};
|
||||||
|
|
||||||
|
// dct02_dev_descriptor
|
||||||
|
DEFINE_DCT_DEV_DESCRIPTOR(dct02, 0x05, 0x0005);
|
||||||
|
|
||||||
} // namespace usb_pad
|
} // namespace usb_pad
|
||||||
|
|
Loading…
Reference in New Issue