common_protocol.h 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. // Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
  4. // engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
  5. // https://github.com/CTCaer/jc_toolkit
  6. // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
  7. #pragma once
  8. #include <memory>
  9. #include <span>
  10. #include <vector>
  11. #include "common/common_types.h"
  12. #include "input_common/helpers/joycon_protocol/joycon_types.h"
  13. namespace InputCommon::Joycon {
  14. /// Joycon driver functions that handle low level communication
  15. class JoyconCommonProtocol {
  16. public:
  17. explicit JoyconCommonProtocol(std::shared_ptr<JoyconHandle> hidapi_handle_);
  18. /**
  19. * Sets handle to blocking. In blocking mode, SDL_hid_read() will wait (block) until there is
  20. * data to read before returning.
  21. */
  22. void SetBlocking();
  23. /**
  24. * Sets handle to non blocking. In non-blocking mode calls to SDL_hid_read() will return
  25. * immediately with a value of 0 if there is no data to be read
  26. */
  27. void SetNonBlocking();
  28. /**
  29. * Sends a request to obtain the joycon type from device
  30. * @returns controller type of the joycon
  31. */
  32. DriverResult GetDeviceType(ControllerType& controller_type);
  33. /**
  34. * Verifies and sets the joycon_handle if device is valid
  35. * @param device info from the driver
  36. * @returns success if the device is valid
  37. */
  38. DriverResult CheckDeviceAccess(SDL_hid_device_info* device);
  39. /**
  40. * Sends a request to set the polling mode of the joycon
  41. * @param report_mode polling mode to be set
  42. */
  43. DriverResult SetReportMode(Joycon::ReportMode report_mode);
  44. /**
  45. * Sends data to the joycon device
  46. * @param buffer data to be send
  47. */
  48. DriverResult SendRawData(std::span<const u8> buffer);
  49. template <typename Output>
  50. requires std::is_trivially_copyable_v<Output>
  51. DriverResult SendData(const Output& output) {
  52. std::array<u8, sizeof(Output)> buffer;
  53. std::memcpy(buffer.data(), &output, sizeof(Output));
  54. return SendRawData(buffer);
  55. }
  56. /**
  57. * Waits for incoming data of the joycon device that matchs the subcommand
  58. * @param sub_command type of data to be returned
  59. * @returns a buffer containing the response
  60. */
  61. DriverResult GetSubCommandResponse(SubCommand sub_command, SubCommandResponse& output);
  62. /**
  63. * Sends a sub command to the device and waits for it's reply
  64. * @param sc sub command to be send
  65. * @param buffer data to be send
  66. * @returns output buffer containing the response
  67. */
  68. DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer,
  69. SubCommandResponse& output);
  70. /**
  71. * Sends a sub command to the device and waits for it's reply and ignores the output
  72. * @param sc sub command to be send
  73. * @param buffer data to be send
  74. */
  75. DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer);
  76. /**
  77. * Sends a mcu command to the device
  78. * @param sc sub command to be send
  79. * @param buffer data to be send
  80. */
  81. DriverResult SendMCUCommand(SubCommand sc, std::span<const u8> buffer);
  82. /**
  83. * Sends vibration data to the joycon
  84. * @param buffer data to be send
  85. */
  86. DriverResult SendVibrationReport(std::span<const u8> buffer);
  87. /**
  88. * Reads the SPI memory stored on the joycon
  89. * @param Initial address location
  90. * @returns output buffer containing the response
  91. */
  92. DriverResult ReadRawSPI(SpiAddress addr, std::span<u8> output);
  93. /**
  94. * Reads the SPI memory stored on the joycon
  95. * @param Initial address location
  96. * @returns output object containing the response
  97. */
  98. template <typename Output>
  99. requires std::is_trivially_copyable_v<Output>
  100. DriverResult ReadSPI(SpiAddress addr, Output& output) {
  101. std::array<u8, sizeof(Output)> buffer;
  102. output = {};
  103. const auto result = ReadRawSPI(addr, buffer);
  104. if (result != DriverResult::Success) {
  105. return result;
  106. }
  107. std::memcpy(&output, buffer.data(), sizeof(Output));
  108. return DriverResult::Success;
  109. }
  110. /**
  111. * Enables MCU chip on the joycon
  112. * @param enable if true the chip will be enabled
  113. */
  114. DriverResult EnableMCU(bool enable);
  115. /**
  116. * Configures the MCU to the correspoinding mode
  117. * @param MCUConfig configuration
  118. */
  119. DriverResult ConfigureMCU(const MCUConfig& config);
  120. /**
  121. * Waits until there's MCU data available. On timeout returns error
  122. * @param report mode of the expected reply
  123. * @returns a buffer containing the response
  124. */
  125. DriverResult GetMCUDataResponse(ReportMode report_mode_, MCUCommandResponse& output);
  126. /**
  127. * Sends data to the MCU chip and waits for it's reply
  128. * @param report mode of the expected reply
  129. * @param sub command to be send
  130. * @param buffer data to be send
  131. * @returns output buffer containing the response
  132. */
  133. DriverResult SendMCUData(ReportMode report_mode, SubCommand sc, std::span<const u8> buffer,
  134. MCUCommandResponse& output);
  135. /**
  136. * Wait's until the MCU chip is on the specified mode
  137. * @param report mode of the expected reply
  138. * @param MCUMode configuration
  139. */
  140. DriverResult WaitSetMCUMode(ReportMode report_mode, MCUMode mode);
  141. /**
  142. * Calculates the checksum from the MCU data
  143. * @param buffer containing the data to be send
  144. * @param size of the buffer in bytes
  145. * @returns byte with the correct checksum
  146. */
  147. u8 CalculateMCU_CRC8(u8* buffer, u8 size) const;
  148. private:
  149. /**
  150. * Increments and returns the packet counter of the handle
  151. * @param joycon_handle device to send the data
  152. * @returns packet counter value
  153. */
  154. u8 GetCounter();
  155. std::shared_ptr<JoyconHandle> hidapi_handle;
  156. };
  157. class ScopedSetBlocking {
  158. public:
  159. explicit ScopedSetBlocking(JoyconCommonProtocol* self) : m_self{self} {
  160. m_self->SetBlocking();
  161. }
  162. ~ScopedSetBlocking() {
  163. m_self->SetNonBlocking();
  164. }
  165. private:
  166. JoyconCommonProtocol* m_self{};
  167. };
  168. } // namespace InputCommon::Joycon