#pragma once // ============================================================================== // USB transport (IOKit / IOUSBFamily) // ============================================================================== // Owns the physical Xbox Wireless Dongle: // VID 0x045E, PIDs 0x02E6 (old), 0x02FE (new), 0x02F9 (ASUS/Lenovo built-in), // 0x091E (Surface Book 2). // // Port target: medusalix/xone transport/dongle.c + the USB calls in mt76.c. // Linux usb_control_msg()/usb_bulk_msg() map to IOUSBDeviceInterface // DeviceRequest and IOUSBInterfaceInterface WritePipe/ReadPipeAsync. // ============================================================================== #include #include #include #include namespace xone::usb { // Vendor ID of the Xbox Wireless Dongle. constexpr std::uint16_t vid = 0x045E; // Supported dongle PIDs (port of xone_dongle_id_table in transport/dongle.c). constexpr std::uint16_t pid_old_dongle = 0x02E6; constexpr std::uint16_t pid_new_dongle = 0x02FE; constexpr std::uint16_t pid_builtin_asus_lenovo = 0x02F9; constexpr std::uint16_t pid_surface_book_2 = 0x091E; // Vendor control request codes (port of MT_VEND_* in transport/mt76_defs.h). enum class vendor_request : std::uint8_t { dev_mode = 0x01, // enter firmware load mode write = 0x02, // register write power_on = 0x04, multi_write = 0x06, // register write (multi) multi_read = 0x07, // register read (multi) read_eeprom = 0x09, write_fce = 0x42, write_cfg = 0x46, // register write (config space) read_cfg = 0x47, // register read (config space) read_ext = 0x63, write_ext = 0x66, feature_set = 0x91, }; // Bulk endpoint numbers (port of XONE_MT_EP_* in transport/mt76.h). constexpr std::uint8_t ep_in_cmd = 0x05; // MCU command responses constexpr std::uint8_t ep_in_wlan = 0x04; // WLAN data frames constexpr std::uint8_t ep_out = 0x04; // commands to the chip // Receive buffer sizes (port of XONE_DONGLE_LEN_* in transport/dongle.c). constexpr std::size_t len_cmd_pkt = 0x0654; constexpr std::size_t len_wlan_pkt = 0x8400; // Delivered from the transport's reader thread for every message received // on a bulk IN endpoint. `ep` is ep_in_cmd or ep_in_wlan. Do not block and // do not destroy the transport from within the callback. using frame_callback = std::function; // Delivered once from the transport's reader thread when the dongle is // disconnected (including the chip reconnect during firmware load). Do not // block and do not destroy the transport from within the callback. using disconnect_callback = std::function; // Owns an open Xbox Wireless Dongle: its device and interface connections, // endpoint pipes, and the reader thread that pumps async bulk IN reads. class transport { public: // Probe for a connected dongle (any supported PID), open it, and start // the async read pump on EP 0x05 IN and EP 0x04 IN. `frames` receives // each received message and `disconnected` fires when the dongle goes // away; either may be empty. Returns nullptr if no dongle is present or // opening fails. static auto probe(frame_callback frames, disconnect_callback disconnected) -> std::unique_ptr; ~transport(); transport(transport const&) = delete; transport& operator=(transport const&) = delete; // Product ID of the connected dongle. auto pid() const -> std::uint16_t; // Vendor control request on EP0 (register R/W, firmware mode). `is_read` // selects IN (`data` holds len bytes to receive) or OUT (`data` holds // len bytes to send). Returns bytes transferred, or -EIO. auto send_vendor_request(vendor_request req, bool is_read, std::uint16_t w_value, std::uint16_t w_index, void *data, std::size_t len) -> int; // Bulk write to EP 0x04 OUT. Returns bytes written, or -EIO. auto bulk_write(void const *data, std::size_t len) -> int; private: struct read_slot; struct state; transport() = default; auto open(frame_callback frames, disconnect_callback disconnected) -> bool; // IOKit services are uint32_t registry handles (io_service_t). auto open_interface(std::uint32_t child) -> bool; auto submit_read(read_slot *slot) -> bool; void worker_loop(); // IOKit callbacks use IOReturn (int32_t) and io_iterator_t (uint32_t). void handle_read(read_slot *slot, std::int32_t result, std::size_t len); void handle_disconnected(); static void on_read_completion(void *refcon, std::int32_t result, void *arg0); static void on_dongle_terminated(void *refcon, std::uint32_t iter); std::unique_ptr state_; }; // TRUE if an Xbox Wireless Dongle is connected (any supported PID). auto dongle_present() -> bool; } // namespace xone::usb