chromium/device/gamepad/nintendo_controller.cc

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "device/gamepad/nintendo_controller.h"

#include <algorithm>
#include <utility>

#include "base/functional/bind.h"
#include "base/ranges/algorithm.h"
#include "base/task/single_thread_task_runner.h"
#include "device/gamepad/gamepad_data_fetcher.h"
#include "device/gamepad/gamepad_id_list.h"

namespace device {
namespace {
// Device IDs for the Switch Charging Grip, also used for composite devices.
const uint16_t kVendorNintendo =;
const uint16_t kProductSwitchChargingGrip =;

// Maximum output report sizes, used to distinguish USB and Bluetooth.
const size_t kSwitchProMaxOutputReportSizeBytesUsb =;
const size_t kSwitchProMaxOutputReportSizeBytesBluetooth =;

// Input report size.
const size_t kMaxInputReportSizeBytes =;

// Device name for a composite Joy-Con device.
const char kProductNameSwitchCompositeDevice[] =;

// Report IDs.
const uint8_t kReportIdOutput01 =;
const uint8_t kReportIdOutput10 =;
const uint8_t kReportIdInput21 =;
const uint8_t kReportIdInput30 =;
const uint8_t kUsbReportIdOutput80 =;
const uint8_t kUsbReportIdInput81 =;

// Sub-types of the 0x80 output report, used for initialization.
const uint8_t kSubTypeRequestMac =;
const uint8_t kSubTypeHandshake =;
const uint8_t kSubTypeBaudRate =;
const uint8_t kSubTypeDisableUsbTimeout =;
const uint8_t kSubTypeEnableUsbTimeout =;

// UART subcommands.
const uint8_t kSubCommandSetInputReportMode =;
const uint8_t kSubCommandReadSpi =;
const uint8_t kSubCommandSetPlayerLights =;
const uint8_t kSubCommand33 =;
const uint8_t kSubCommandEnableImu =;
const uint8_t kSubCommandSetImuSensitivity =;
const uint8_t kSubCommandEnableVibration =;

// SPI memory regions.
const uint16_t kSpiImuCalibrationAddress =;
const size_t kSpiImuCalibrationSize =;
const uint16_t kSpiAnalogStickCalibrationAddress =;
const size_t kSpiAnalogStickCalibrationSize =;
const uint16_t kSpiImuHorizontalOffsetsAddress =;
const size_t kSpiImuHorizontalOffsetsSize =;
const uint16_t kSpiAnalogStickParametersAddress =;
const size_t kSpiAnalogStickParametersSize =;

// Byte index for the first byte of subcommand data in 0x80 output reports.
const size_t kSubCommandDataOffset =;
// Byte index for the first byte of SPI data in SPI read responses.
const size_t kSpiDataOffset =;

// Values for the |device_type| field reported in the MAC reply.
const uint8_t kUsbDeviceTypeChargingGripNoDevice =;
const uint8_t kUsbDeviceTypeChargingGripJoyConL =;
const uint8_t kUsbDeviceTypeChargingGripJoyConR =;
const uint8_t kUsbDeviceTypeProController =;

// During initialization, the current initialization step will be retried if
// the client does not respond within |kTimeoutDuration|. After |kMaxRetryCount|
// retries, initialization is restarted from the first step.
//
// The timeout duration was chosen through experimentation. A shorter duration
// (~1 second) works for Pro controllers, but Joy-Cons sometimes fail to
// initialize correctly.
const base::TimeDelta kTimeoutDuration =;
const size_t kMaxRetryCount =;

const size_t kMaxVibrationEffectDurationMillis =;

// Initialization parameters.
const uint8_t kGyroSensitivity2000Dps =;
const uint8_t kAccelerometerSensitivity8G =;
const uint8_t kGyroPerformance208Hz =;
const uint8_t kAccelerometerFilterBandwidth100Hz =;
const uint8_t kPlayerLightPattern1 =;

// Bogus calibration value that should be ignored.
const uint16_t kCalBogusValue =;

// Default calibration values to use if the controller returns bogus values.
const uint16_t kCalDefaultDeadzone =;
const uint16_t kCalDefaultMin =;
const uint16_t kCalDefaultCenter =;
const uint16_t kCalDefaultMax =;

// Parameters for the "strong" and "weak" components of the dual-rumble effect.
const double kVibrationFrequencyStrongRumble =;
const double kVibrationFrequencyWeakRumble =;
const double kVibrationAmplitudeStrongRumbleMax =;
const double kVibrationAmplitudeWeakRumbleMax =;

const int kVibrationFrequencyHzMin =;
const int kVibrationFrequencyHzMax =;
const int kVibrationAmplitudeMax =;

// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
struct VibrationFrequency {} kVibrationFrequency[] =;
const size_t kVibrationFrequencySize =;

// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
struct VibrationAmplitude {} kVibrationAmplitude[]{};
const size_t kVibrationAmplitudeSize =;

// Define indices for the additional buttons on Switch controllers.
enum SWITCH_BUTTON_INDICES {};

// Input reports with ID 0x81 are replies to commands sent during the
// initialization sequence.
#pragma pack(push, 1)
struct UsbInputReport81 {};
#pragma pack(pop)
static_assert;

// When connected over USB, the initialization sequence includes a step to
// request the MAC address. The MAC is returned in an input report with ID 0x81
// and subtype 0x01.
#pragma pack(push, 1)
struct MacAddressReport {};
#pragma pack(pop)
static_assert;

// When configured for standard full input report mode, controller data is
// reported at regular intervals. The data format is the same for all Switch
// devices, although some buttons are not present on all devices.
#pragma pack(push, 1)
struct ControllerData {};
#pragma pack(pop)
static_assert;

// In standard full input report mode, controller data is reported with IMU data
// in reports with ID 0x30.
#pragma pack(push, 1)
struct ControllerDataReport {};
#pragma pack(pop)
static_assert;

// Responses to SPI read requests are sent in reports with ID 0x21. These
// reports also include controller data.
#pragma pack(push, 1)
struct SpiReadReport {};
#pragma pack(pop)
static_assert;

// Unpack two packed 12-bit values.
void UnpackShorts(uint8_t byte0,
                  uint8_t byte1,
                  uint8_t byte2,
                  uint16_t* short1,
                  uint16_t* short2) {}

// Unpack a 6-byte MAC address.
uint64_t UnpackSwitchMacAddress(const uint8_t* data) {}

// Unpack the analog stick parameters into |cal|.
void UnpackSwitchAnalogStickParameters(
    const uint8_t* data,
    NintendoController::SwitchCalibrationData& cal) {}

// Unpack the IMU calibration data into |cal|
void UnpackSwitchImuCalibration(
    const uint8_t* data,
    NintendoController::SwitchCalibrationData& cal) {}

// Unpack the IMU horizontal offsets into |cal|.
void UnpackSwitchImuHorizontalOffsets(
    const uint8_t* data,
    NintendoController::SwitchCalibrationData& cal) {}

// Unpack the analog stick calibration data into |cal|.
void UnpackSwitchAnalogStickCalibration(
    const uint8_t* data,
    NintendoController::SwitchCalibrationData& cal) {}

// Unpack one frame of IMU data into |imu_data|.
void UnpackSwitchImuData(const uint8_t* data,
                         NintendoController::SwitchImuData* imu_data) {}

// Given joystick input |x|,|y|, apply a radial deadzone with radius
// |dead_zone| centered at |x_center|,|y_center|. If the input is within the
// dead zone region, the value is snapped to the center of the dead zone.
bool ApplyDeadZone(uint16_t& x,
                   uint16_t& y,
                   uint16_t x_center,
                   uint16_t y_center,
                   uint16_t dead_zone) {}

// Normalize |value| to the range [|min|,|max|]. If |value| is outside this
// range, clamp it.
double NormalizeAndClampAxis(int value, int min, int max) {}

// Update the button and axis state in |pad| with the new controller data in
// |data|, using the calibration data |cal|. Returns true if the new data
// differs from the previous data.
bool UpdateGamepadFromControllerData(
    const ControllerData& data,
    const NintendoController::SwitchCalibrationData& cal,
    Gamepad& pad) {}

// Update the state for a single button. The button state is taken from
// the button at index |button_index| in |src_pad|. If this is a composite
// device, |src_pad| holds the state for the left component. If |horizontal| is
// true, the button index is remapped for horizontal orientation before updating
// the state in |dst_pad|.
void UpdateButtonForLeftSide(const Gamepad& src_pad,
                             Gamepad& dst_pad,
                             size_t button_index,
                             bool horizontal) {}

// Update the state for a single button. The button state is taken from
// the button at index |button_index| in |src_pad|. If this is a composite
// device, |src_pad| holds the state for the right component. If |horizontal| is
// true, the button index is remapped for horizontal orientation before updating
// the state in |dst_pad|.
void UpdateButtonForRightSide(const Gamepad& src_pad,
                              Gamepad& dst_pad,
                              size_t button_index,
                              bool horizontal) {}

// Update the state for a single axis. The axis state is taken from the axis at
// index |axis_index| in |src_pad|. If this is a composite device, |src_pad|
// holds the state for the left component. If |horizontal| is true, the axis
// index and value are remapped for horizontal orientation before updating the
// state in |dst_pad|.
void UpdateAxisForLeftSide(const Gamepad& src_pad,
                           Gamepad& dst_pad,
                           size_t axis_index,
                           bool horizontal) {}

// Update the state for a single axis. The axis state is taken from the axis at
// index |axis_index| in |src_pad|. If this is a composite device, |src_pad|
// holds the state for the right component. If |horizontal| is true, the axis
// index and value are remapped for horizontal orientation before updating the
// state in |dst_pad|.
void UpdateAxisForRightSide(const Gamepad& src_pad,
                            Gamepad& dst_pad,
                            size_t axis_index,
                            bool horizontal) {}

// Convert the vibration parameters |frequency| and |amplitude| into a set of
// parameters that can be sent to the vibration actuator.
void FrequencyToHex(float frequency,
                    float amplitude,
                    uint16_t* hf,
                    uint8_t* lf,
                    uint8_t* hf_amp,
                    uint16_t* lf_amp) {}

// Return the bus type of the Switch device described by |device_info|. This is
// needed for Windows which does not report the bus type in the HID API.
GamepadBusType BusTypeFromDeviceInfo(const mojom::HidDeviceInfo* device_info) {}
}  // namespace

NintendoController::SwitchCalibrationData::SwitchCalibrationData() = default;
NintendoController::SwitchCalibrationData::~SwitchCalibrationData() = default;

NintendoController::SwitchImuData::SwitchImuData() = default;
NintendoController::SwitchImuData::~SwitchImuData() = default;

NintendoController::NintendoController(int source_id,
                                       GamepadBusType bus_type,
                                       mojom::HidDeviceInfoPtr device_info,
                                       mojom::HidManager* hid_manager)
    :{}

NintendoController::NintendoController(
    int source_id,
    std::unique_ptr<NintendoController> composite1,
    std::unique_ptr<NintendoController> composite2,
    mojom::HidManager* hid_manager)
    :{}

NintendoController::~NintendoController() = default;

// static
std::unique_ptr<NintendoController> NintendoController::Create(
    int source_id,
    mojom::HidDeviceInfoPtr device_info,
    mojom::HidManager* hid_manager) {}

// static
std::unique_ptr<NintendoController> NintendoController::CreateComposite(
    int source_id,
    std::unique_ptr<NintendoController> composite1,
    std::unique_ptr<NintendoController> composite2,
    mojom::HidManager* hid_manager) {}

// static
bool NintendoController::IsNintendoController(GamepadId gamepad_id) {}

std::vector<std::unique_ptr<NintendoController>>
NintendoController::Decompose() {}

void NintendoController::Open(base::OnceClosure device_ready_closure) {}

GamepadHand NintendoController::GetGamepadHand() const {}

bool NintendoController::IsUsable() const {}

bool NintendoController::HasGuid(const std::string& guid) const {}

GamepadStandardMappingFunction NintendoController::GetMappingFunction() const {}

void NintendoController::InitializeGamepadState(bool has_standard_mapping,
                                                Gamepad& pad) const {}

void NintendoController::UpdatePadConnected() {}

void NintendoController::UpdateGamepadState(Gamepad& pad) const {}

void NintendoController::UpdateLeftGamepadState(Gamepad& pad,
                                                bool horizontal) const {}

void NintendoController::UpdateRightGamepadState(Gamepad& pad,
                                                 bool horizontal) const {}

void NintendoController::Connect(mojom::HidManager::ConnectCallback callback) {}

void NintendoController::OnConnect(
    mojo::PendingRemote<mojom::HidConnection> connection) {}

void NintendoController::StartInitSequence() {}

void NintendoController::FinishInitSequence() {}

void NintendoController::FailInitSequence() {}

void NintendoController::HandleInputReport(
    uint8_t report_id,
    const std::vector<uint8_t>& report_bytes) {}

void NintendoController::HandleUsbInputReport81(
    const std::vector<uint8_t>& report_bytes) {}

void NintendoController::HandleInputReport21(
    const std::vector<uint8_t>& report_bytes) {}

void NintendoController::HandleInputReport30(
    const std::vector<uint8_t>& report_bytes) {}

void NintendoController::ContinueInitSequence(
    uint8_t report_id,
    const std::vector<uint8_t>& report_bytes) {}

void NintendoController::MakeInitSequenceRequests(InitializationState state) {}

void NintendoController::SubCommand(uint8_t sub_command,
                                    const std::vector<uint8_t>& bytes) {}

void NintendoController::RequestMacAddress() {}

void NintendoController::RequestHandshake() {}

void NintendoController::RequestBaudRate() {}

void NintendoController::RequestSubCommand33() {}

void NintendoController::RequestVibration(double left_frequency,
                                          double left_magnitude,
                                          double right_frequency,
                                          double right_magnitude) {}

void NintendoController::RequestEnableUsbTimeout(bool enable) {}

void NintendoController::RequestEnableImu(bool enable) {}

void NintendoController::RequestEnableVibration(bool enable) {}

void NintendoController::RequestSetPlayerLights(uint8_t light_pattern) {}

void NintendoController::RequestSetImuSensitivity(
    uint8_t gyro_sensitivity,
    uint8_t accelerometer_sensitivity,
    uint8_t gyro_performance_rate,
    uint8_t accelerometer_filter_bandwidth) {}

void NintendoController::RequestSetInputReportMode(uint8_t mode) {}

void NintendoController::ReadSpi(uint16_t address, size_t length) {}

void NintendoController::RequestImuCalibration() {}

void NintendoController::RequestHorizontalOffsets() {}

void NintendoController::RequestAnalogCalibration() {}

void NintendoController::RequestAnalogParameters() {}

void NintendoController::ReadInputReport() {}

void NintendoController::OnReadInputReport(
    bool success,
    uint8_t report_id,
    const std::optional<std::vector<uint8_t>>& report_bytes) {}

void NintendoController::WriteOutputReport(
    uint8_t report_id,
    const std::vector<uint8_t>& report_bytes,
    bool expect_reply) {}

void NintendoController::OnWriteOutputReport(bool success) {}

void NintendoController::DoShutdown() {}

void NintendoController::SetVibration(
    mojom::GamepadEffectParametersPtr params) {}

double NintendoController::GetMaxEffectDurationMillis() {}

void NintendoController::ArmTimeout() {}

void NintendoController::CancelTimeout() {}

void NintendoController::OnTimeout() {}

base::WeakPtr<AbstractHapticGamepad> NintendoController::GetWeakPtr() {}

}  // namespace device