/* * Copyright (C) 2020 ndeadly * * 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 "controller_management.hpp" #include #include #include #include #include namespace ams::controller { namespace { constexpr auto cod_major_peripheral = 0x05; constexpr auto cod_minor_gamepad = 0x08; constexpr auto cod_minor_joystick = 0x04; os::Mutex g_controller_lock(false); std::vector> g_controllers; inline bool bdcmp(const bluetooth::Address *addr1, const bluetooth::Address *addr2) { return std::memcmp(addr1, addr2, sizeof(bluetooth::Address)) == 0; } } ControllerType Identify(const BluetoothDevicesSettings *device) { for (auto hwId : SwitchController::hardware_ids) { if ( (device->vid == hwId.vid) && (device->pid == hwId.pid) ) { return ControllerType_Switch; } } for (auto hwId : WiiController::hardware_ids) { if ( (device->vid == hwId.vid) && (device->pid == hwId.pid) ) { return ControllerType_Wii; } } for (auto hwId : Dualshock4Controller::hardware_ids) { if ( (device->vid == hwId.vid) && (device->pid == hwId.pid) ) { return ControllerType_Dualshock4; } } for (auto hwId : XboxOneController::hardware_ids) { if ( (device->vid == hwId.vid) && (device->pid == hwId.pid) ) { return ControllerType_XboxOne; } } // Handle the case where joycons have been assigned random hardware ids when paired via rails if (IsJoyCon(device->name)) { return ControllerType_Switch;; } return ControllerType_Unknown; } bool IsGamepad(const bluetooth::DeviceClass *cod) { return ((cod->cod[1] & 0x0f) == cod_major_peripheral) && (((cod->cod[2] & 0x0f) == cod_minor_gamepad) || ((cod->cod[2] & 0x0f) == cod_minor_joystick)); } bool IsJoyCon(const char *name) { return std::strncmp(name, "Joy-Con (L)", sizeof(BluetoothName)) == 0 || std::strncmp(name, "Joy-Con (R)", sizeof(BluetoothName)) == 0; } bool IsOfficialSwitchControllerName(const char *name) { return std::strncmp(name, "Joy-Con (L)", sizeof(BluetoothName)) == 0 || std::strncmp(name, "Joy-Con (R)", sizeof(BluetoothName)) == 0 || std::strncmp(name, "Pro Controller", sizeof(BluetoothName)) == 0 || std::strncmp(name, "Lic Pro Controller", sizeof(BluetoothName)) == 0 || std::strncmp(name, "NES Controller", sizeof(BluetoothName)) == 0 || std::strncmp(name, "HVC Controller", sizeof(BluetoothName)) == 0 || std::strncmp(name, "SNES Controller", sizeof(BluetoothName)) == 0 || std::strncmp(name, "NintendoGamepad", sizeof(BluetoothName)) == 0 ; } void AttachHandler(const bluetooth::Address *address) { std::scoped_lock lk(g_controller_lock); BluetoothDevicesSettings device; R_ABORT_UNLESS(btdrvGetPairedDeviceInfo(address, &device)); switch (Identify(&device)) { case ControllerType_Switch: g_controllers.push_back(std::make_unique(address)); break; case ControllerType_Wii: g_controllers.push_back(std::make_unique(address)); break; case ControllerType_Dualshock4: g_controllers.push_back(std::make_unique(address)); break; case ControllerType_XboxOne: g_controllers.push_back(std::make_unique(address)); break; default: return; } g_controllers.back()->Initialize(); } void RemoveHandler(const bluetooth::Address *address) { std::scoped_lock lk(g_controller_lock); for (auto it = g_controllers.begin(); it < g_controllers.end(); ++it) { if (bdcmp(&(*it)->Address(), address)) { g_controllers.erase(it); return; } } } SwitchController *LocateHandler(const bluetooth::Address *address) { std::scoped_lock lk(g_controller_lock); for (auto it = g_controllers.begin(); it < g_controllers.end(); ++it) { if (bdcmp(&(*it)->Address(), address)) { return (*it).get(); } } return nullptr; } }