feat: port GIP protocol and auth stack
Port the GIP protocol and authentication layers from medusalix/xone into the C++ stack: - crypto: SHA-256/HMAC (CommonCrypto), RSA PKCS#1 (Security.framework), and a self-contained P-256 ECDH validated against OpenSSL vectors - auth: v1 (RSA) and v2 (ECDH) handshake state machine - gip: header/varint/chunk handling and packet dispatch, with the kernel device model replaced by transport + client_listener interfaces Adds test_crypto, test_auth, and test_gip suites. Co-Authored-By: deepseek (deepseek/deepseek-v4-pro-0813): ported crypto, auth, and GIP
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
|
||||
// ==============================================================================
|
||||
// GIP authentication handshake
|
||||
// ==============================================================================
|
||||
// Port target: medusalix/xone auth/auth.c + auth/auth.h.
|
||||
//
|
||||
// The kernel driver runs the RSA/ECDH exchanges on workqueues and speaks to
|
||||
// the controller through gip_send_authenticate(). Here the exchanges run
|
||||
// synchronously and the GIP layer is reached through the auth_sink callback
|
||||
// interface, which the gip::client implements.
|
||||
// ==============================================================================
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
|
||||
#include "auth/crypto.hpp"
|
||||
|
||||
namespace xone::auth {
|
||||
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
|
||||
// Wire sizes from the upstream auth.h.
|
||||
inline constexpr auto k_trailer_len = 8;
|
||||
inline constexpr auto k_random_len = 32;
|
||||
inline constexpr auto k_certificate_max_len = 1024;
|
||||
inline constexpr auto k_pubkey_len = 270; // v1 RSA public key
|
||||
inline constexpr auto k_secret_len = 48;
|
||||
inline constexpr auto k_encrypted_pms_len = 256;
|
||||
inline constexpr auto k_transcript_len = 32;
|
||||
inline constexpr auto k_session_key_len = 16;
|
||||
inline constexpr auto k_pubkey2_len = 64; // v2 EC public key (X || Y)
|
||||
inline constexpr auto k_secret2_len = 32;
|
||||
|
||||
// Callback interface implemented by the GIP layer.
|
||||
class auth_sink {
|
||||
public:
|
||||
virtual ~auth_sink() = default;
|
||||
|
||||
// Send an AUTHENTICATE command packet to the controller.
|
||||
virtual auto send(std::span<u8 const> pkt, bool acknowledge) -> int = 0;
|
||||
|
||||
// Install the derived AES-CCMP session key (16 bytes).
|
||||
virtual auto set_encryption_key(std::span<u8 const> key) -> int = 0;
|
||||
};
|
||||
|
||||
// Runs the handshake for one controller. Port of struct gip_auth.
|
||||
class auth {
|
||||
public:
|
||||
explicit auth(auth_sink& sink);
|
||||
|
||||
// Begin the handshake by sending the (v1) host hello.
|
||||
auto start() -> int;
|
||||
|
||||
// Process one incoming AUTHENTICATE packet from the controller.
|
||||
auto process_pkt(std::span<u8 const> data) -> int;
|
||||
|
||||
private:
|
||||
auto send_pkt(u8 cmd, void const* pkt, std::size_t len) -> int;
|
||||
auto request_pkt(u8 cmd, std::uint16_t len) -> int;
|
||||
auto send_hello() -> int;
|
||||
auto send_hello2() -> int;
|
||||
auto send_finish(u8 cmd) -> int;
|
||||
auto send_complete() -> int;
|
||||
auto exchange_rsa() -> void;
|
||||
auto exchange_ecdh() -> void;
|
||||
auto complete_handshake() -> void;
|
||||
auto handle_pkt_acknowledge() -> int;
|
||||
auto handle_pkt_data(std::span<u8 const> data) -> int;
|
||||
auto dispatch_pkt(u8 cmd, std::span<u8 const> data) -> int;
|
||||
auto handle_hello(std::span<u8 const> data) -> int;
|
||||
auto handle_certificate(std::span<u8 const> data) -> int;
|
||||
auto handle_finish(std::span<u8 const> data) -> int;
|
||||
auto handle_hello2(std::span<u8 const> data) -> int;
|
||||
auto handle_certificate2(std::span<u8 const> data) -> int;
|
||||
auto handle_pubkey(std::span<u8 const> data) -> int;
|
||||
auto get_transcript() -> std::array<u8, k_transcript_len>;
|
||||
|
||||
auth_sink& sink_;
|
||||
sha256 transcript_;
|
||||
u8 last_sent_command_ = 0;
|
||||
|
||||
std::array<u8, k_random_len> random_host_{};
|
||||
std::array<u8, k_random_len> random_client_{};
|
||||
std::array<u8, k_pubkey_len> pubkey_client_{};
|
||||
std::array<u8, k_pubkey2_len> pubkey_client2_{};
|
||||
std::array<u8, k_secret_len> master_secret_{};
|
||||
};
|
||||
|
||||
} // namespace xone::auth
|
||||
+100
-6
@@ -1,16 +1,110 @@
|
||||
#pragma once
|
||||
|
||||
// ==============================================================================
|
||||
// Auth + crypto
|
||||
// Auth + crypto primitives
|
||||
// ==============================================================================
|
||||
// Port target: medusalix/xone auth/auth.c + auth/crypto.c.
|
||||
// ECDH (P-256) key agreement, RSA encryption, SHA transcript/PRF for the GIP
|
||||
// authentication handshake. AES-CCMP frame encryption itself runs on-chip;
|
||||
// this layer only derives and installs keys into the MT76 WCID registers.
|
||||
// Port target: medusalix/xone auth/crypto.c + auth/crypto.h.
|
||||
//
|
||||
// The kernel driver uses the in-kernel crypto API (crypto_shash for
|
||||
// SHA-256/HMAC, crypto_akcipher for PKCS#1 RSA, crypto_kpp for ECDH
|
||||
// P-256). These are replaced here with user-space equivalents:
|
||||
// - SHA-256 / HMAC-SHA256: CommonCrypto (part of libSystem)
|
||||
// - RSA PKCS#1 v1.5: Security.framework
|
||||
// - ECDH P-256: self-contained implementation (no public
|
||||
// macOS C API exists for EC key agreement)
|
||||
// - randomness: arc4random_buf
|
||||
//
|
||||
// AES-CCMP frame encryption itself runs on the MT76 chip; this layer
|
||||
// only derives keys and installs them (see xone_mt76_set_client_key).
|
||||
// ==============================================================================
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
#include <CommonCrypto/CommonDigest.h>
|
||||
|
||||
namespace xone::auth {
|
||||
|
||||
// TODO(phase 1): ECDH/RSA/SHA primitives (CommonCrypto / Security.framework).
|
||||
using u8 = std::uint8_t;
|
||||
|
||||
inline constexpr auto k_sha256_len = 32;
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// SHA-256
|
||||
// --------------------------------------------------------------------------
|
||||
// Incremental SHA-256. Instances are cheap to copy; a copy is an
|
||||
// independent snapshot of the current hash state, which the handshake
|
||||
// uses to compute the running transcript without losing progress.
|
||||
class sha256 {
|
||||
public:
|
||||
sha256();
|
||||
~sha256() = default;
|
||||
sha256(sha256 const&) = default;
|
||||
auto operator=(sha256 const&) -> sha256& = default;
|
||||
|
||||
auto update(std::span<u8 const> data) -> void;
|
||||
auto finalize(std::array<u8, k_sha256_len>& out) -> void;
|
||||
|
||||
private:
|
||||
CC_SHA256_CTX ctx_;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// HMAC-SHA256
|
||||
// --------------------------------------------------------------------------
|
||||
class hmac_sha256 {
|
||||
public:
|
||||
explicit hmac_sha256(std::span<u8 const> key);
|
||||
|
||||
auto update(std::span<u8 const> data) -> void;
|
||||
auto finalize(std::array<u8, k_sha256_len>& out) -> void;
|
||||
|
||||
private:
|
||||
sha256 inner_;
|
||||
sha256 outer_;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// TLS P_SHA256 PRF
|
||||
// --------------------------------------------------------------------------
|
||||
// Expands key + label + seed into out_len bytes, exactly like the kernel
|
||||
// driver's gip_auth_compute_prf(). Used to derive the master secret, the
|
||||
// handshake transcript checks, and the session key.
|
||||
auto prf_sha256(std::span<u8 const> key, std::string_view label,
|
||||
std::span<u8 const> seed, std::span<u8> out) -> void;
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// RSA (PKCS#1 v1.5)
|
||||
// --------------------------------------------------------------------------
|
||||
// Encrypts plaintext with a DER RSAPublicKey (the 270-byte ASN.1 SEQUENCE
|
||||
// that Microsoft controllers embed in their X.509 certificate). out must
|
||||
// be at least the RSA modulus size (256 bytes for 2048-bit keys).
|
||||
auto rsa_encrypt_pkcs1(std::span<u8 const> der_key,
|
||||
std::span<u8 const> plaintext, std::span<u8> out)
|
||||
-> bool;
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// ECDH (P-256)
|
||||
// --------------------------------------------------------------------------
|
||||
using ec_scalar = std::array<u8, 32>; // big-endian private key / x-coord
|
||||
using ec_point = std::array<u8, 64>; // X || Y, big-endian, no 0x04 prefix
|
||||
|
||||
// q = d * G.
|
||||
auto ec_base_point_multiply(ec_scalar const& d, ec_point& q) -> void;
|
||||
|
||||
// shared = x-coordinate of d * peer (the standard P-256 shared secret).
|
||||
// Returns false if peer is not a valid point on the curve.
|
||||
auto ec_compute_shared(ec_scalar const& d, ec_point const& peer,
|
||||
ec_scalar& shared) -> bool;
|
||||
|
||||
// Generate a random keypair.
|
||||
auto ec_generate_keypair(ec_scalar& d, ec_point& q) -> void;
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Randomness
|
||||
// --------------------------------------------------------------------------
|
||||
auto random_bytes(std::span<u8> out) -> void;
|
||||
|
||||
} // namespace xone::auth
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
// ==============================================================================
|
||||
// Minimal printf-style logger
|
||||
// ==============================================================================
|
||||
// Replaces the kernel's dev_dbg/dev_err from medusalix/xone. Writes to
|
||||
// stderr; the Swift app layer can redirect or swallow it later.
|
||||
// ==============================================================================
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
|
||||
namespace xone {
|
||||
|
||||
enum class log_level {
|
||||
debug = 0,
|
||||
info,
|
||||
warn,
|
||||
error,
|
||||
};
|
||||
|
||||
inline auto log_msg(log_level level, char const* fmt, ...) -> void
|
||||
{
|
||||
char const* tag = "?";
|
||||
switch (level) {
|
||||
case log_level::debug:
|
||||
tag = "debug";
|
||||
break;
|
||||
case log_level::info:
|
||||
tag = "info";
|
||||
break;
|
||||
case log_level::warn:
|
||||
tag = "warn";
|
||||
break;
|
||||
case log_level::error:
|
||||
tag = "error";
|
||||
break;
|
||||
}
|
||||
|
||||
std::fprintf(stderr, "[%s] ", tag);
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
std::vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
std::fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
} // namespace xone
|
||||
@@ -10,6 +10,11 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// Mark a struct as packed (no padding), matching the wire formats of the
|
||||
// MT76 chip and the GIP protocol. clang-specific, like the rest of this
|
||||
// macOS-only project.
|
||||
#define XONE_PACKED __attribute__((packed))
|
||||
|
||||
namespace xone {
|
||||
|
||||
// 128-bit GUID used by the GIP protocol (port of the kernel guid_t).
|
||||
@@ -46,4 +51,18 @@ inline auto store_le32(void *p, std::uint32_t v) -> void
|
||||
b[3] = static_cast<std::uint8_t>((v >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
// Big-endian 16-bit load/store (used by the auth handshake headers).
|
||||
inline auto load_be16(void const *p) -> std::uint16_t
|
||||
{
|
||||
auto const *b = static_cast<std::uint8_t const *>(p);
|
||||
return static_cast<std::uint16_t>((b[0] << 8) | b[1]);
|
||||
}
|
||||
|
||||
inline auto store_be16(void *p, std::uint16_t v) -> void
|
||||
{
|
||||
auto *b = static_cast<std::uint8_t *>(p);
|
||||
b[0] = static_cast<std::uint8_t>((v >> 8) & 0xFF);
|
||||
b[1] = static_cast<std::uint8_t>(v & 0xFF);
|
||||
}
|
||||
|
||||
} // namespace xone
|
||||
|
||||
+288
-3
@@ -4,12 +4,297 @@
|
||||
// GIP protocol (Game Input Protocol)
|
||||
// ==============================================================================
|
||||
// Port target: medusalix/xone bus/protocol.c + bus/bus.c.
|
||||
// Client lifecycle, ANNOUNCE/IDENTIFY handshake, auth handshake, status
|
||||
// reports (battery, connected), HID report pass-through.
|
||||
//
|
||||
// The kernel driver's device model (struct device, sysfs, driver
|
||||
// registration) is gone. The adapter owns up to 16 clients; the transport
|
||||
// interface abstracts the MT76 data path (Phase 3), and client_listener
|
||||
// delivers input/battery/audio events to the HID layer (Phase 4). The
|
||||
// adapter is driven single-threaded from the USB read callback.
|
||||
// ==============================================================================
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "auth/auth.hpp"
|
||||
#include "common/types.hpp"
|
||||
|
||||
namespace xone::gip {
|
||||
|
||||
// TODO(phase 1): GIP frame types, client lifecycle, handshake state machine.
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using u32 = std::uint32_t;
|
||||
|
||||
inline constexpr u16 k_vid_microsoft = 0x045e;
|
||||
inline constexpr int k_audio_interval = 8; // ms between audio packets
|
||||
inline constexpr u32 k_pkt_max_length = 58;
|
||||
inline constexpr u32 k_chunk_buf_max_length = 0xffff;
|
||||
inline constexpr int k_max_clients = 16;
|
||||
|
||||
enum : u8 {
|
||||
GIP_CMD_ACKNOWLEDGE = 0x01,
|
||||
GIP_CMD_ANNOUNCE = 0x02,
|
||||
GIP_CMD_STATUS = 0x03,
|
||||
GIP_CMD_IDENTIFY = 0x04,
|
||||
GIP_CMD_POWER = 0x05,
|
||||
GIP_CMD_AUTHENTICATE = 0x06,
|
||||
GIP_CMD_VIRTUAL_KEY = 0x07,
|
||||
GIP_CMD_AUDIO_CONTROL = 0x08,
|
||||
GIP_CMD_RUMBLE = 0x09,
|
||||
GIP_CMD_LED = 0x0a,
|
||||
GIP_CMD_HID_REPORT = 0x0b,
|
||||
GIP_CMD_FIRMWARE = 0x0c,
|
||||
GIP_CMD_SERIAL_NUMBER = 0x1e,
|
||||
GIP_CMD_INPUT = 0x20,
|
||||
GIP_CMD_AUDIO_SAMPLES = 0x60,
|
||||
};
|
||||
|
||||
enum : u8 {
|
||||
GIP_OPT_ACKNOWLEDGE = 1 << 4,
|
||||
GIP_OPT_INTERNAL = 1 << 5,
|
||||
GIP_OPT_CHUNK_START = 1 << 6,
|
||||
GIP_OPT_CHUNK = 1 << 7,
|
||||
};
|
||||
|
||||
enum : u8 {
|
||||
GIP_BATT_TYPE_NONE = 0x00,
|
||||
GIP_BATT_TYPE_STANDARD = 0x01,
|
||||
GIP_BATT_TYPE_KIT = 0x02,
|
||||
};
|
||||
|
||||
enum : u8 {
|
||||
GIP_BATT_LEVEL_LOW = 0x00,
|
||||
GIP_BATT_LEVEL_NORMAL = 0x01,
|
||||
GIP_BATT_LEVEL_HIGH = 0x02,
|
||||
GIP_BATT_LEVEL_FULL = 0x03,
|
||||
};
|
||||
|
||||
enum : u8 {
|
||||
GIP_PWR_ON = 0x00,
|
||||
GIP_PWR_SLEEP = 0x01,
|
||||
GIP_PWR_OFF = 0x04,
|
||||
GIP_PWR_RESET = 0x07,
|
||||
};
|
||||
|
||||
enum : u8 {
|
||||
GIP_LED_OFF = 0x00,
|
||||
GIP_LED_ON = 0x01,
|
||||
GIP_LED_BLINK_FAST = 0x02,
|
||||
GIP_LED_BLINK_NORMAL = 0x03,
|
||||
GIP_LED_BLINK_SLOW = 0x04,
|
||||
GIP_LED_FADE_SLOW = 0x08,
|
||||
GIP_LED_FADE_FAST = 0x09,
|
||||
};
|
||||
|
||||
enum : u8 {
|
||||
GIP_AUD_FORMAT_16KHZ_MONO = 0x05,
|
||||
GIP_AUD_FORMAT_24KHZ_MONO = 0x09,
|
||||
GIP_AUD_FORMAT_48KHZ_STEREO = 0x10,
|
||||
};
|
||||
|
||||
enum : u8 {
|
||||
GIP_AUD_FORMAT_CHAT_24KHZ = 0x04,
|
||||
GIP_AUD_FORMAT_CHAT_16KHZ = 0x05,
|
||||
};
|
||||
|
||||
// Decoded wire header (variable length, not packed).
|
||||
struct gip_header {
|
||||
u8 command = 0;
|
||||
u8 options = 0;
|
||||
u8 sequence = 0;
|
||||
u32 packet_length = 0;
|
||||
u32 chunk_offset = 0;
|
||||
};
|
||||
|
||||
struct gip_hardware {
|
||||
u16 vendor = 0;
|
||||
u16 product = 0;
|
||||
u16 version = 0;
|
||||
};
|
||||
|
||||
struct gip_audio_config {
|
||||
u8 format = 0;
|
||||
int channels = 0;
|
||||
int sample_rate = 0;
|
||||
int buffer_size = 0;
|
||||
int fragment_size = 0;
|
||||
int packet_size = 0;
|
||||
};
|
||||
|
||||
// Low-level data path to the MT76 chip (implemented by Phase 3).
|
||||
class transport {
|
||||
public:
|
||||
virtual ~transport() = default;
|
||||
|
||||
// Send one framed GIP data buffer to the chip.
|
||||
virtual auto send_frame(std::span<u8 const> frame) -> int = 0;
|
||||
|
||||
// Install the AES-CCMP session key for a client (no-op by default).
|
||||
virtual auto set_encryption_key(u8 client_id, std::span<u8 const> key) -> int
|
||||
{
|
||||
(void)client_id;
|
||||
(void)key;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Events delivered to the HID layer (Phase 4).
|
||||
class client_listener {
|
||||
public:
|
||||
virtual ~client_listener() = default;
|
||||
virtual auto on_client_added(class client&) -> void {}
|
||||
virtual auto on_client_removed(u8) -> void {}
|
||||
virtual auto on_battery(class client&, u8, u8) -> void {}
|
||||
virtual auto on_guide_button(class client&, bool) -> void {}
|
||||
virtual auto on_input(class client&, std::span<u8 const>) -> void {}
|
||||
virtual auto on_hid_report(class client&, std::span<u8 const>) -> void {}
|
||||
virtual auto on_audio_ready(class client&) -> void {}
|
||||
virtual auto on_audio_volume(class client&, u8, u8) -> void {}
|
||||
virtual auto on_audio_samples(class client&, std::span<u8 const>) -> void {}
|
||||
};
|
||||
|
||||
class adapter;
|
||||
|
||||
// One connected controller. Also implements the auth_sink so the auth
|
||||
// handshake can send AUTHENTICATE packets back through the GIP layer.
|
||||
class client : public xone::auth::auth_sink {
|
||||
public:
|
||||
client(adapter& adapter, u8 id);
|
||||
|
||||
auto id() const -> u8 { return id_; }
|
||||
auto hardware() const -> gip_hardware const& { return hardware_; }
|
||||
auto audio_config_in() const -> gip_audio_config const& { return audio_config_in_; }
|
||||
auto audio_config_out() const -> gip_audio_config const& { return audio_config_out_; }
|
||||
auto classes() const -> std::vector<std::string> const& { return classes_; }
|
||||
|
||||
auto has_interface(xone::guid_t const& guid) const -> bool;
|
||||
|
||||
auto set_power_mode(u8 mode) -> int;
|
||||
auto send_rumble(std::span<u8 const> pkt) -> int;
|
||||
auto set_led_mode(u8 mode, u8 brightness) -> int;
|
||||
auto suggest_audio_format(u8 in, u8 out, bool chat) -> int;
|
||||
auto set_audio_volume(u8 in, u8 chat, u8 out) -> int;
|
||||
auto send_audio_samples(std::span<u8 const> samples) -> int;
|
||||
auto enable_audio() -> int;
|
||||
auto init_audio_in() -> int;
|
||||
auto init_audio_out() -> int;
|
||||
auto disable_audio() -> void;
|
||||
|
||||
// auth_sink implementation.
|
||||
auto send(std::span<u8 const> pkt, bool acknowledge) -> int override;
|
||||
auto set_encryption_key(std::span<u8 const> key) -> int override;
|
||||
|
||||
auto start_auth() -> int { return auth_.start(); }
|
||||
auto process_auth(std::span<u8 const> data) -> int
|
||||
{
|
||||
return auth_.process_pkt(data);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class adapter;
|
||||
|
||||
adapter& adapter_;
|
||||
u8 id_;
|
||||
gip_hardware hardware_{};
|
||||
|
||||
struct info_element {
|
||||
u8 count = 0;
|
||||
std::vector<u8> data;
|
||||
};
|
||||
|
||||
std::unique_ptr<info_element> client_commands_;
|
||||
std::unique_ptr<info_element> firmware_versions_;
|
||||
std::unique_ptr<info_element> audio_formats_;
|
||||
std::unique_ptr<info_element> capabilities_out_;
|
||||
std::unique_ptr<info_element> capabilities_in_;
|
||||
std::vector<std::string> classes_;
|
||||
std::unique_ptr<info_element> interfaces_;
|
||||
std::unique_ptr<info_element> hid_descriptor_;
|
||||
|
||||
gip_audio_config audio_config_in_;
|
||||
gip_audio_config audio_config_out_;
|
||||
|
||||
// Chunk reassembly buffers (large packets are split into chunks).
|
||||
struct chunk_buffer {
|
||||
gip_header header;
|
||||
u32 length = 0;
|
||||
std::vector<u8> data;
|
||||
};
|
||||
std::unique_ptr<chunk_buffer> chunk_buf_out_;
|
||||
std::unique_ptr<chunk_buffer> chunk_buf_in_;
|
||||
|
||||
xone::auth::auth auth_{*this};
|
||||
};
|
||||
|
||||
class adapter {
|
||||
public:
|
||||
adapter(transport& transport, client_listener& listener,
|
||||
int audio_packet_count = 8);
|
||||
|
||||
// Dispatch one USB data buffer (may contain multiple GIP packets).
|
||||
auto process_buffer(std::span<u8 const> data) -> int;
|
||||
|
||||
auto get_client(u8 id) -> client*;
|
||||
auto client_count() const -> int;
|
||||
|
||||
private:
|
||||
friend class client;
|
||||
|
||||
auto send_pkt(client& c, gip_header& hdr, void const* data) -> int;
|
||||
auto send_pkt_simple(gip_header& hdr, void const* data) -> int;
|
||||
auto init_chunk_buffer(gip_header const& hdr,
|
||||
std::unique_ptr<client::chunk_buffer>& buf) -> int;
|
||||
auto send_remaining_chunks(client& c) -> int;
|
||||
auto request_identification(client& c) -> int;
|
||||
auto acknowledge_pkt(client& c, gip_header const& ack) -> int;
|
||||
auto remove_client(client& c) -> void;
|
||||
auto free_client_info(client& c) -> void;
|
||||
auto add_client(client& c) -> void;
|
||||
auto make_audio_config(gip_audio_config& cfg) -> int;
|
||||
auto set_audio_format(client& c, u8 in, u8 out) -> int;
|
||||
auto set_audio_format_chat(client& c, u8 in_out) -> int;
|
||||
|
||||
auto dispatch_pkt(client& c, gip_header const& hdr, void const* data, u32 len) -> int;
|
||||
auto process_pkt(client& c, gip_header& hdr, void const* data) -> int;
|
||||
auto process_pkt_chunked(client& c, gip_header const& hdr, void const* data) -> int;
|
||||
|
||||
auto handle_pkt_acknowledge(client& c, void const* data, u32 len) -> int;
|
||||
auto handle_pkt_announce(client& c, void const* data, u32 len) -> int;
|
||||
auto handle_pkt_status(client& c, void const* data, u32 len) -> int;
|
||||
auto handle_pkt_identify(client& c, void const* data, u32 len) -> int;
|
||||
auto handle_pkt_authenticate(client& c, void const* data, u32 len) -> int;
|
||||
auto handle_pkt_virtual_key(client& c, void const* data, u32 len) -> int;
|
||||
auto handle_pkt_audio_control(client& c, void const* data, u32 len) -> int;
|
||||
auto handle_pkt_hid_report(client& c, void const* data, u32 len) -> int;
|
||||
auto handle_pkt_input(client& c, void const* data, u32 len) -> int;
|
||||
auto handle_pkt_audio_samples(client& c, void const* data, u32 len) -> int;
|
||||
|
||||
auto parse_info_element(std::span<u8 const> data, u16 offset, int item_length,
|
||||
std::unique_ptr<client::info_element>& out) -> int;
|
||||
auto parse_client_commands(client& c, u16 const offsets[8],
|
||||
std::span<u8 const> data) -> int;
|
||||
auto parse_firmware_versions(client& c, u16 const offsets[8],
|
||||
std::span<u8 const> data) -> int;
|
||||
auto parse_audio_formats(client& c, u16 const offsets[8],
|
||||
std::span<u8 const> data) -> int;
|
||||
auto parse_capabilities(client& c, u16 const offsets[8],
|
||||
std::span<u8 const> data) -> int;
|
||||
auto parse_classes(client& c, std::span<u8 const> data, u16 offset) -> int;
|
||||
auto parse_interfaces(client& c, u16 const offsets[8],
|
||||
std::span<u8 const> data) -> int;
|
||||
auto parse_hid_descriptor(client& c, u16 const offsets[8],
|
||||
std::span<u8 const> data) -> int;
|
||||
|
||||
transport& transport_;
|
||||
client_listener& listener_;
|
||||
int audio_packet_count_;
|
||||
|
||||
std::array<std::unique_ptr<client>, k_max_clients> clients_;
|
||||
u8 data_sequence_ = 0;
|
||||
u8 audio_sequence_ = 0;
|
||||
};
|
||||
|
||||
} // namespace xone::gip
|
||||
|
||||
Reference in New Issue
Block a user