diff options
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/hle/service/ir/extra_hid.cpp | 231 | ||||
| -rw-r--r-- | src/core/hle/service/ir/extra_hid.h | 48 | ||||
| -rw-r--r-- | src/core/hle/service/ir/ir_user.cpp | 489 | ||||
| -rw-r--r-- | src/core/hle/service/ir/ir_user.h | 33 | ||||
| -rw-r--r-- | src/core/settings.cpp | 2 |
6 files changed, 761 insertions, 44 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 61a0b1cc3..a2866fdd8 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -109,6 +109,7 @@ set(SRCS | |||
| 109 | hle/service/hid/hid_spvr.cpp | 109 | hle/service/hid/hid_spvr.cpp |
| 110 | hle/service/hid/hid_user.cpp | 110 | hle/service/hid/hid_user.cpp |
| 111 | hle/service/http_c.cpp | 111 | hle/service/http_c.cpp |
| 112 | hle/service/ir/extra_hid.cpp | ||
| 112 | hle/service/ir/ir.cpp | 113 | hle/service/ir/ir.cpp |
| 113 | hle/service/ir/ir_rst.cpp | 114 | hle/service/ir/ir_rst.cpp |
| 114 | hle/service/ir/ir_u.cpp | 115 | hle/service/ir/ir_u.cpp |
| @@ -297,6 +298,7 @@ set(HEADERS | |||
| 297 | hle/service/hid/hid_spvr.h | 298 | hle/service/hid/hid_spvr.h |
| 298 | hle/service/hid/hid_user.h | 299 | hle/service/hid/hid_user.h |
| 299 | hle/service/http_c.h | 300 | hle/service/http_c.h |
| 301 | hle/service/ir/extra_hid.h | ||
| 300 | hle/service/ir/ir.h | 302 | hle/service/ir/ir.h |
| 301 | hle/service/ir/ir_rst.h | 303 | hle/service/ir/ir_rst.h |
| 302 | hle/service/ir/ir_u.h | 304 | hle/service/ir/ir_u.h |
diff --git a/src/core/hle/service/ir/extra_hid.cpp b/src/core/hle/service/ir/extra_hid.cpp new file mode 100644 index 000000000..e7acc17a5 --- /dev/null +++ b/src/core/hle/service/ir/extra_hid.cpp | |||
| @@ -0,0 +1,231 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/alignment.h" | ||
| 6 | #include "common/bit_field.h" | ||
| 7 | #include "common/string_util.h" | ||
| 8 | #include "core/core_timing.h" | ||
| 9 | #include "core/hle/service/ir/extra_hid.h" | ||
| 10 | #include "core/settings.h" | ||
| 11 | |||
| 12 | namespace Service { | ||
| 13 | namespace IR { | ||
| 14 | |||
| 15 | enum class RequestID : u8 { | ||
| 16 | /** | ||
| 17 | * ConfigureHIDPolling request | ||
| 18 | * Starts HID input polling, or changes the polling interval if it is already started. | ||
| 19 | * Inputs: | ||
| 20 | * byte 0: request ID | ||
| 21 | * byte 1: polling interval in ms | ||
| 22 | * byte 2: unknown | ||
| 23 | */ | ||
| 24 | ConfigureHIDPolling = 1, | ||
| 25 | |||
| 26 | /** | ||
| 27 | * ReadCalibrationData request | ||
| 28 | * Reads the calibration data stored in circle pad pro. | ||
| 29 | * Inputs: | ||
| 30 | * byte 0: request ID | ||
| 31 | * byte 1: expected response time in ms? | ||
| 32 | * byte 2-3: data offset (aligned to 0x10) | ||
| 33 | * byte 4-5: data size (aligned to 0x10) | ||
| 34 | */ | ||
| 35 | ReadCalibrationData = 2, | ||
| 36 | |||
| 37 | // TODO(wwylele): there are three more request types (id = 3, 4 and 5) | ||
| 38 | }; | ||
| 39 | |||
| 40 | enum class ResponseID : u8 { | ||
| 41 | |||
| 42 | /** | ||
| 43 | * PollHID response | ||
| 44 | * Sends current HID status | ||
| 45 | * Output: | ||
| 46 | * byte 0: response ID | ||
| 47 | * byte 1-3: Right circle pad position. This three bytes are two little-endian 12-bit | ||
| 48 | * fields. The first one is for x-axis and the second one is for y-axis. | ||
| 49 | * byte 4: bit[0:4] battery level; bit[5] ZL button; bit[6] ZR button; bit[7] R button | ||
| 50 | * Note that for the three button fields, the bit is set when the button is NOT pressed. | ||
| 51 | * byte 5: unknown | ||
| 52 | */ | ||
| 53 | PollHID = 0x10, | ||
| 54 | |||
| 55 | /** | ||
| 56 | * ReadCalibrationData response | ||
| 57 | * Sends the calibration data reads from circle pad pro. | ||
| 58 | * Output: | ||
| 59 | * byte 0: resonse ID | ||
| 60 | * byte 1-2: data offset (aligned to 0x10) | ||
| 61 | * byte 3-4: data size (aligned to 0x10) | ||
| 62 | * byte 5-...: calibration data | ||
| 63 | */ | ||
| 64 | ReadCalibrationData = 0x11, | ||
| 65 | }; | ||
| 66 | |||
| 67 | ExtraHID::ExtraHID(SendFunc send_func) : IRDevice(send_func) { | ||
| 68 | LoadInputDevices(); | ||
| 69 | |||
| 70 | // The data below was retrieved from a New 3DS | ||
| 71 | // TODO(wwylele): this data is probably writable (via request 3?) and thus should be saved to | ||
| 72 | // and loaded from somewhere. | ||
| 73 | calibration_data = std::array<u8, 0x40>{{ | ||
| 74 | // 0x00 | ||
| 75 | 0x00, 0x00, 0x08, 0x80, 0x85, 0xEB, 0x11, 0x3F, | ||
| 76 | // 0x08 | ||
| 77 | 0x85, 0xEB, 0x11, 0x3F, 0xFF, 0xFF, 0xFF, 0xF5, | ||
| 78 | // 0x10 | ||
| 79 | 0xFF, 0x00, 0x08, 0x80, 0x85, 0xEB, 0x11, 0x3F, | ||
| 80 | // 0x18 | ||
| 81 | 0x85, 0xEB, 0x11, 0x3F, 0xFF, 0xFF, 0xFF, 0x65, | ||
| 82 | // 0x20 | ||
| 83 | 0xFF, 0x00, 0x08, 0x80, 0x85, 0xEB, 0x11, 0x3F, | ||
| 84 | // 0x28 | ||
| 85 | 0x85, 0xEB, 0x11, 0x3F, 0xFF, 0xFF, 0xFF, 0x65, | ||
| 86 | // 0x30 | ||
| 87 | 0xFF, 0x00, 0x08, 0x80, 0x85, 0xEB, 0x11, 0x3F, | ||
| 88 | // 0x38 | ||
| 89 | 0x85, 0xEB, 0x11, 0x3F, 0xFF, 0xFF, 0xFF, 0x65, | ||
| 90 | }}; | ||
| 91 | |||
| 92 | hid_polling_callback_id = | ||
| 93 | CoreTiming::RegisterEvent("ExtraHID::SendHIDStatus", [this](u64, int cycles_late) { | ||
| 94 | SendHIDStatus(); | ||
| 95 | CoreTiming::ScheduleEvent(msToCycles(hid_period) - cycles_late, | ||
| 96 | hid_polling_callback_id); | ||
| 97 | }); | ||
| 98 | } | ||
| 99 | |||
| 100 | ExtraHID::~ExtraHID() { | ||
| 101 | OnDisconnect(); | ||
| 102 | } | ||
| 103 | |||
| 104 | void ExtraHID::OnConnect() {} | ||
| 105 | |||
| 106 | void ExtraHID::OnDisconnect() { | ||
| 107 | CoreTiming::UnscheduleEvent(hid_polling_callback_id, 0); | ||
| 108 | } | ||
| 109 | |||
| 110 | void ExtraHID::HandleConfigureHIDPollingRequest(const std::vector<u8>& request) { | ||
| 111 | if (request.size() != 3) { | ||
| 112 | LOG_ERROR(Service_IR, "Wrong request size (%zu): %s", request.size(), | ||
| 113 | Common::ArrayToString(request.data(), request.size()).c_str()); | ||
| 114 | return; | ||
| 115 | } | ||
| 116 | |||
| 117 | // Change HID input polling interval | ||
| 118 | CoreTiming::UnscheduleEvent(hid_polling_callback_id, 0); | ||
| 119 | hid_period = request[1]; | ||
| 120 | CoreTiming::ScheduleEvent(msToCycles(hid_period), hid_polling_callback_id); | ||
| 121 | } | ||
| 122 | |||
| 123 | void ExtraHID::HandleReadCalibrationDataRequest(const std::vector<u8>& request_buf) { | ||
| 124 | struct ReadCalibrationDataRequest { | ||
| 125 | RequestID request_id; | ||
| 126 | u8 expected_response_time; | ||
| 127 | u16_le offset; | ||
| 128 | u16_le size; | ||
| 129 | }; | ||
| 130 | static_assert(sizeof(ReadCalibrationDataRequest) == 6, | ||
| 131 | "ReadCalibrationDataRequest has wrong size"); | ||
| 132 | |||
| 133 | if (request_buf.size() != sizeof(ReadCalibrationDataRequest)) { | ||
| 134 | LOG_ERROR(Service_IR, "Wrong request size (%zu): %s", request_buf.size(), | ||
| 135 | Common::ArrayToString(request_buf.data(), request_buf.size()).c_str()); | ||
| 136 | return; | ||
| 137 | } | ||
| 138 | |||
| 139 | ReadCalibrationDataRequest request; | ||
| 140 | std::memcpy(&request, request_buf.data(), sizeof(request)); | ||
| 141 | |||
| 142 | const u16 offset = Common::AlignDown(request.offset, 16); | ||
| 143 | const u16 size = Common::AlignDown(request.size, 16); | ||
| 144 | |||
| 145 | if (offset + size > calibration_data.size()) { | ||
| 146 | LOG_ERROR(Service_IR, "Read beyond the end of calibration data! (offset=%u, size=%u)", | ||
| 147 | offset, size); | ||
| 148 | return; | ||
| 149 | } | ||
| 150 | |||
| 151 | std::vector<u8> response(5); | ||
| 152 | response[0] = static_cast<u8>(ResponseID::ReadCalibrationData); | ||
| 153 | std::memcpy(&response[1], &request.offset, sizeof(request.offset)); | ||
| 154 | std::memcpy(&response[3], &request.size, sizeof(request.size)); | ||
| 155 | response.insert(response.end(), calibration_data.begin() + offset, | ||
| 156 | calibration_data.begin() + offset + size); | ||
| 157 | Send(response); | ||
| 158 | } | ||
| 159 | |||
| 160 | void ExtraHID::OnReceive(const std::vector<u8>& data) { | ||
| 161 | switch (static_cast<RequestID>(data[0])) { | ||
| 162 | case RequestID::ConfigureHIDPolling: | ||
| 163 | HandleConfigureHIDPollingRequest(data); | ||
| 164 | break; | ||
| 165 | case RequestID::ReadCalibrationData: | ||
| 166 | HandleReadCalibrationDataRequest(data); | ||
| 167 | break; | ||
| 168 | default: | ||
| 169 | LOG_ERROR(Service_IR, "Unknown request: %s", | ||
| 170 | Common::ArrayToString(data.data(), data.size()).c_str()); | ||
| 171 | break; | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | void ExtraHID::SendHIDStatus() { | ||
| 176 | if (is_device_reload_pending.exchange(false)) | ||
| 177 | LoadInputDevices(); | ||
| 178 | |||
| 179 | struct { | ||
| 180 | union { | ||
| 181 | BitField<0, 8, u32_le> header; | ||
| 182 | BitField<8, 12, u32_le> c_stick_x; | ||
| 183 | BitField<20, 12, u32_le> c_stick_y; | ||
| 184 | } c_stick; | ||
| 185 | union { | ||
| 186 | BitField<0, 5, u8> battery_level; | ||
| 187 | BitField<5, 1, u8> zl_not_held; | ||
| 188 | BitField<6, 1, u8> zr_not_held; | ||
| 189 | BitField<7, 1, u8> r_not_held; | ||
| 190 | } buttons; | ||
| 191 | u8 unknown; | ||
| 192 | } response; | ||
| 193 | static_assert(sizeof(response) == 6, "HID status response has wrong size!"); | ||
| 194 | |||
| 195 | constexpr int C_STICK_CENTER = 0x800; | ||
| 196 | // TODO(wwylele): this value is not accurately measured. We currently assume that the axis can | ||
| 197 | // take values in the whole range of a 12-bit integer. | ||
| 198 | constexpr int C_STICK_RADIUS = 0x7FF; | ||
| 199 | |||
| 200 | float x, y; | ||
| 201 | std::tie(x, y) = c_stick->GetStatus(); | ||
| 202 | |||
| 203 | response.c_stick.header.Assign(static_cast<u8>(ResponseID::PollHID)); | ||
| 204 | response.c_stick.c_stick_x.Assign(static_cast<u32>(C_STICK_CENTER + C_STICK_RADIUS * x)); | ||
| 205 | response.c_stick.c_stick_y.Assign(static_cast<u32>(C_STICK_CENTER + C_STICK_RADIUS * y)); | ||
| 206 | response.buttons.battery_level.Assign(0x1F); | ||
| 207 | response.buttons.zl_not_held.Assign(!zl->GetStatus()); | ||
| 208 | response.buttons.zr_not_held.Assign(!zr->GetStatus()); | ||
| 209 | response.buttons.r_not_held.Assign(1); | ||
| 210 | response.unknown = 0; | ||
| 211 | |||
| 212 | std::vector<u8> response_buffer(sizeof(response)); | ||
| 213 | memcpy(response_buffer.data(), &response, sizeof(response)); | ||
| 214 | Send(response_buffer); | ||
| 215 | } | ||
| 216 | |||
| 217 | void ExtraHID::RequestInputDevicesReload() { | ||
| 218 | is_device_reload_pending.store(true); | ||
| 219 | } | ||
| 220 | |||
| 221 | void ExtraHID::LoadInputDevices() { | ||
| 222 | zl = Input::CreateDevice<Input::ButtonDevice>( | ||
| 223 | Settings::values.buttons[Settings::NativeButton::ZL]); | ||
| 224 | zr = Input::CreateDevice<Input::ButtonDevice>( | ||
| 225 | Settings::values.buttons[Settings::NativeButton::ZR]); | ||
| 226 | c_stick = Input::CreateDevice<Input::AnalogDevice>( | ||
| 227 | Settings::values.analogs[Settings::NativeAnalog::CStick]); | ||
| 228 | } | ||
| 229 | |||
| 230 | } // namespace IR | ||
| 231 | } // namespace Service | ||
diff --git a/src/core/hle/service/ir/extra_hid.h b/src/core/hle/service/ir/extra_hid.h new file mode 100644 index 000000000..a2459a73a --- /dev/null +++ b/src/core/hle/service/ir/extra_hid.h | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <atomic> | ||
| 9 | #include "core/frontend/input.h" | ||
| 10 | #include "core/hle/service/ir/ir_user.h" | ||
| 11 | |||
| 12 | namespace Service { | ||
| 13 | namespace IR { | ||
| 14 | |||
| 15 | /** | ||
| 16 | * An IRDevice emulating Circle Pad Pro or New 3DS additional HID hardware. | ||
| 17 | * This device sends periodic udates at a rate configured by the 3DS, and sends calibration data if | ||
| 18 | * requested. | ||
| 19 | */ | ||
| 20 | class ExtraHID final : public IRDevice { | ||
| 21 | public: | ||
| 22 | explicit ExtraHID(SendFunc send_func); | ||
| 23 | ~ExtraHID(); | ||
| 24 | |||
| 25 | void OnConnect() override; | ||
| 26 | void OnDisconnect() override; | ||
| 27 | void OnReceive(const std::vector<u8>& data) override; | ||
| 28 | |||
| 29 | /// Requests input devices reload from current settings. Called when the input settings change. | ||
| 30 | void RequestInputDevicesReload(); | ||
| 31 | |||
| 32 | private: | ||
| 33 | void SendHIDStatus(); | ||
| 34 | void HandleConfigureHIDPollingRequest(const std::vector<u8>& request); | ||
| 35 | void HandleReadCalibrationDataRequest(const std::vector<u8>& request); | ||
| 36 | void LoadInputDevices(); | ||
| 37 | |||
| 38 | u8 hid_period; | ||
| 39 | int hid_polling_callback_id; | ||
| 40 | std::array<u8, 0x40> calibration_data; | ||
| 41 | std::unique_ptr<Input::ButtonDevice> zl; | ||
| 42 | std::unique_ptr<Input::ButtonDevice> zr; | ||
| 43 | std::unique_ptr<Input::AnalogDevice> c_stick; | ||
| 44 | std::atomic<bool> is_device_reload_pending; | ||
| 45 | }; | ||
| 46 | |||
| 47 | } // namespace IR | ||
| 48 | } // namespace Service | ||
diff --git a/src/core/hle/service/ir/ir_user.cpp b/src/core/hle/service/ir/ir_user.cpp index b326d7fc7..bccf6bce7 100644 --- a/src/core/hle/service/ir/ir_user.cpp +++ b/src/core/hle/service/ir/ir_user.cpp | |||
| @@ -2,110 +2,481 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <memory> | ||
| 6 | #include <boost/crc.hpp> | ||
| 7 | #include <boost/optional.hpp> | ||
| 8 | #include "common/string_util.h" | ||
| 9 | #include "common/swap.h" | ||
| 5 | #include "core/hle/kernel/event.h" | 10 | #include "core/hle/kernel/event.h" |
| 6 | #include "core/hle/kernel/shared_memory.h" | 11 | #include "core/hle/kernel/shared_memory.h" |
| 12 | #include "core/hle/service/ir/extra_hid.h" | ||
| 7 | #include "core/hle/service/ir/ir.h" | 13 | #include "core/hle/service/ir/ir.h" |
| 8 | #include "core/hle/service/ir/ir_user.h" | 14 | #include "core/hle/service/ir/ir_user.h" |
| 9 | 15 | ||
| 10 | namespace Service { | 16 | namespace Service { |
| 11 | namespace IR { | 17 | namespace IR { |
| 12 | 18 | ||
| 13 | static Kernel::SharedPtr<Kernel::Event> conn_status_event; | 19 | // This is a header that will present in the ir:USER shared memory if it is initialized with |
| 14 | static Kernel::SharedPtr<Kernel::SharedMemory> transfer_shared_memory; | 20 | // InitializeIrNopShared service function. Otherwise the shared memory doesn't have this header if |
| 21 | // it is initialized with InitializeIrNop service function. | ||
| 22 | struct SharedMemoryHeader { | ||
| 23 | u32_le latest_receive_error_result; | ||
| 24 | u32_le latest_send_error_result; | ||
| 25 | // TODO(wwylele): for these fields below, make them enum when the meaning of values is known. | ||
| 26 | u8 connection_status; | ||
| 27 | u8 trying_to_connect_status; | ||
| 28 | u8 connection_role; | ||
| 29 | u8 machine_id; | ||
| 30 | u8 connected; | ||
| 31 | u8 network_id; | ||
| 32 | u8 initialized; | ||
| 33 | u8 unknown; | ||
| 34 | |||
| 35 | // This is not the end of the shared memory. It is followed by a receive buffer and a send | ||
| 36 | // buffer. We handle receive buffer in the BufferManager class. For the send buffer, because | ||
| 37 | // games usually don't access it, we don't emulate it. | ||
| 38 | }; | ||
| 39 | static_assert(sizeof(SharedMemoryHeader) == 16, "SharedMemoryHeader has wrong size!"); | ||
| 40 | |||
| 41 | /** | ||
| 42 | * A manager of the send/receive buffers in the shared memory. Currently it is only used for the | ||
| 43 | * receive buffer. | ||
| 44 | * | ||
| 45 | * A buffer consists of three parts: | ||
| 46 | * - BufferInfo: stores available count of packets, and their position in the PacketInfo | ||
| 47 | * circular queue. | ||
| 48 | * - PacketInfo circular queue: stores the position of each avaiable packets in the Packet data | ||
| 49 | * buffer. Each entry is a pair of {offset, size}. | ||
| 50 | * - Packet data circular buffer: stores the actual data of packets. | ||
| 51 | * | ||
| 52 | * IR packets can be put into and get from the buffer. | ||
| 53 | * | ||
| 54 | * When a new packet is put into the buffer, its data is put into the data circular buffer, | ||
| 55 | * following the end of previous packet data. A new entry is also added to the PacketInfo circular | ||
| 56 | * queue pointing to the added packet data. Then BufferInfo is updated. | ||
| 57 | * | ||
| 58 | * Packets can be released from the other end of the buffer. When releasing a packet, the front | ||
| 59 | * entry in thePacketInfo circular queue is removed, and as a result the corresponding memory in the | ||
| 60 | * data circular buffer is also released. BufferInfo is updated as well. | ||
| 61 | * | ||
| 62 | * The client application usually has a similar manager constructed over the same shared memory | ||
| 63 | * region, performing the same put/get/release operation. This way the client and the service | ||
| 64 | * communicate via a pair of manager of the same buffer. | ||
| 65 | * | ||
| 66 | * TODO(wwylele): implement Get function, which is used by ReceiveIrnop service function. | ||
| 67 | */ | ||
| 68 | class BufferManager { | ||
| 69 | public: | ||
| 70 | BufferManager(Kernel::SharedPtr<Kernel::SharedMemory> shared_memory_, u32 info_offset_, | ||
| 71 | u32 buffer_offset_, u32 max_packet_count_, u32 buffer_size) | ||
| 72 | : shared_memory(shared_memory_), info_offset(info_offset_), buffer_offset(buffer_offset_), | ||
| 73 | max_packet_count(max_packet_count_), | ||
| 74 | max_data_size(buffer_size - sizeof(PacketInfo) * max_packet_count_) { | ||
| 75 | UpdateBufferInfo(); | ||
| 76 | } | ||
| 77 | |||
| 78 | /** | ||
| 79 | * Puts a packet to the head of the buffer. | ||
| 80 | * @params packet The data of the packet to put. | ||
| 81 | * @returns whether the operation is successful. | ||
| 82 | */ | ||
| 83 | bool Put(const std::vector<u8>& packet) { | ||
| 84 | if (info.packet_count == max_packet_count) | ||
| 85 | return false; | ||
| 86 | |||
| 87 | u32 write_offset; | ||
| 88 | |||
| 89 | // finds free space offset in data buffer | ||
| 90 | if (info.packet_count == 0) { | ||
| 91 | write_offset = 0; | ||
| 92 | if (packet.size() > max_data_size) | ||
| 93 | return false; | ||
| 94 | } else { | ||
| 95 | const u32 last_index = (info.end_index + max_packet_count - 1) % max_packet_count; | ||
| 96 | const PacketInfo first = GetPacketInfo(info.begin_index); | ||
| 97 | const PacketInfo last = GetPacketInfo(last_index); | ||
| 98 | write_offset = (last.offset + last.size) % max_data_size; | ||
| 99 | const u32 free_space = (first.offset + max_data_size - write_offset) % max_data_size; | ||
| 100 | if (packet.size() > free_space) | ||
| 101 | return false; | ||
| 102 | } | ||
| 103 | |||
| 104 | // writes packet info | ||
| 105 | PacketInfo packet_info{write_offset, static_cast<u32>(packet.size())}; | ||
| 106 | SetPacketInfo(info.end_index, packet_info); | ||
| 107 | |||
| 108 | // writes packet data | ||
| 109 | for (size_t i = 0; i < packet.size(); ++i) { | ||
| 110 | *GetDataBufferPointer((write_offset + i) % max_data_size) = packet[i]; | ||
| 111 | } | ||
| 112 | |||
| 113 | // updates buffer info | ||
| 114 | info.end_index++; | ||
| 115 | info.end_index %= max_packet_count; | ||
| 116 | info.packet_count++; | ||
| 117 | UpdateBufferInfo(); | ||
| 118 | return true; | ||
| 119 | } | ||
| 120 | |||
| 121 | /** | ||
| 122 | * Release packets from the tail of the buffer | ||
| 123 | * @params count Numbers of packets to release. | ||
| 124 | * @returns whether the operation is successful. | ||
| 125 | */ | ||
| 126 | bool Release(u32 count) { | ||
| 127 | if (info.packet_count < count) | ||
| 128 | return false; | ||
| 129 | |||
| 130 | info.packet_count -= count; | ||
| 131 | info.begin_index += count; | ||
| 132 | info.begin_index %= max_packet_count; | ||
| 133 | UpdateBufferInfo(); | ||
| 134 | return true; | ||
| 135 | } | ||
| 136 | |||
| 137 | private: | ||
| 138 | struct BufferInfo { | ||
| 139 | u32_le begin_index; | ||
| 140 | u32_le end_index; | ||
| 141 | u32_le packet_count; | ||
| 142 | u32_le unknown; | ||
| 143 | }; | ||
| 144 | static_assert(sizeof(BufferInfo) == 16, "BufferInfo has wrong size!"); | ||
| 145 | |||
| 146 | struct PacketInfo { | ||
| 147 | u32_le offset; | ||
| 148 | u32_le size; | ||
| 149 | }; | ||
| 150 | static_assert(sizeof(PacketInfo) == 8, "PacketInfo has wrong size!"); | ||
| 151 | |||
| 152 | u8* GetPacketInfoPointer(u32 index) { | ||
| 153 | return shared_memory->GetPointer(buffer_offset + sizeof(PacketInfo) * index); | ||
| 154 | } | ||
| 155 | |||
| 156 | void SetPacketInfo(u32 index, const PacketInfo& packet_info) { | ||
| 157 | memcpy(GetPacketInfoPointer(index), &packet_info, sizeof(PacketInfo)); | ||
| 158 | } | ||
| 159 | |||
| 160 | PacketInfo GetPacketInfo(u32 index) { | ||
| 161 | PacketInfo packet_info; | ||
| 162 | memcpy(&packet_info, GetPacketInfoPointer(index), sizeof(PacketInfo)); | ||
| 163 | return packet_info; | ||
| 164 | } | ||
| 165 | |||
| 166 | u8* GetDataBufferPointer(u32 offset) { | ||
| 167 | return shared_memory->GetPointer(buffer_offset + sizeof(PacketInfo) * max_packet_count + | ||
| 168 | offset); | ||
| 169 | } | ||
| 170 | |||
| 171 | void UpdateBufferInfo() { | ||
| 172 | if (info_offset) { | ||
| 173 | memcpy(shared_memory->GetPointer(info_offset), &info, sizeof(info)); | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | BufferInfo info{0, 0, 0, 0}; | ||
| 178 | Kernel::SharedPtr<Kernel::SharedMemory> shared_memory; | ||
| 179 | u32 info_offset; | ||
| 180 | u32 buffer_offset; | ||
| 181 | u32 max_packet_count; | ||
| 182 | u32 max_data_size; | ||
| 183 | }; | ||
| 184 | |||
| 185 | static Kernel::SharedPtr<Kernel::Event> conn_status_event, send_event, receive_event; | ||
| 186 | static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory; | ||
| 187 | static std::unique_ptr<ExtraHID> extra_hid; | ||
| 188 | static IRDevice* connected_device; | ||
| 189 | static boost::optional<BufferManager> receive_buffer; | ||
| 190 | |||
| 191 | /// Wraps the payload into packet and puts it to the receive buffer | ||
| 192 | static void PutToReceive(const std::vector<u8>& payload) { | ||
| 193 | LOG_TRACE(Service_IR, "called, data=%s", | ||
| 194 | Common::ArrayToString(payload.data(), payload.size()).c_str()); | ||
| 195 | size_t size = payload.size(); | ||
| 196 | |||
| 197 | std::vector<u8> packet; | ||
| 198 | |||
| 199 | // Builds packet header. For the format info: | ||
| 200 | // https://www.3dbrew.org/wiki/IRUSER_Shared_Memory#Packet_structure | ||
| 201 | |||
| 202 | // fixed value | ||
| 203 | packet.push_back(0xA5); | ||
| 204 | // destination network ID | ||
| 205 | u8 network_id = *(shared_memory->GetPointer(offsetof(SharedMemoryHeader, network_id))); | ||
| 206 | packet.push_back(network_id); | ||
| 207 | |||
| 208 | // puts the size info. | ||
| 209 | // The highest bit of the first byte is unknown, which is set to zero here. The second highest | ||
| 210 | // bit is a flag that determines whether the size info is in extended form. If the packet size | ||
| 211 | // can be represent within 6 bits, the short form (1 byte) of size info is chosen, the size is | ||
| 212 | // put to the lower bits of this byte, and the flag is clear. If the packet size cannot be | ||
| 213 | // represent within 6 bits, the extended form (2 bytes) is chosen, the lower 8 bits of the size | ||
| 214 | // is put to the second byte, the higher bits of the size is put to the lower bits of the first | ||
| 215 | // byte, and the flag is set. Note that the packet size must be within 14 bits due to this | ||
| 216 | // format restriction, or it will overlap with the flag bit. | ||
| 217 | if (size < 0x40) { | ||
| 218 | packet.push_back(static_cast<u8>(size)); | ||
| 219 | } else if (size < 0x4000) { | ||
| 220 | packet.push_back(static_cast<u8>(size >> 8) | 0x40); | ||
| 221 | packet.push_back(static_cast<u8>(size)); | ||
| 222 | } else { | ||
| 223 | ASSERT(false); | ||
| 224 | } | ||
| 225 | |||
| 226 | // puts the payload | ||
| 227 | packet.insert(packet.end(), payload.begin(), payload.end()); | ||
| 228 | |||
| 229 | // calculates CRC and puts to the end | ||
| 230 | packet.push_back(boost::crc<8, 0x07, 0, 0, false, false>(packet.data(), packet.size())); | ||
| 231 | |||
| 232 | if (receive_buffer->Put(packet)) { | ||
| 233 | receive_event->Signal(); | ||
| 234 | } else { | ||
| 235 | LOG_ERROR(Service_IR, "receive buffer is full!"); | ||
| 236 | } | ||
| 237 | } | ||
| 15 | 238 | ||
| 16 | /** | 239 | /** |
| 17 | * IR::InitializeIrNopShared service function | 240 | * IR::InitializeIrNopShared service function |
| 241 | * Initializes ir:USER service with a user provided shared memory. The shared memory is configured | ||
| 242 | * to shared mode (with SharedMemoryHeader at the beginning of the shared memory). | ||
| 18 | * Inputs: | 243 | * Inputs: |
| 19 | * 1 : Size of transfer buffer | 244 | * 1 : Size of shared memory |
| 20 | * 2 : Recv buffer size | 245 | * 2 : Recv buffer size |
| 21 | * 3 : unknown | 246 | * 3 : Recv buffer packet count |
| 22 | * 4 : Send buffer size | 247 | * 4 : Send buffer size |
| 23 | * 5 : unknown | 248 | * 5 : Send buffer packet count |
| 24 | * 6 : BaudRate (u8) | 249 | * 6 : BaudRate (u8) |
| 25 | * 7 : 0 | 250 | * 7 : 0 (Handle descriptor) |
| 26 | * 8 : Handle of transfer shared memory | 251 | * 8 : Handle of shared memory |
| 27 | * Outputs: | 252 | * Outputs: |
| 28 | * 1 : Result of function, 0 on success, otherwise error code | 253 | * 1 : Result of function, 0 on success, otherwise error code |
| 29 | */ | 254 | */ |
| 30 | static void InitializeIrNopShared(Interface* self) { | 255 | static void InitializeIrNopShared(Interface* self) { |
| 31 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 256 | IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x18, 6, 2); |
| 257 | const u32 shared_buff_size = rp.Pop<u32>(); | ||
| 258 | const u32 recv_buff_size = rp.Pop<u32>(); | ||
| 259 | const u32 recv_buff_packet_count = rp.Pop<u32>(); | ||
| 260 | const u32 send_buff_size = rp.Pop<u32>(); | ||
| 261 | const u32 send_buff_packet_count = rp.Pop<u32>(); | ||
| 262 | const u8 baud_rate = rp.Pop<u8>(); | ||
| 263 | const Kernel::Handle handle = rp.PopHandle(); | ||
| 32 | 264 | ||
| 33 | u32 transfer_buff_size = cmd_buff[1]; | 265 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |
| 34 | u32 recv_buff_size = cmd_buff[2]; | ||
| 35 | u32 unk1 = cmd_buff[3]; | ||
| 36 | u32 send_buff_size = cmd_buff[4]; | ||
| 37 | u32 unk2 = cmd_buff[5]; | ||
| 38 | u8 baud_rate = cmd_buff[6] & 0xFF; | ||
| 39 | Kernel::Handle handle = cmd_buff[8]; | ||
| 40 | 266 | ||
| 41 | if (Kernel::g_handle_table.IsValid(handle)) { | 267 | shared_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(handle); |
| 42 | transfer_shared_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(handle); | 268 | if (!shared_memory) { |
| 43 | transfer_shared_memory->name = "IR:TransferSharedMemory"; | 269 | LOG_CRITICAL(Service_IR, "invalid shared memory handle 0x%08X", handle); |
| 270 | rb.Push(ResultCode(ErrorDescription::InvalidHandle, ErrorModule::OS, | ||
| 271 | ErrorSummary::WrongArgument, ErrorLevel::Permanent)); | ||
| 272 | return; | ||
| 44 | } | 273 | } |
| 274 | shared_memory->name = "IR_USER: shared memory"; | ||
| 45 | 275 | ||
| 46 | cmd_buff[1] = RESULT_SUCCESS.raw; | 276 | receive_buffer = |
| 277 | BufferManager(shared_memory, 0x10, 0x20, recv_buff_packet_count, recv_buff_size); | ||
| 278 | SharedMemoryHeader shared_memory_init{}; | ||
| 279 | shared_memory_init.initialized = 1; | ||
| 280 | std::memcpy(shared_memory->GetPointer(), &shared_memory_init, sizeof(SharedMemoryHeader)); | ||
| 47 | 281 | ||
| 48 | LOG_WARNING(Service_IR, "(STUBBED) called, transfer_buff_size=%d, recv_buff_size=%d, " | 282 | rb.Push(RESULT_SUCCESS); |
| 49 | "unk1=%d, send_buff_size=%d, unk2=%d, baud_rate=%u, handle=0x%08X", | 283 | |
| 50 | transfer_buff_size, recv_buff_size, unk1, send_buff_size, unk2, baud_rate, handle); | 284 | LOG_INFO(Service_IR, "called, shared_buff_size=%u, recv_buff_size=%u, " |
| 285 | "recv_buff_packet_count=%u, send_buff_size=%u, " | ||
| 286 | "send_buff_packet_count=%u, baud_rate=%u, handle=0x%08X", | ||
| 287 | shared_buff_size, recv_buff_size, recv_buff_packet_count, send_buff_size, | ||
| 288 | send_buff_packet_count, baud_rate, handle); | ||
| 51 | } | 289 | } |
| 52 | 290 | ||
| 53 | /** | 291 | /** |
| 54 | * IR::RequireConnection service function | 292 | * IR::RequireConnection service function |
| 293 | * Searches for an IR device and connects to it. After connecting to the device, applications can | ||
| 294 | * use SendIrNop function, ReceiveIrNop function (or read from the buffer directly) to communicate | ||
| 295 | * with the device. | ||
| 55 | * Inputs: | 296 | * Inputs: |
| 56 | * 1 : unknown (u8), looks like always 1 | 297 | * 1 : device ID? always 1 for circle pad pro |
| 57 | * Outputs: | 298 | * Outputs: |
| 58 | * 1 : Result of function, 0 on success, otherwise error code | 299 | * 1 : Result of function, 0 on success, otherwise error code |
| 59 | */ | 300 | */ |
| 60 | static void RequireConnection(Interface* self) { | 301 | static void RequireConnection(Interface* self) { |
| 61 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 302 | IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x06, 1, 0); |
| 303 | const u8 device_id = rp.Pop<u8>(); | ||
| 304 | |||
| 305 | u8* shared_memory_ptr = shared_memory->GetPointer(); | ||
| 306 | if (device_id == 1) { | ||
| 307 | // These values are observed on a New 3DS. The meaning of them is unclear. | ||
| 308 | // TODO (wwylele): should assign network_id a (random?) number | ||
| 309 | shared_memory_ptr[offsetof(SharedMemoryHeader, connection_status)] = 2; | ||
| 310 | shared_memory_ptr[offsetof(SharedMemoryHeader, connection_role)] = 2; | ||
| 311 | shared_memory_ptr[offsetof(SharedMemoryHeader, connected)] = 1; | ||
| 312 | |||
| 313 | connected_device = extra_hid.get(); | ||
| 314 | connected_device->OnConnect(); | ||
| 315 | conn_status_event->Signal(); | ||
| 316 | } else { | ||
| 317 | LOG_WARNING(Service_IR, "unknown device id %u. Won't connect.", device_id); | ||
| 318 | shared_memory_ptr[offsetof(SharedMemoryHeader, connection_status)] = 1; | ||
| 319 | shared_memory_ptr[offsetof(SharedMemoryHeader, trying_to_connect_status)] = 2; | ||
| 320 | } | ||
| 321 | |||
| 322 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||
| 323 | rb.Push(RESULT_SUCCESS); | ||
| 324 | |||
| 325 | LOG_INFO(Service_IR, "called, device_id = %u", device_id); | ||
| 326 | } | ||
| 327 | |||
| 328 | /** | ||
| 329 | * IR::GetReceiveEvent service function | ||
| 330 | * Gets an event that is signaled when a packet is received from the IR device. | ||
| 331 | * Outputs: | ||
| 332 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 333 | * 2 : 0 (Handle descriptor) | ||
| 334 | * 3 : Receive event handle | ||
| 335 | */ | ||
| 336 | void GetReceiveEvent(Interface* self) { | ||
| 337 | IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0A, 1, 2); | ||
| 62 | 338 | ||
| 63 | conn_status_event->Signal(); | 339 | rb.Push(RESULT_SUCCESS); |
| 340 | rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::receive_event).MoveFrom()); | ||
| 341 | |||
| 342 | LOG_INFO(Service_IR, "called"); | ||
| 343 | } | ||
| 344 | |||
| 345 | /** | ||
| 346 | * IR::GetSendEvent service function | ||
| 347 | * Gets an event that is signaled when the sending of a packet is complete | ||
| 348 | * Outputs: | ||
| 349 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 350 | * 2 : 0 (Handle descriptor) | ||
| 351 | * 3 : Send event handle | ||
| 352 | */ | ||
| 353 | void GetSendEvent(Interface* self) { | ||
| 354 | IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0B, 1, 2); | ||
| 64 | 355 | ||
| 65 | cmd_buff[1] = RESULT_SUCCESS.raw; | 356 | rb.Push(RESULT_SUCCESS); |
| 357 | rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::send_event).MoveFrom()); | ||
| 66 | 358 | ||
| 67 | LOG_WARNING(Service_IR, "(STUBBED) called"); | 359 | LOG_INFO(Service_IR, "called"); |
| 68 | } | 360 | } |
| 69 | 361 | ||
| 70 | /** | 362 | /** |
| 71 | * IR::Disconnect service function | 363 | * IR::Disconnect service function |
| 364 | * Disconnects from the current connected IR device. | ||
| 72 | * Outputs: | 365 | * Outputs: |
| 73 | * 1 : Result of function, 0 on success, otherwise error code | 366 | * 1 : Result of function, 0 on success, otherwise error code |
| 74 | */ | 367 | */ |
| 75 | static void Disconnect(Interface* self) { | 368 | static void Disconnect(Interface* self) { |
| 76 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 369 | if (connected_device) { |
| 370 | connected_device->OnDisconnect(); | ||
| 371 | connected_device = nullptr; | ||
| 372 | conn_status_event->Signal(); | ||
| 373 | } | ||
| 374 | |||
| 375 | u8* shared_memory_ptr = shared_memory->GetPointer(); | ||
| 376 | shared_memory_ptr[offsetof(SharedMemoryHeader, connection_status)] = 0; | ||
| 377 | shared_memory_ptr[offsetof(SharedMemoryHeader, connected)] = 0; | ||
| 77 | 378 | ||
| 78 | cmd_buff[1] = RESULT_SUCCESS.raw; | 379 | IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x09, 1, 0); |
| 380 | rb.Push(RESULT_SUCCESS); | ||
| 79 | 381 | ||
| 80 | LOG_WARNING(Service_IR, "(STUBBED) called"); | 382 | LOG_INFO(Service_IR, "called"); |
| 81 | } | 383 | } |
| 82 | 384 | ||
| 83 | /** | 385 | /** |
| 84 | * IR::GetConnectionStatusEvent service function | 386 | * IR::GetConnectionStatusEvent service function |
| 387 | * Gets an event that is signaled when the connection status is changed | ||
| 85 | * Outputs: | 388 | * Outputs: |
| 86 | * 1 : Result of function, 0 on success, otherwise error code | 389 | * 1 : Result of function, 0 on success, otherwise error code |
| 87 | * 2 : Connection Status Event handle | 390 | * 2 : 0 (Handle descriptor) |
| 391 | * 3 : Connection Status Event handle | ||
| 88 | */ | 392 | */ |
| 89 | static void GetConnectionStatusEvent(Interface* self) { | 393 | static void GetConnectionStatusEvent(Interface* self) { |
| 90 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 394 | IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0C, 1, 2); |
| 91 | 395 | ||
| 92 | cmd_buff[1] = RESULT_SUCCESS.raw; | 396 | rb.Push(RESULT_SUCCESS); |
| 93 | cmd_buff[3] = Kernel::g_handle_table.Create(Service::IR::conn_status_event).MoveFrom(); | 397 | rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::conn_status_event).MoveFrom()); |
| 94 | 398 | ||
| 95 | LOG_WARNING(Service_IR, "(STUBBED) called"); | 399 | LOG_INFO(Service_IR, "called"); |
| 96 | } | 400 | } |
| 97 | 401 | ||
| 98 | /** | 402 | /** |
| 99 | * IR::FinalizeIrNop service function | 403 | * IR::FinalizeIrNop service function |
| 404 | * Finalize ir:USER service. | ||
| 100 | * Outputs: | 405 | * Outputs: |
| 101 | * 1 : Result of function, 0 on success, otherwise error code | 406 | * 1 : Result of function, 0 on success, otherwise error code |
| 102 | */ | 407 | */ |
| 103 | static void FinalizeIrNop(Interface* self) { | 408 | static void FinalizeIrNop(Interface* self) { |
| 104 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 409 | if (connected_device) { |
| 410 | connected_device->OnDisconnect(); | ||
| 411 | connected_device = nullptr; | ||
| 412 | } | ||
| 413 | |||
| 414 | shared_memory = nullptr; | ||
| 415 | receive_buffer = boost::none; | ||
| 416 | |||
| 417 | IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x02, 1, 0); | ||
| 418 | rb.Push(RESULT_SUCCESS); | ||
| 419 | |||
| 420 | LOG_INFO(Service_IR, "called"); | ||
| 421 | } | ||
| 422 | |||
| 423 | /** | ||
| 424 | * IR::SendIrNop service function | ||
| 425 | * Sends a packet to the connected IR device | ||
| 426 | * Inpus: | ||
| 427 | * 1 : Size of data to send | ||
| 428 | * 2 : 2 + (size << 14) (Static buffer descriptor) | ||
| 429 | * 3 : Data buffer address | ||
| 430 | * Outputs: | ||
| 431 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 432 | */ | ||
| 433 | static void SendIrNop(Interface* self) { | ||
| 434 | IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0D, 1, 2); | ||
| 435 | const u32 size = rp.Pop<u32>(); | ||
| 436 | const VAddr address = rp.PopStaticBuffer(); | ||
| 437 | |||
| 438 | std::vector<u8> buffer(size); | ||
| 439 | Memory::ReadBlock(address, buffer.data(), size); | ||
| 440 | |||
| 441 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||
| 442 | if (connected_device) { | ||
| 443 | connected_device->OnReceive(buffer); | ||
| 444 | send_event->Signal(); | ||
| 445 | rb.Push(RESULT_SUCCESS); | ||
| 446 | } else { | ||
| 447 | LOG_ERROR(Service_IR, "not connected"); | ||
| 448 | rb.Push(ResultCode(static_cast<ErrorDescription>(13), ErrorModule::IR, | ||
| 449 | ErrorSummary::InvalidState, ErrorLevel::Status)); | ||
| 450 | } | ||
| 451 | |||
| 452 | LOG_TRACE(Service_IR, "called, data=%s", Common::ArrayToString(buffer.data(), size).c_str()); | ||
| 453 | } | ||
| 454 | |||
| 455 | /** | ||
| 456 | * IR::ReleaseReceivedData function | ||
| 457 | * Release a specified amount of packet from the receive buffer. This is called after the | ||
| 458 | * application reads received packet from the buffer directly, to release the buffer space for | ||
| 459 | * future packets. | ||
| 460 | * Inpus: | ||
| 461 | * 1 : Number of packets to release | ||
| 462 | * Outputs: | ||
| 463 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 464 | */ | ||
| 465 | static void ReleaseReceivedData(Interface* self) { | ||
| 466 | IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x19, 1, 0); | ||
| 467 | u32 count = rp.Pop<u32>(); | ||
| 468 | |||
| 469 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||
| 105 | 470 | ||
| 106 | cmd_buff[1] = RESULT_SUCCESS.raw; | 471 | if (receive_buffer->Release(count)) { |
| 472 | rb.Push(RESULT_SUCCESS); | ||
| 473 | } else { | ||
| 474 | LOG_ERROR(Service_IR, "failed to release %u packets", count); | ||
| 475 | rb.Push(ResultCode(ErrorDescription::NoData, ErrorModule::IR, ErrorSummary::NotFound, | ||
| 476 | ErrorLevel::Status)); | ||
| 477 | } | ||
| 107 | 478 | ||
| 108 | LOG_WARNING(Service_IR, "(STUBBED) called"); | 479 | LOG_TRACE(Service_IR, "called, count=%u", count); |
| 109 | } | 480 | } |
| 110 | 481 | ||
| 111 | const Interface::FunctionInfo FunctionTable[] = { | 482 | const Interface::FunctionInfo FunctionTable[] = { |
| @@ -118,10 +489,10 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 118 | {0x000702C0, nullptr, "AutoConnection"}, | 489 | {0x000702C0, nullptr, "AutoConnection"}, |
| 119 | {0x00080000, nullptr, "AnyConnection"}, | 490 | {0x00080000, nullptr, "AnyConnection"}, |
| 120 | {0x00090000, Disconnect, "Disconnect"}, | 491 | {0x00090000, Disconnect, "Disconnect"}, |
| 121 | {0x000A0000, nullptr, "GetReceiveEvent"}, | 492 | {0x000A0000, GetReceiveEvent, "GetReceiveEvent"}, |
| 122 | {0x000B0000, nullptr, "GetSendEvent"}, | 493 | {0x000B0000, GetSendEvent, "GetSendEvent"}, |
| 123 | {0x000C0000, GetConnectionStatusEvent, "GetConnectionStatusEvent"}, | 494 | {0x000C0000, GetConnectionStatusEvent, "GetConnectionStatusEvent"}, |
| 124 | {0x000D0042, nullptr, "SendIrNop"}, | 495 | {0x000D0042, SendIrNop, "SendIrNop"}, |
| 125 | {0x000E0042, nullptr, "SendIrNopLarge"}, | 496 | {0x000E0042, nullptr, "SendIrNopLarge"}, |
| 126 | {0x000F0040, nullptr, "ReceiveIrnop"}, | 497 | {0x000F0040, nullptr, "ReceiveIrnop"}, |
| 127 | {0x00100042, nullptr, "ReceiveIrnopLarge"}, | 498 | {0x00100042, nullptr, "ReceiveIrnopLarge"}, |
| @@ -133,7 +504,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 133 | {0x00160000, nullptr, "GetSendSizeFreeAndUsed"}, | 504 | {0x00160000, nullptr, "GetSendSizeFreeAndUsed"}, |
| 134 | {0x00170000, nullptr, "GetConnectionRole"}, | 505 | {0x00170000, nullptr, "GetConnectionRole"}, |
| 135 | {0x00180182, InitializeIrNopShared, "InitializeIrNopShared"}, | 506 | {0x00180182, InitializeIrNopShared, "InitializeIrNopShared"}, |
| 136 | {0x00190040, nullptr, "ReleaseReceivedData"}, | 507 | {0x00190040, ReleaseReceivedData, "ReleaseReceivedData"}, |
| 137 | {0x001A0040, nullptr, "SetOwnMachineId"}, | 508 | {0x001A0040, nullptr, "SetOwnMachineId"}, |
| 138 | }; | 509 | }; |
| 139 | 510 | ||
| @@ -144,13 +515,43 @@ IR_User_Interface::IR_User_Interface() { | |||
| 144 | void InitUser() { | 515 | void InitUser() { |
| 145 | using namespace Kernel; | 516 | using namespace Kernel; |
| 146 | 517 | ||
| 147 | transfer_shared_memory = nullptr; | 518 | shared_memory = nullptr; |
| 519 | |||
| 148 | conn_status_event = Event::Create(ResetType::OneShot, "IR:ConnectionStatusEvent"); | 520 | conn_status_event = Event::Create(ResetType::OneShot, "IR:ConnectionStatusEvent"); |
| 521 | send_event = Event::Create(ResetType::OneShot, "IR:SendEvent"); | ||
| 522 | receive_event = Event::Create(ResetType::OneShot, "IR:ReceiveEvent"); | ||
| 523 | |||
| 524 | receive_buffer = boost::none; | ||
| 525 | |||
| 526 | extra_hid = std::make_unique<ExtraHID>(PutToReceive); | ||
| 527 | |||
| 528 | connected_device = nullptr; | ||
| 149 | } | 529 | } |
| 150 | 530 | ||
| 151 | void ShutdownUser() { | 531 | void ShutdownUser() { |
| 152 | transfer_shared_memory = nullptr; | 532 | if (connected_device) { |
| 533 | connected_device->OnDisconnect(); | ||
| 534 | connected_device = nullptr; | ||
| 535 | } | ||
| 536 | |||
| 537 | extra_hid = nullptr; | ||
| 538 | receive_buffer = boost::none; | ||
| 539 | shared_memory = nullptr; | ||
| 153 | conn_status_event = nullptr; | 540 | conn_status_event = nullptr; |
| 541 | send_event = nullptr; | ||
| 542 | receive_event = nullptr; | ||
| 543 | } | ||
| 544 | |||
| 545 | void ReloadInputDevices() { | ||
| 546 | if (extra_hid) | ||
| 547 | extra_hid->RequestInputDevicesReload(); | ||
| 548 | } | ||
| 549 | |||
| 550 | IRDevice::IRDevice(SendFunc send_func_) : send_func(send_func_) {} | ||
| 551 | IRDevice::~IRDevice() = default; | ||
| 552 | |||
| 553 | void IRDevice::Send(const std::vector<u8>& data) { | ||
| 554 | send_func(data); | ||
| 154 | } | 555 | } |
| 155 | 556 | ||
| 156 | } // namespace IR | 557 | } // namespace IR |
diff --git a/src/core/hle/service/ir/ir_user.h b/src/core/hle/service/ir/ir_user.h index 3849bd923..2401346e8 100644 --- a/src/core/hle/service/ir/ir_user.h +++ b/src/core/hle/service/ir/ir_user.h | |||
| @@ -4,11 +4,41 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <functional> | ||
| 7 | #include "core/hle/service/service.h" | 8 | #include "core/hle/service/service.h" |
| 8 | 9 | ||
| 9 | namespace Service { | 10 | namespace Service { |
| 10 | namespace IR { | 11 | namespace IR { |
| 11 | 12 | ||
| 13 | /// An interface representing a device that can communicate with 3DS via ir:USER service | ||
| 14 | class IRDevice { | ||
| 15 | public: | ||
| 16 | /** | ||
| 17 | * A function object that implements the method to send data to the 3DS, which takes a vector of | ||
| 18 | * data to send. | ||
| 19 | */ | ||
| 20 | using SendFunc = std::function<void(const std::vector<u8>& data)>; | ||
| 21 | |||
| 22 | explicit IRDevice(SendFunc send_func); | ||
| 23 | virtual ~IRDevice(); | ||
| 24 | |||
| 25 | /// Called when connected with 3DS | ||
| 26 | virtual void OnConnect() = 0; | ||
| 27 | |||
| 28 | /// Called when disconnected from 3DS | ||
| 29 | virtual void OnDisconnect() = 0; | ||
| 30 | |||
| 31 | /// Called when data is received from the 3DS. This is invoked by the ir:USER send function. | ||
| 32 | virtual void OnReceive(const std::vector<u8>& data) = 0; | ||
| 33 | |||
| 34 | protected: | ||
| 35 | /// Sends data to the 3DS. The actual sending method is specified in the constructor | ||
| 36 | void Send(const std::vector<u8>& data); | ||
| 37 | |||
| 38 | private: | ||
| 39 | const SendFunc send_func; | ||
| 40 | }; | ||
| 41 | |||
| 12 | class IR_User_Interface : public Service::Interface { | 42 | class IR_User_Interface : public Service::Interface { |
| 13 | public: | 43 | public: |
| 14 | IR_User_Interface(); | 44 | IR_User_Interface(); |
| @@ -21,5 +51,8 @@ public: | |||
| 21 | void InitUser(); | 51 | void InitUser(); |
| 22 | void ShutdownUser(); | 52 | void ShutdownUser(); |
| 23 | 53 | ||
| 54 | /// Reload input devices. Used when input configuration changed | ||
| 55 | void ReloadInputDevices(); | ||
| 56 | |||
| 24 | } // namespace IR | 57 | } // namespace IR |
| 25 | } // namespace Service | 58 | } // namespace Service |
diff --git a/src/core/settings.cpp b/src/core/settings.cpp index a598f9f2f..3d22c0afa 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include "audio_core/audio_core.h" | 5 | #include "audio_core/audio_core.h" |
| 6 | #include "core/gdbstub/gdbstub.h" | 6 | #include "core/gdbstub/gdbstub.h" |
| 7 | #include "core/hle/service/hid/hid.h" | 7 | #include "core/hle/service/hid/hid.h" |
| 8 | #include "core/hle/service/ir/ir_user.h" | ||
| 8 | #include "settings.h" | 9 | #include "settings.h" |
| 9 | #include "video_core/video_core.h" | 10 | #include "video_core/video_core.h" |
| 10 | 11 | ||
| @@ -32,6 +33,7 @@ void Apply() { | |||
| 32 | AudioCore::EnableStretching(values.enable_audio_stretching); | 33 | AudioCore::EnableStretching(values.enable_audio_stretching); |
| 33 | 34 | ||
| 34 | Service::HID::ReloadInputDevices(); | 35 | Service::HID::ReloadInputDevices(); |
| 36 | Service::IR::ReloadInputDevices(); | ||
| 35 | } | 37 | } |
| 36 | 38 | ||
| 37 | } // namespace | 39 | } // namespace |