diff --git a/include/scc/driver.h b/include/scc/driver.h index f83e0b4c1..91b03772e 100644 --- a/include/scc/driver.h +++ b/include/scc/driver.h @@ -18,8 +18,8 @@ typedef unsigned short Product; typedef uintptr_t TaskID; typedef enum { USB = 0, -#ifdef __linux__ BT = 1, +#ifdef __linux__ EVDEV = 2, #endif #ifdef __BSD__ diff --git a/include/scc/input_device.h b/include/scc/input_device.h index d084c4472..c24b4bae7 100644 --- a/include/scc/input_device.h +++ b/include/scc/input_device.h @@ -125,7 +125,7 @@ typedef struct InputDevice { * * 'idx' has meaning only with libusb devices and is ignored for everything else. */ - uint8_t* (*hid_request)(InputDevice* dev, uint16_t idx, uint8_t* data, int32_t length); + uint8_t* (*hid_request)(InputDevice* dev, int16_t idx, uint8_t* data, int32_t length); } InputDevice; diff --git a/make-win32-release.sh b/make-win32-release.sh index 0ce4111c3..00091ffad 100644 --- a/make-win32-release.sh +++ b/make-win32-release.sh @@ -37,7 +37,7 @@ GTK_ICONS=( ui/pan-end-symbolic.symbolic.png ui/pan-up-symbolic.symbolic.png ) -DRIVERS=(sc_by_cable sc_dongle) +DRIVERS=(sc_by_cable sc_dongle sc_by_bt) export PROCESSOR_ARCHITEW6432=x86 # meson $1 diff --git a/src/config/defaults.c b/src/config/defaults.c index 3c48d3088..ad90d1b07 100644 --- a/src/config/defaults.c +++ b/src/config/defaults.c @@ -24,7 +24,7 @@ const char* DEFAULT_PROFILES[] = { * Default list of enabled drivers. If driver is not listed in "drivers" * object, it's enabled only if listed here. */ -const char* DEFAULT_ENABLED_DRIVERS[] = { "sc_by_cable" }; +const char* DEFAULT_ENABLED_DRIVERS[] = { "sc_by_cable sc_by_bt" }; const struct config_item DEFAULTS[] = { diff --git a/src/daemon/drivers/meson.build b/src/daemon/drivers/meson.build index 2c62c0039..f0dd0958d 100644 --- a/src/daemon/drivers/meson.build +++ b/src/daemon/drivers/meson.build @@ -88,5 +88,15 @@ if host_machine.system() == 'windows' 'generic/dinput.c', ] ) + + shared_library('scc-drv-sc_by_bt', + include_directories: [ include ], + link_with: [ utils_lib, config_lib ], + dependencies: deps, + sources: [ + 'sc/common.c', + 'sc/by_bt.c', + ] + ) endif diff --git a/src/daemon/drivers/sc/by_bt.c b/src/daemon/drivers/sc/by_bt.c new file mode 100644 index 000000000..78da3da3e --- /dev/null +++ b/src/daemon/drivers/sc/by_bt.c @@ -0,0 +1,267 @@ +/** + * Steam Controller Controller Steam Controller Driver + * + * Used to communicate with single Steam Controller + * connected via bluetooth. + */ +#define LOG_TAG "sc_by_bt" +#include "scc/utils/logging.h" +#include "scc/utils/assert.h" +#include "scc/input_device.h" +#include "scc/input_test.h" +#include "scc/driver.h" +#include "scc/mapper.h" +#include "scc/tools.h" +#include "sc.h" +#include + +#define ENDPOINT 3 +#define CONTROLIDX -1 +#define CHUNK_LENGTH 64 +#define VENDOR_ID 0x28de +#define PRODUCT_ID 0x1106 +#define PACKET_SIZE 20 +#define LONG_PACKET 0x80 +#define BT_BUTTONS_BITS 23 + +static controller_available_cb controller_available = NULL; + +enum BtInPacketType { + BUTTON = 0x0010, + TRIGGERS = 0x0020, + STICK = 0x0080, + LPAD = 0x0100, + RPAD = 0x0200, + GYRO = 0x1800, + PING = 0x5000, +}; + +static inline void debug_packet(char* buffer, size_t size) { + size_t i; + for(i=0; imapper != NULL){ + if (sc->long_packet) { + // Previous packet had long flag set and this is its 2nd part + memcpy(ptr_buffer + PACKET_SIZE, tmp_buffer + 1, PACKET_SIZE - 1); + sc->long_packet = 0; + } else { + // This is 1st part of long packet + memcpy(ptr_buffer, (void *)i, PACKET_SIZE); + sc->long_packet = *((uint8_t*)(ptr_buffer + 1)) == LONG_PACKET; + if (sc->long_packet) { + return 0; + } + // debug_packet(ptr->buffer, PACKET_SIZE); + } + + int rv = 0; + int bit; + uint16_t type = *((uint16_t*)(ptr_buffer + 2)); + char* data = &ptr_buffer[4]; + if ((type & PING) == PING) { + // PING packet does nothing + return 0; + } + if ((type & BUTTON) == BUTTON) { + rv = 1; + uint32_t bt_buttons = *((uint32_t*)data); + uint32_t sc_buttons = 0; + //TODO cover remaining bits + for (bit=0; bit>= 1; + } + sc->input.buttons = (SCButton)sc_buttons; + data += 3; + } + if ((type & TRIGGERS) == TRIGGERS) { + rv = 1; + sc->input.ltrig = *(((uint8_t*)data) + 0); + sc->input.rtrig = *(((uint8_t*)data) + 1); + data += 2; + } + if ((type & STICK) == STICK) { + rv = 1; + sc->input.stick_x = *(((int16_t*)data) + 0); + sc->input.stick_y = *(((int16_t*)data) + 1); + data += 4; + } + if ((type & LPAD) == LPAD) { + rv = 1; + sc->input.lpad_x = *(((int16_t*)data) + 0); + sc->input.lpad_y = *(((int16_t*)data) + 1); + data += 4; + } + if ((type & RPAD) == RPAD) { + rv = 1; + sc->input.rpad_x = *(((int16_t*)data) + 0); + sc->input.rpad_y = *(((int16_t*)data) + 1); + data += 4; + } + if ((type & GYRO) == GYRO) { + rv = 1; + sc->input.gyro.gpitch = *(((int16_t*)data) + 0); + sc->input.gyro.groll = *(((int16_t*)data) + 1); + sc->input.gyro.gyaw = *(((int16_t*)data) + 2); + sc->input.gyro.q0 = *(((int16_t*)data) + 3); + sc->input.gyro.q1 = *(((int16_t*)data) + 4); + sc->input.gyro.q2 = *(((int16_t*)data) + 5); + sc->input.gyro.q3 = *(((int16_t*)data) + 6); + data += 14; + } + sc->input.buttons &= ~0b00000000000011110000000000000000; + //input eval same bitmap? + //sc->mapper->input(sc->mapper, &sc->input); + return rv; + } +} + +void input_interrupt_cb(Daemon* d, InputDevice* dev, uint8_t endpoint, const uint8_t* data, void* userdata) { + SCController* sc = (SCController*)userdata; + if (data == NULL) { + // Means controller disconnected (or failed in any other way) + DEBUG("%s disconnected", sc->desc); + // USBHelper* usb = d->get_usb_helper(); + // usb->close(sc->usb_hndl); + // TODO: Calling close at this point may hang. Closing should be + // scheduled for later time instead, ideally in sccd_usb_dev_close. + disconnected(sc); + // TODO: Deallocate sc + return; + } + //debug_packet((char *)data, PACKET_SIZE * 2); + int status = bt_handle_input(sc, data); + if(status == 1){ + //TODO input rotation support + /*if ( + //self._input_rotation_l and + (self._state.type & 0x0100) != 0) { + lx, ly = self._state.lpad_x, self._state.lpad_y + //s, c = sin(self._input_rotation_l), cos(self._input_rotation_l) + self._state.lpad_x = int(lx * c - ly * s) + self._state.lpad_y = int(lx * s + ly * c) + } + if ( + //self._input_rotation_r and + (self._state.type & 0x0200) != 0) { + rx, ry = self._state.rpad_x, self._state.rpad_y + s, c = sin(self._input_rotation_r), cos(self._input_rotation_r) + self._state.rpad_x = int(rx * c - ry * s) + self._state.rpad_y = int(rx * s + ry * c) + }*/ + + sc->mapper->input(sc->mapper, &sc->input); + + //it's unlikely this is necessary + //flush() + } else if(status > 1) { + DDEBUG("Read Failed"); + + //TODO maybe should retry + //self.close() + //self.driver.retry(self.syspath) + } +} + + +static bool hotplug_cb(Daemon* daemon, const InputDeviceData* idata) { + if (controller_available != NULL) { + controller_available("sc_by_bt", 9, idata); + return true; + } + SCController* sc = NULL; + DDEBUG("%s",idata->path); + InputDevice* dev = idata->open(idata); + if (dev == NULL) { + LERROR("Failed to open '%s'", idata->path); + return true; // and nothing happens + } + if ((sc = create_usb_controller(daemon, dev, SC_BT, CONTROLIDX)) == NULL) { + LERROR("Failed to allocate memory"); + goto hotplug_cb_fail; + } + if (dev->sys == USB) { + if (dev->claim_interfaces_by(dev, 3, 0, 0) <= 0) { + LERROR("Failed to claim interfaces"); + goto hotplug_cb_fail; + } + } + //TODO fix serial grabbing + if (!read_serial(sc)) { + LERROR("Failed to read serial number"); + goto hotplug_cb_failed_to_configure; + } + //TODO needed? + if (!clear_mappings(sc)) + // clear_mappings is needed on Windows, as kernel driver cannot be deatached there + goto hotplug_cb_failed_to_configure; + if (!configure(sc)) + goto hotplug_cb_failed_to_configure; + if (!dev->interupt_read_loop(dev, ENDPOINT, PACKET_SIZE, &input_interrupt_cb, sc)) + DEBUG("New BLE Steam Controller with serial %s connected", sc->serial); + sc->state = SS_READY; + if (!daemon->controller_add(&sc->controller)) { + // This shouldn't happen unless memory is running out + DEBUG("Failed to add controller to daemon"); + goto hotplug_cb_fail; + } + return true; +hotplug_cb_failed_to_configure: + LERROR("Failed to configure controlller"); +hotplug_cb_fail: + if (sc != NULL) + free(sc); + dev->close(dev); + return true; +} + +static bool driver_start(Driver* drv, Daemon* daemon) { + HotplugFilter filter_vendor = { .type=SCCD_HOTPLUG_FILTER_VENDOR, .vendor=VENDOR_ID }; + HotplugFilter filter_product = { .type=SCCD_HOTPLUG_FILTER_PRODUCT, .product=PRODUCT_ID }; + HotplugFilter filter_idx = { .type=SCCD_HOTPLUG_FILTER_IDX, .idx=CONTROLIDX }; + Subsystem s = HIDAPI; +#if defined(_WIN32) + #define FILTERS &filter_vendor, &filter_product, &filter_idx +#elif defined(__BSD__) + #define FILTERS &filter_vendor, &filter_product, &filter_idx + s = UHID; +#else + #define FILTERS &filter_vendor, &filter_product +#endif + if (!daemon->hotplug_cb_add(s, hotplug_cb, FILTERS, NULL)) { + LERROR("Failed to register hotplug callback"); + return false; + } + return true; +} + +static void driver_list_devices(Driver* drv, Daemon* daemon, const controller_available_cb ca) { + controller_available = ca; + driver_start(drv, daemon); +} + +static Driver driver = { + .unload = NULL, + .start = driver_start, + // .list_devices = driver_list_devices, +}; + +Driver* scc_driver_init(Daemon* daemon) { + ASSERT(sizeof(TriggerValue) == 1); + ASSERT(sizeof(AxisValue) == 2); + ASSERT(sizeof(GyroValue) == 2); + // ^^ If any of above assertions fails, input_interrupt_cb code has to be + // modified so it doesn't use memcpy calls, as those depends on those sizes + + return &driver; +} + diff --git a/src/daemon/drivers/sc/common.c b/src/daemon/drivers/sc/common.c index f7ff90973..a44ffc711 100644 --- a/src/daemon/drivers/sc/common.c +++ b/src/daemon/drivers/sc/common.c @@ -12,6 +12,7 @@ #define B_STICKTILT 0b10000000000000000000000000000000 #define BUFFER_SIZE 128 +#define SMALL_BUFFER_SIZE 64 static const char* get_description(Controller* c); static const char* get_type(Controller* c); @@ -99,18 +100,21 @@ SCController* create_usb_controller(Daemon* daemon, InputDevice* dev, SCControll sc->controller.get_gyro_enabled = &get_gyro_enabled; // Main difference between dongle-bound and wired controller is that dongle-bound // countroller doesn't close USB device when deallocated - sc->controller.deallocate = (type == SC_WIRED) ? &deallocate : &deallocate_dongle_controller; + sc->controller.deallocate = (type == SC_WIRED || type == SC_BT) ? &deallocate : &deallocate_dongle_controller; HAPTIC_DISABLE(&sc->hdata[0]); sc->hdata[0].pos = HAPTIC_LEFT; HAPTIC_DISABLE(&sc->hdata[1]); sc->hdata[1].pos = HAPTIC_RIGHT; sc->state = SS_NOT_CONFIGURED; - sc->gyro_enabled = true; + // spam inputs as some cards drop packets when gyro packets are sent too fast + // TODO timesliced scheduler and poller system as in python version + sc->gyro_enabled = (type == SC_BT) ? false : true; sc->dev = dev; sc->daemon = daemon; sc->auto_id_used = false; sc->idle_timeout = 10 * 60; // 10 minutes sc->led_level = 50; sc->type = type; + sc->long_packet = 0; sc->idx = idx; return sc; } @@ -172,6 +176,7 @@ static union { uint16_t period; uint16_t cunt; }; +//TODO need change for BT? } haptic = { .packet_type = PT_FEEDBACK, .len = PL_FEEDBACK, .cunt = 1 }; /** @@ -189,6 +194,7 @@ static void flush(Controller* c, Mapper* m) { // Special case, windows needs to do this synchronously // using hid_request, or it throws "overlapped operation in progress" // error. + //TODO need change for BT? haptic.packet_type = PT_FEEDBACK; haptic.len = PL_FEEDBACK; haptic.cunt = 1; @@ -201,6 +207,29 @@ static void flush(Controller* c, Mapper* m) { } } +// TODO does not work, find correct SC_BT command +static void turnoff(Controller* c) { + SCController* sc = container_of(c, SCController, controller); + DDEBUG("turn off req"); + switch (sc->type) { + case SC_WIRED: + break; + case SC_WIRELESS: + //0xC0, 0x9F, 0x04, 0x6f, 0x66, 0x66, 0x21 }; + uint8_t data1[] = { PT_OFF, PL_OFF, 0x6f, 0x66, 0x66, 0x21 }; + if (sc->dev->hid_request(sc->dev, sc->idx, data1, -(SMALL_BUFFER_SIZE)) == NULL) + LERROR("Failed to turn off controller"); + case SC_BT: + //0xC0, 0x9F, 0x04, 0x6f, 0x66, 0x66, 0x21 }; + uint8_t data[] = { PT_BT_PREFIX, PT_OFF, PL_OFF, 0x6f, 0x66, 0x66, 0x21 }; + if (sc->dev->hid_request(sc->dev, sc->idx, data, -(SMALL_BUFFER_SIZE + 1)) == NULL) + LERROR("Failed to turn off controller"); + break; + case SC_DECK: + break; + } +} + static void update_desc(SCController* sc) { switch (sc->type) { @@ -219,6 +248,12 @@ static void update_desc(SCController* sc) { } } +static inline void debug_packet(char* buffer, size_t size) { + size_t i; + for(i=0; itype == SC_BT ? BUFFER_SIZE : (SMALL_BUFFER_SIZE + 1); +#else + int data_size = sc->type == SC_BT ? BUFFER_SIZE : (SMALL_BUFFER_SIZE); +#endif + int bt_offset = 0; uint8_t* response; - response = sc->dev->hid_request(sc->dev, sc->idx, data, -64); + uint8_t* data; + if(sc->type == SC_BT){ + data = calloc(data_size, sizeof(uint8_t)); + data[0] = PT_BT_PREFIX; + data[1] = PT_GET_SERIAL; + data[2] = PL_GET_SERIAL; + data[3] = 0x01; + bt_offset = 2; + } else { + //TODO expand to 128 okay? + data = calloc(data_size, sizeof(uint8_t)); + data[0] = PT_GET_SERIAL; + data[1] = PL_GET_SERIAL; + data[2] = 0x01; + } + + if(sc->type == SC_BT){ +#ifdef _WIN32 + response = sc->dev->hid_request(sc->dev, sc->idx, data, -(SMALL_BUFFER_SIZE + 1)); +#else + response = sc->dev->hid_request(sc->dev, sc->idx, data, -(SMALL_BUFFER_SIZE)); +#endif + } else { + response = sc->dev->hid_request(sc->dev, sc->idx, data, -SMALL_BUFFER_SIZE); + } if (response == NULL) { LERROR("Failed to retrieve serial number"); return false; } - if ((data[0] != PT_GET_SERIAL) && (data[1] != PL_GET_SERIAL)) { + if (sc->type != SC_BT && (data[0] != PT_GET_SERIAL) && (data[1] != PL_GET_SERIAL)) { // Sometimes, freshly connected controller is not able to send its own // serial straight away return false; + } + if (sc->type == SC_BT && data[4] == 0) { + //TODO flush so don't have to reset controller + debug_packet((char *)data, data_size); + return false; } - data[13] = 0; // to terminate string - strncpy(sc->serial, (const char*) &data[3], MAX_SERIAL_LEN); + data[13 + bt_offset] = 0; // to terminate string + debug_packet((char *)data, data_size); + strncpy(sc->serial, (const char*) &data[3 + bt_offset], MAX_SERIAL_LEN); if (sc->type == SC_DECK) snprintf(sc->id, MAX_ID_LEN, "deck%s", sc->serial); else @@ -282,16 +353,52 @@ bool lizard_mode(SCController* sc) { } bool clear_mappings(SCController* sc) { - uint8_t data[BUFFER_SIZE] = { PT_CLEAR_MAPPINGS, 0x01 }; - - if (sc->dev->hid_request(sc->dev, sc->idx, data, -64) == NULL) { - LERROR("Failed to clear mappings"); - return false; + uint8_t* data; +#ifdef _WIN32 + int data_size = sc->type == SC_BT ? BUFFER_SIZE : (SMALL_BUFFER_SIZE + 1); +#else + int data_size = sc->type == SC_BT ? BUFFER_SIZE : (SMALL_BUFFER_SIZE); +#endif + int bt_offset = 0; + if(sc->type == SC_BT){ + data = calloc(data_size, sizeof(uint8_t)); + data[0] = PT_BT_PREFIX; + data[1] = PT_CLEAR_MAPPINGS; + data[2] = 0x01; + bt_offset = 2; + } else { + //TODO expand to 128 okay? + data = calloc(data_size, sizeof(uint8_t)); + data[0] = PT_CLEAR_MAPPINGS; + data[1] = 0x01; + } + + if(sc->type == SC_BT){ +#ifdef _WIN32 + if (sc->dev->hid_request(sc->dev, sc->idx, data, -(SMALL_BUFFER_SIZE+1)) == NULL){ +#else + if (sc->dev->hid_request(sc->dev, sc->idx, data, -(SMALL_BUFFER_SIZE)) == NULL){ +#endif + LERROR("Failed to clear mappings"); + return false; + } + } else { + if (sc->dev->hid_request(sc->dev, sc->idx, data, -SMALL_BUFFER_SIZE) == NULL){ + LERROR("Failed to clear mappings"); + return false; + } } return true; } bool configure(SCController* sc) { + int bt_offset = 0; +#ifdef _WIN32 + int data_size = sc->type == SC_BT ? BUFFER_SIZE : (SMALL_BUFFER_SIZE + 1); +#else + int data_size = sc->type == SC_BT ? BUFFER_SIZE : (SMALL_BUFFER_SIZE); +#endif + if (sc->dev == NULL) // Special case, controller was disconnected, but it's not deallocated yet goto configure_fail; @@ -299,6 +406,40 @@ bool configure(SCController* sc) { uint8_t deck_configure[BUFFER_SIZE] = { PT_CONFIGURE, 0x03, 0x08, 0x07 }; if (sc->dev->hid_request(sc->dev, sc->idx, deck_configure, -64) == NULL) goto configure_fail; + } else if (sc->type == SC_BT) { + DDEBUG("configuring BT"); + +#ifdef _WIN32 + uint8_t gyro_and_timeout[SMALL_BUFFER_SIZE + 1] = { +#else + uint8_t gyro_and_timeout[SMALL_BUFFER_SIZE] = { +#endif + // Header + // 0xc0 0x87 0x0f 0x18 + PT_BT_PREFIX, PT_CONFIGURE, PL_CONFIGURE_BT, CONFIGURE_BT, + // Idle timeout + //TODO write IDLE timeout(?) + //(uint8_t)(sc->idle_timeout & 0xFF), (uint8_t)((sc->idle_timeout & 0xFF00) >> 8), + // unknown1 + 0x00, 0x00, 0x31, 0x02, 0x00, 0x08, 0x07, 0x00, 0x07, 0x07, 0x00, 0x30, + // Gyros + (sc->gyro_enabled ? 0x14 : 0), + // unknown2: + 0x00, 0x2e, + }; + uint8_t leds[65] = { PT_BT_PREFIX, PT_CONFIGURE, PL_LED, CT_LED, sc->led_level }; +#ifdef _WIN32 + if (sc->dev->hid_request(sc->dev, sc->idx, gyro_and_timeout, -(SMALL_BUFFER_SIZE + 1)) == NULL) + goto configure_fail; + if (sc->dev->hid_request(sc->dev, sc->idx, leds, -(SMALL_BUFFER_SIZE + 1)) == NULL) + goto configure_fail; +#else + if (sc->dev->hid_request(sc->dev, sc->idx, gyro_and_timeout, -(SMALL_BUFFER_SIZE)) == NULL) + goto configure_fail; + if (sc->dev->hid_request(sc->dev, sc->idx, leds, -(SMALL_BUFFER_SIZE)) == NULL) + goto configure_fail; +#endif + bt_offset = 2; } else { uint8_t leds[BUFFER_SIZE] = { PT_CONFIGURE, PL_LED, CT_LED, sc->led_level }; uint8_t gyro_and_timeout[BUFFER_SIZE] = { diff --git a/src/daemon/drivers/sc/sc.h b/src/daemon/drivers/sc/sc.h index d0b294b36..ff76b7019 100644 --- a/src/daemon/drivers/sc/sc.h +++ b/src/daemon/drivers/sc/sc.h @@ -3,6 +3,7 @@ typedef struct SCController SCController; typedef struct SCInput SCInput; +typedef struct SCByBtControllerInput SCByBtControllerInput; typedef enum { SC_WIRED = 1, @@ -51,6 +52,76 @@ struct SCInput { // uint8_t _a4[16]; }; +struct SCByBtControllerInput { + uint16_t type; + uint32_t buttons; + uint8_t ltrig; + uint8_t rtrig; + int32_t stick_x; + int32_t stick_y; + int32_t lpad_x; + int32_t lpad_y; + int32_t rpad_x; + int32_t rpad_y; + int32_t gpitch; + int32_t groll; + int32_t gyaw; + int32_t q1; + int32_t q2; + int32_t q3; + int32_t q4; +}; + +enum SCButtons { + // This may be moved later to something shared + SCB_RPADTOUCH = 0b10000000000000000000000000000, + SCB_LPADTOUCH = 0b01000000000000000000000000000, + SCB_RPAD = 0b00100000000000000000000000000, + SCB_LPAD = 0b00010000000000000000000000000, // # Same for stick but without LPadTouch + SCB_STICKPRESS = 0b00000000000000000000000000001, // # generated internally, not sent by controller + SCB_RGRIP = 0b00001000000000000000000000000, + SCB_LGRIP = 0b00000100000000000000000000000, + SCB_START = 0b00000010000000000000000000000, + SCB_C = 0b00000001000000000000000000000, + SCB_BACK = 0b00000000100000000000000000000, + SCB_A = 0b00000000000001000000000000000, + SCB_X = 0b00000000000000100000000000000, + SCB_B = 0b00000000000000010000000000000, + SCB_Y = 0b00000000000000001000000000000, + SCB_LB = 0b00000000000000000100000000000, + SCB_RB = 0b00000000000000000010000000000, + SCB_LT = 0b00000000000000000001000000000, + SCB_RT = 0b00000000000000000000100000000, + SCB_CPADTOUCH = 0b00000000000000000000000000100, // # Available on DS4 pad + SCB_CPADPRESS = 0b00000000000000000000000000010, // # Available on DS4 pad +}; + +static uint32_t BT_BUTTONS[] = { + // Bit to SCButton + SCB_RT, // 00 + SCB_LT, // 01 + SCB_RB, // 02 + SCB_LB, // 03 + SCB_Y, // 04 + SCB_B, // 05 + SCB_X, // 06 + SCB_A, // 07 + 0, // 08 - dpad, ignored + 0, // 09 - dpad, ignored + 0, // 10 - dpad, ignored + 0, // 11 - dpad, ignored + SCB_BACK, // 12 + SCB_C, // 13 + SCB_START, // 14 + SCB_LGRIP, // 15 + SCB_RGRIP, // 16 + SCB_LPAD, // 17 + SCB_RPAD, // 18 + SCB_LPADTOUCH, // 19 + SCB_RPADTOUCH, // 20 + 0, // 21 - nothing + SCB_STICKPRESS, // 22 +}; struct SCController { Controller controller; @@ -59,10 +130,11 @@ struct SCController { SCControllerType type; SCControllerState state; InputDevice* dev; + bool long_packet; char serial[MAX_SERIAL_LEN]; char desc[MAX_DESC_LEN]; char id[MAX_ID_LEN]; - uint16_t idx; + int16_t idx; bool gyro_enabled; uint64_t auto_id; bool auto_id_used; @@ -89,6 +161,7 @@ typedef enum { PT_FEEDBACK = 0x8f, PT_RESET = 0x95, PT_GET_SERIAL = 0xAE, + PT_BT_PREFIX = 0xC0 } SCPacketType; typedef enum { diff --git a/src/daemon/input_bsd.c b/src/daemon/input_bsd.c index bf9a72303..f730fc069 100644 --- a/src/daemon/input_bsd.c +++ b/src/daemon/input_bsd.c @@ -62,7 +62,7 @@ static void sccd_input_bsd_hid_write(InputDevice* _dev, uint16_t idx, uint8_t* d WARN("sccd_usb_dev_hid_write: USB_SET_REPORT: %s", strerror(err)); } -static uint8_t* sccd_input_bsd_hid_request(InputDevice* _dev, uint16_t idx, uint8_t* data, int32_t _length) { +static uint8_t* sccd_input_bsd_hid_request(InputDevice* _dev, int16_t idx, uint8_t* data, int32_t _length) { BSDInputDevice* dev = container_of(_dev, BSDInputDevice, dev); uint16_t length; uint8_t* out_buffer = NULL; diff --git a/src/daemon/input_hidapi.c b/src/daemon/input_hidapi.c index 2edefa091..72b6c8bbc 100644 --- a/src/daemon/input_hidapi.c +++ b/src/daemon/input_hidapi.c @@ -18,6 +18,7 @@ #include #define BUFFER_MAX 256 +#define SMALL_BUFFER_MAX 64 #define DEV_TYPECHECK(dev, method_name, err_return) do { \ if (dev->sys != HIDAPI) { \ LERROR("" #method_name " called on device of subsystem %i", dev->sys); \ @@ -93,7 +94,7 @@ static void sccd_input_hidapi_hid_write(InputDevice* _dev, uint16_t idx, uint8_t hid_write(dev->hid, data, length); } -static uint8_t* sccd_input_hidapi_hid_request(InputDevice* _dev, uint16_t idx, uint8_t* data, int32_t _length) { +static uint8_t* sccd_input_hidapi_hid_request(InputDevice* _dev, int16_t idx, uint8_t* data, int32_t _length) { DEV_TYPECHECK(_dev, sccd_input_hidapi_dev_close, NULL); HidapiInputDevice* dev = container_of(_dev, HidapiInputDevice, dev); @@ -114,15 +115,35 @@ static uint8_t* sccd_input_hidapi_hid_request(InputDevice* _dev, uint16_t idx, u } } - unsigned char buffer[BUFFER_MAX + 1]; - if (length > BUFFER_MAX) { - LERROR("sccd_input_hid_request: called with length larger" - "than supported. Changing BUFFER_MAX will fix this issue"); + //BT buffer + int ACTUAL_BUFFER = BUFFER_MAX; + if(idx == -42){ + ACTUAL_BUFFER = SMALL_BUFFER_MAX; + } + +#ifdef _WIN32 + unsigned char buffer[ACTUAL_BUFFER + 1]; + + //NEF TODO + if (length > ACTUAL_BUFFER+1) { + LERROR("sccd_input_hid_request: called with length larger %u > %d" + "than supported. Changing BUFFER_MAX will fix this issue", length, ACTUAL_BUFFER+1); + return NULL; + } +#else + unsigned char buffer[ACTUAL_BUFFER]; + //NEF TODO + if (length > ACTUAL_BUFFER) { + LERROR("sccd_input_hid_request: called with length larger %u > %d" + "than supported. Changing BUFFER_MAX will fix this issue", length, ACTUAL_BUFFER); return NULL; } - buffer[0] = 0; +#endif + + //TODO dont rely on IDX -1 quirk for BT report ID, test other controllers + buffer[0] = (idx == -1 ? 0x03 : 0x00); memcpy(&buffer[1], data, length); - if (dev->idx != idx) { + if (dev->idx != -42 && dev->idx != idx) { LERROR("sccd_input_hid_request: trying to send request to " "different idx than device was originally opened for (%i != %i)", dev->idx, idx); @@ -130,13 +151,13 @@ static uint8_t* sccd_input_hidapi_hid_request(InputDevice* _dev, uint16_t idx, u } err = hid_send_feature_report(dev->hid, buffer, length + 1); if (err < 0) { - wcstombs((char*)buffer, hid_error(dev->hid), BUFFER_MAX); + wcstombs((char*)buffer, hid_error(dev->hid), ACTUAL_BUFFER); LERROR("sccd_input_hid_request: hid_send_feature_report failed: %s", buffer); goto sccd_input_hid_request_fail; } err = hid_get_feature_report(dev->hid, buffer, length + 1); if (err < 0) { - wcstombs((char*)buffer, hid_error(dev->hid), BUFFER_MAX); + wcstombs((char*)buffer, hid_error(dev->hid), ACTUAL_BUFFER); LERROR("sccd_input_hid_request: hid_get_feautre_report failed: %s", buffer); goto sccd_input_hid_request_fail; } @@ -207,6 +228,7 @@ InputDevice* sccd_input_hidapi_open(const char* syspath) { free(dev); return NULL; } + DDEBUG("successfully opened device %s", device_path); dev->hid = hid; *((Subsystem*)(&dev->dev.sys)) = HIDAPI; @@ -226,9 +248,16 @@ InputDevice* sccd_input_hidapi_open(const char* syspath) { char* endptr = NULL; dev->idx = strtol(hex_str, &endptr, 16); if (endptr == hex_str) { - // Parsing failed + // Parsing failed dev->idx = -1; } + // TODO Add more criteria to matching + char* rev_component = strstr(device_path, "rev&0001"); + char* unknown_component = strstr(device_path, "0&0002"); + if(rev_component && unknown_component){ + //most likely a SC + dev->idx = -42; + } } #endif return &dev->dev; @@ -253,6 +282,7 @@ void sccd_input_hidapi_rescan() { wdev.product = dev->product_id; wdev.vendor = dev->vendor_id; wdev.idx = dev->interface_number; + DDEBUG("%u %u", dev->usage, dev->usage_page); sccd_device_monitor_new_device(get_daemon(), &wdev.idev); #endif dev = dev->next; diff --git a/src/daemon/input_libusb.c b/src/daemon/input_libusb.c index f82c97b69..281f6df6e 100644 --- a/src/daemon/input_libusb.c +++ b/src/daemon/input_libusb.c @@ -189,7 +189,7 @@ static void sccd_input_libusb_hid_write(InputDevice* _dev, uint16_t idx, uint8_t LERROR("sccd_input_libusb_hid_write: out: %s", libusb_strerror(err)); } -static uint8_t* sccd_input_libusb_hid_request(InputDevice* _dev, uint16_t idx, uint8_t* data, int32_t _length) { +static uint8_t* sccd_input_libusb_hid_request(InputDevice* _dev, int16_t idx, uint8_t* data, int32_t _length) { DEV_TYPECHECK(_dev, sccd_input_libusb_hid_request, NULL); USBInputDevice* dev = container_of(_dev, USBInputDevice, dev);