From a5eb4ba1296c6279e5535bf3bcc1f1796707f6c6 Mon Sep 17 00:00:00 2001 From: Lars-Dominik Braun Date: Thu, 5 Jul 2012 16:53:55 +0200 Subject: Initial import --- COPYING | 21 ++++ Makefile | 24 ++++ src/common.c | 37 ++++++ src/common.h | 50 ++++++++ src/device.c | 265 +++++++++++++++++++++++++++++++++++++++++++ src/device.h | 120 ++++++++++++++++++++ src/feature/battery.c | 148 ++++++++++++++++++++++++ src/feature/battery.h | 32 ++++++ src/feature/reprogrammable.c | 162 ++++++++++++++++++++++++++ src/feature/reprogrammable.h | 32 ++++++ src/lshidpp.c | 115 +++++++++++++++++++ src/unbrick.c | 61 ++++++++++ src/usb.c | 194 +++++++++++++++++++++++++++++++ src/usb.h | 67 +++++++++++ 14 files changed, 1328 insertions(+) create mode 100644 COPYING create mode 100644 Makefile create mode 100644 src/common.c create mode 100644 src/common.h create mode 100644 src/device.c create mode 100644 src/device.h create mode 100644 src/feature/battery.c create mode 100644 src/feature/battery.h create mode 100644 src/feature/reprogrammable.c create mode 100644 src/feature/reprogrammable.h create mode 100644 src/lshidpp.c create mode 100644 src/unbrick.c create mode 100644 src/usb.c create mode 100644 src/usb.h diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..8d7eb04 --- /dev/null +++ b/COPYING @@ -0,0 +1,21 @@ +Copyright (c) 2012 + Lars-Dominik Braun + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4f66dd2 --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +CFLAGS=-Wall -Wextra -g $(shell pkg-config --cflags 'libusb-1.0') -I src -I src/feature +LDFLAGS=$(shell pkg-config --libs 'libusb-1.0') +CC=c99 + +LSHIDPP_SRC=src/lshidpp.c \ + src/device.c \ + src/usb.c \ + src/common.c \ + src/feature/battery.c \ + src/feature/reprogrammable.c +LSHIDPP_OBJ=${LSHIDPP_SRC:.c=.o} + +UNBRICK_SRC=src/unbrick.c +UNBRICK_OBJ=${UNBRICK_SRC:.c=.o} + +all: lshidpp unbrick +lshidpp: ${LSHIDPP_OBJ} + ${CC} -o $@ ${LSHIDPP_OBJ} ${LDFLAGS} + +unbrick: ${UNBRICK_OBJ} + ${CC} -o $@ ${UNBRICK_OBJ} ${LDFLAGS} + +clean: + ${RM} ${LSHIDPP_OBJ} ${UNBRICK_OBJ} diff --git a/src/common.c b/src/common.c new file mode 100644 index 0000000..c4f5ed9 --- /dev/null +++ b/src/common.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2012 + * Lars-Dominik Braun + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include + +#include "common.h" + +void dumphex (const char *prefix, const unsigned char *data, const size_t size) { + fprintf (stderr, "%s", prefix); + for (size_t i = 0; i < size; i++) { + fprintf (stderr, "%02X ", data[i]); + if ((i+1) % 16 == 0) { + fprintf (stderr, "\n%s", prefix); + } + } + fprintf (stderr, "\n"); +} diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..b8d5aa9 --- /dev/null +++ b/src/common.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2012 + * Lars-Dominik Braun + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef _COMMON_H +#define _COMMON_H + +#include + +#include "device.h" + +typedef enum { + HIDPP_RET_OK = 0, + HIDPP_RET_ERROR, + HIDPP_RET_USB_ERROR, +} hidppReturn_t; + +typedef bool (*hidppAvailableFunction_t) (const hidppDevice_t * const d); +typedef void (*hidppReadFunction_t) (hidppDevice_t * const d, void ** const data); +typedef void (*hidppPrintFunction_t) (const void * const data); + +typedef struct { + hidppAvailableFunction_t available; + hidppReadFunction_t read; + hidppPrintFunction_t print; +} hidppModule_t; + +void dumphex (const char *prefix, const unsigned char *data, const size_t size); + +#endif /* _COMMON_H */ + diff --git a/src/device.c b/src/device.c new file mode 100644 index 0000000..4f92599 --- /dev/null +++ b/src/device.c @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2012 + * Lars-Dominik Braun + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "device.h" +#include "usb.h" + +/* free hid++ device handle + */ +void hidppDeviceDestroy (hidppDevice_t * const d) { + assert (d != NULL); + + free (d->features); + free (d->name); + free (d->firmware); + memset (d, 0, sizeof (*d)); +} + +/* populate device handle + */ +void hidppDeviceInit (hidppDevice_t * const d, + libusb_device_handle * const usbDev, const hidppDeviceId_t id) { + hidppPacket_t rp; + + assert (d != NULL); + assert (usbDev != NULL); + + memset (d, 0, sizeof (*d)); + + d->usbDev = usbDev; + d->id = id; + + /* send ping/get version */ + hidppUsbRequest (d, HIDPP_FEATURE_ROOT, 0x1, NULL, 0, &rp); + if (rp.error == HIDPP_PERROR_UNKNOWN) { + /* old hid++ 1.0 device */ + d->hidVersion.major = 1; + d->hidVersion.minor = 0; + } else { + d->hidVersion.major = rp.args[0]; + d->hidVersion.minor = rp.args[1]; + } + + /* get featureIdx of 0x0001 (IFeatureSet) */ + hidppUsbRequest (d, HIDPP_FEATURE_ROOT, 0x0, + (const uint8_t *) "\x00\x01\x00", 3, &rp); + /* temporary features table */ + d->featuresCount = rp.args[0]+1; + d->features = calloc (d->featuresCount, sizeof (*d->features)); + assert (d->features != NULL); + d->features[d->featuresCount-1].id = HIDPP_FEATURE_SET; + + /* get features count */ + hidppUsbRequest (d, HIDPP_FEATURE_SET, 0, NULL, 0, &rp); + /* return value does not count feature idx 0 */ + d->featuresCount = rp.args[0]+1; + d->features = realloc (d->features, d->featuresCount * sizeof (*d->features)); + assert (d->features != NULL); + + /* populate feature index table, index x is reserved for IRoot */ + for (uint8_t f = 1; f < d->featuresCount; f++) { + /* get feature */ + hidppUsbRequest (d, HIDPP_FEATURE_SET, 0x1, &f, 1, &rp); + /* FIXME: what about big-endian machines? */ + d->features[f].id = rp.args[0]<<8 | rp.args[1]; + d->features[f].flags = rp.args[2]; + } + + /* device type */ + hidppUsbRequest (d, HIDPP_FEATURE_DEVICE_INFO, 0x2, NULL, 0, &rp); + d->type = rp.args[0]; + + /* device name */ + hidppUsbRequest (d, HIDPP_FEATURE_DEVICE_INFO, 0x0, NULL, 0, &rp); + size_t len = rp.args[0]; + d->name = calloc (len+1, sizeof (*d->name)); + + do { + uint8_t pos = strlen (d->name); + hidppUsbRequest (d, HIDPP_FEATURE_DEVICE_INFO, 0x1, &pos, 1, &rp); + strcat (d->name, (char *) rp.args); + /* FIXME: ugly */ + } while (strlen (d->name) < len); + + /* firmware versions */ + hidppUsbRequest (d, HIDPP_FEATURE_FIRMWARE_INFO, 0x0, NULL, 0, &rp); + d->firmwareCount = rp.args[0]; + d->firmware = calloc (d->firmwareCount, sizeof (*d->firmware)); + + for (uint8_t i = 0; i < d->firmwareCount; i++) { + hidppUsbRequest (d, HIDPP_FEATURE_FIRMWARE_INFO, 0x1, &i, 1, &rp); + memcpy (d->firmware[i].prefix, &rp.args[1], 3); + d->firmware[i].version = rp.args[4]<<8 | rp.args[5]; + d->firmware[i].build = rp.args[6]<<8 | rp.args[7]; + } +} + +const char *hidppDeviceTypeToStr (const hidppDeviceType_t t) { + switch (t) { + case HIDPP_DEV_KEYBOARD: + return "keyboard"; + break; + + case HIDPP_DEV_REMOTE_CONTROL: + return "remote control"; + break; + + case HIDPP_DEV_NUMPAD: + return "numpad"; + break; + + case HIDPP_DEV_MOUSE: + return "mouse"; + break; + + case HIDPP_DEV_TOUCHPAD: + return "touchpad"; + break; + + case HIDPP_DEV_TRACKBALL: + return "trackball"; + break; + + case HIDPP_DEV_PRESENTER: + return "presenter"; + break; + + case HIDPP_DEV_RECEIVER: + /* FIXME: unifying receiver? */ + return "receiver"; + break; + } + return "unknown"; +} + +const char *hidppFirmwareTypeToStr (const hidppFirmwareType_t t) { + switch (t) { + case HIDPP_FW_TYPE_MAIN: + return "Firmware version"; + break; + + case HIDPP_FW_TYPE_BOOT: + return "Bootloader version"; + break; + + case HIDPP_FW_TYPE_HARDWARE: + return "Hardware version"; + break; + } + return "unknown version"; +} + +const char *hidppFeatureIdToStr (const hidppFeatureId_t f) { + switch (f) { + case HIDPP_FEATURE_ROOT: + return "root"; + break; + + case HIDPP_FEATURE_SET: + return "list features"; + break; + + case HIDPP_FEATURE_FIRMWARE_INFO: + return "firmware info"; + break; + + case HIDPP_FEATURE_DEVICE_INFO: + return "device info"; + break; + + case HIDPP_FEATURE_BATTERY_STATUS: + return "battery status"; + break; + + case HIDPP_FEATURE_PROGRAMMABLE_KEYS: + return "list programmable keys"; + break; + + case HIDPP_FEATURE_WIRELESS_STATUS: + return "wireless status"; + break; + } + return "unknown"; +} + + +void hidppDevicePrint (const hidppDevice_t * const d) { + printf ("Device:\n" + " Name: %s\n" + " Type: %s (%u)\n" + " HID++ version: %u.%u\n", + d->name, hidppDeviceTypeToStr (d->type), d->type, + d->hidVersion.major, + d->hidVersion.minor); + for (size_t i = 0; i < d->firmwareCount; i++) { + const hidppFirmware_t * const f = &d->firmware[i]; + printf (" %s: %s.%03x.%03x.%05x\n", hidppFirmwareTypeToStr (i), + f->prefix, f->version>>8, f->version&0xff, f->build); + } + /* ignoring the first feature */ + printf (" Features: %zu\n", d->featuresCount-1); + for (uint8_t fIdx = 1; fIdx < d->featuresCount; fIdx++) { + const hidppFeature_t * const f = &d->features[fIdx]; + printf (" Feature: %s (%u)\n" + " Feature id: 0x%04x\n" + " Flags:%s%s%s (0x%x)\n", + hidppFeatureIdToStr (f->id), + fIdx, + f->id, + f->flags & HIDPP_FF_OBSOLETE ? " obsolete" : "", + f->flags & HIDPP_FF_HIDDEN ? " hidden" : "", + f->flags & HIDPP_FF_LOGITECH ? " logitech reserved" : "", + f->flags); + } +} + +/* translate global feature id to device-dependent index + */ +bool hidppFeatureIdToIdx (const hidppDevice_t * const d, + const hidppFeatureId_t id, uint8_t * const retIdx) { + assert (d != NULL); + /* 16 bits */ + assert (id < (1<<16)); + assert (retIdx != NULL); + + /* root feature is always at 0 */ + if (id == HIDPP_FEATURE_ROOT) { + *retIdx = 0; + return true; + } + + /* FIXME: hashtable? */ + for (uint8_t idx = 0; idx < d->featuresCount; idx++) { + if (id == d->features[idx].id) { + *retIdx = idx; + return true; + } + } + return false; +} + diff --git a/src/device.h b/src/device.h new file mode 100644 index 0000000..b845890 --- /dev/null +++ b/src/device.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2012 + * Lars-Dominik Braun + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef _DEVICE_H +#define _DEVICE_H + +#include + +#include + +typedef enum { + HIDPP_DEVID_ROOT = 0xff, +} hidppDeviceId_t; + +/* HID++ version */ +typedef struct { + uint8_t major, minor; +} hidppVersion_t; + +/* firmware type */ +typedef enum { + HIDPP_FW_TYPE_MAIN = 0, + HIDPP_FW_TYPE_BOOT = 1, + HIDPP_FW_TYPE_HARDWARE = 2, + /* _OTHER >= 3 */ +} hidppFirmwareType_t; + +/* firmware version object */ +typedef struct { + char prefix[4]; + uint16_t version; + uint16_t build; +} hidppFirmware_t; + +/* device types */ +typedef enum { + HIDPP_DEV_KEYBOARD = 0, + HIDPP_DEV_REMOTE_CONTROL = 1, + HIDPP_DEV_NUMPAD = 2, + HIDPP_DEV_MOUSE = 3, + HIDPP_DEV_TOUCHPAD = 4, + HIDPP_DEV_TRACKBALL = 5, + HIDPP_DEV_PRESENTER = 6, + HIDPP_DEV_RECEIVER = 7, +} hidppDeviceType_t; + +/* feature id’s */ +typedef enum { + HIDPP_FEATURE_ROOT = 0x0000, + HIDPP_FEATURE_SET = 0x0001, + HIDPP_FEATURE_FIRMWARE_INFO = 0x0003, + HIDPP_FEATURE_DEVICE_INFO = 0x0005, + HIDPP_FEATURE_BATTERY_STATUS = 0x1000, + HIDPP_FEATURE_PROGRAMMABLE_KEYS = 0x1b00, + HIDPP_FEATURE_WIRELESS_STATUS = 0x1d4b, +} hidppFeatureId_t; + +/* feature flags (bitfield) */ +typedef enum { + HIDPP_FF_OBSOLETE = (1 << 7), + HIDPP_FF_HIDDEN = (1 << 6), + HIDPP_FF_LOGITECH = (1 << 5), +} hidppFeatureFlags_t; + +typedef struct { + hidppFeatureId_t id; + hidppFeatureFlags_t flags; +} hidppFeature_t; + +struct hidppDevice { + libusb_device_handle *usbDev; + + /* general */ + hidppDeviceId_t id; + hidppDeviceType_t type; + hidppVersion_t hidVersion; + char *name; + + /* feature index table */ + size_t featuresCount; + hidppFeature_t *features; + /* firmware versions */ + size_t firmwareCount; + hidppFirmware_t *firmware; + + /* children (if any) */ + size_t childrenCount; + struct hidppDevice *children; +} hidppDevice; +typedef struct hidppDevice hidppDevice_t; + +void hidppDeviceDestroy (hidppDevice_t * const d); +void hidppDeviceInit (hidppDevice_t * const d, + libusb_device_handle * const usbDev, const hidppDeviceId_t id); +void hidppDevicePrint (const hidppDevice_t * const d); +bool hidppFeatureIdToIdx (const hidppDevice_t * const d, + const hidppFeatureId_t id, uint8_t * const retIdx); + +#endif /* _DEVICE_H */ + diff --git a/src/feature/battery.c b/src/feature/battery.c new file mode 100644 index 0000000..7e2ed26 --- /dev/null +++ b/src/feature/battery.c @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2012 + * Lars-Dominik Braun + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "battery.h" +#include "usb.h" +#include "device.h" + +typedef enum { + HIDPP_BATTERY_STATUS_DISCHARGING = 0, + HIDPP_BATTERY_STATUS_RECHARGING = 1, + HIDPP_BATTERY_STATUS_CHARGE_FINAL = 2, + HIDPP_BATTERY_STATUS_CHARGE_COMPLETE = 3, + HIDPP_BATTERY_STATUS_RECHARGING_SLOW = 4, + HIDPP_BATTERY_STATUS_INVALID_BATTERY = 5, + HIDPP_BATTERY_STATUS_THERMAL_ERROR = 6, + HIDPP_BATTERY_STATUS_CHARGE_ERROR = 7, +} hidppBatteryStatus_t; + +typedef enum { + HIDPP_BATTERY_FLAGS_DISABLED = (1 << 0), + HIDPP_BATTERY_FLAGS_MILEAGE = (1 << 1), + HIDPP_BATTERY_FLAGS_RECHARGEABLE = (1 << 2), +} hidppBatteryFlags_t; + +typedef struct { + uint8_t level, nextLevel, criticalLevel; /* in percent */ + hidppBatteryStatus_t status; + uint8_t levelsCount; + hidppBatteryFlags_t flags; + uint16_t life; +} hidppBattery_t; + +const char *hidppBatteryStatusToStr (const hidppBatteryStatus_t s) { + switch (s) { + case HIDPP_BATTERY_STATUS_DISCHARGING: + return "discharging"; + break; + + case HIDPP_BATTERY_STATUS_RECHARGING: + return "recharging"; + break; + + case HIDPP_BATTERY_STATUS_CHARGE_FINAL: + return "charge in final state"; + break; + + case HIDPP_BATTERY_STATUS_CHARGE_COMPLETE: + return "charge complete"; + break; + + case HIDPP_BATTERY_STATUS_RECHARGING_SLOW: + return "recharging below optimal speed"; + break; + + case HIDPP_BATTERY_STATUS_INVALID_BATTERY: + return "invalid battery"; + break; + + case HIDPP_BATTERY_STATUS_THERMAL_ERROR: + return "thermal error"; + break; + + case HIDPP_BATTERY_STATUS_CHARGE_ERROR: + return "charge error"; + break; + + default: + return "unkown"; + break; + } +} + +void hidppBatteryPrint (const void * const data) { + const hidppBattery_t * const b = data; + + assert (b != NULL); + + printf (" Battery:\n" + " Status: %s (%u)\n" + " Level: %u%%\n" + " Next level: %u%%\n" + " Critical level: %u%%\n" + " Number of levels: %u\n" + " Flags:%s%s%s (0x%x)\n" + " Life: %u\n", + hidppBatteryStatusToStr (b->status), b->status, + b->level, b->nextLevel, b->criticalLevel, b->levelsCount, + (b->flags & HIDPP_BATTERY_FLAGS_DISABLED) ? " disabled" : "", + (b->flags & HIDPP_BATTERY_FLAGS_MILEAGE) ? " mileage" : "", + (b->flags & HIDPP_BATTERY_FLAGS_RECHARGEABLE) ? " rechargeable" : "", + b->flags, + b->life); +} + +void hidppBatteryGet (hidppDevice_t * const d, void ** const data) { + hidppBattery_t *b; + hidppPacket_t rp; + + assert (d != NULL); + + b = calloc (1, sizeof (*b)); + + hidppUsbRequest (d, HIDPP_FEATURE_BATTERY_STATUS, 0x0, NULL, 0, &rp); + b->level = rp.args[0]; + b->nextLevel = rp.args[1]; + + hidppUsbRequest (d, HIDPP_FEATURE_BATTERY_STATUS, 0x1, NULL, 0, &rp); + b->levelsCount = rp.args[0]; + b->flags = rp.args[1]; + b->life = (rp.args[2] << 8) | rp.args[3]; + b->criticalLevel = rp.args[4]; + + *data = b; +} + +bool hidppBatteryAvailable (const hidppDevice_t * const d) { + uint8_t ret; + return hidppFeatureIdToIdx (d, HIDPP_FEATURE_BATTERY_STATUS, &ret); +} + +hidppModule_t moduleBattery = {hidppBatteryAvailable, hidppBatteryGet, + hidppBatteryPrint}; + diff --git a/src/feature/battery.h b/src/feature/battery.h new file mode 100644 index 0000000..d53c0bf --- /dev/null +++ b/src/feature/battery.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2012 + * Lars-Dominik Braun + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef _FEATURE_BATTERY_H +#define _FEATURE_BATTERY_H + +#include "common.h" + +extern hidppModule_t moduleBattery; + +#endif /* _FEATURE_BATTERY_H */ + diff --git a/src/feature/reprogrammable.c b/src/feature/reprogrammable.c new file mode 100644 index 0000000..2c3b61d --- /dev/null +++ b/src/feature/reprogrammable.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2012 + * Lars-Dominik Braun + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "reprogrammable.h" +#include "usb.h" +#include "device.h" + +typedef enum { + HIDPP_TASK_VOLUP = 1, + HIDPP_TASK_VOLDOWN = 2, + HIDPP_TASK_MUTE = 3, + HIDPP_TASK_PLAYPAUSE = 4, + HIDPP_TASK_NEXTFF = 5, + HIDPP_TASK_PREVRW = 6, + HIDPP_TASK_STOP = 7, + HIDPP_TASK_APPSWITCH = 8, +} hidppReprogrammableKeyTask_t; + +typedef enum { + /* reprogrammmable */ + HIDPP_RK_REPGROGRAMMABLE = (1 << 4), + /* affected by fn key */ + HIDPP_RK_FNTOGGLE = (1 << 3), + /* hotkey */ + HIDPP_RK_HOTKEY = (1 << 2), + /* fn key */ + HIDPP_RK_FN = (1 << 1), + /* mouse button */ + HIDPP_RK_MOUSE = (1 << 0), +} hidppReprogrammableKeyFlags_t; + +typedef struct { + uint16_t id; + hidppReprogrammableKeyTask_t task; + hidppReprogrammableKeyFlags_t flags; +} hidppReprogrammableKey_t; + +typedef struct { + hidppReprogrammableKey_t *key; + size_t count; +} hidppReprogrammableData_t; + +const char *hidppReprogrammableTaskToStr (const hidppReprogrammableKeyTask_t t) { + switch (t) { + case HIDPP_TASK_VOLUP: + return "volume up"; + break; + + case HIDPP_TASK_VOLDOWN: + return "volume down"; + break; + + case HIDPP_TASK_MUTE: + return "mute"; + break; + + case HIDPP_TASK_PLAYPAUSE: + return "play/pause"; + break; + + case HIDPP_TASK_NEXTFF: + return "next/fast forward"; + break; + + case HIDPP_TASK_PREVRW: + return "previous/rewind"; + break; + + case HIDPP_TASK_STOP: + return "stop"; + break; + + case HIDPP_TASK_APPSWITCH: + return "application switcher"; + break; + + default: + return "unknown"; + break; + } +} + +void hidppReprogrammablePrint (const void * const data) { + const hidppReprogrammableData_t * const d = data; + + assert (d != NULL); + + for (size_t i = 0; i < d->count; i++) { + const hidppReprogrammableKey_t * const k = &d->key[i]; + printf (" Key: (0x%x)\n" + " Task: %s (%u)\n" + " Flags:%s%s%s%s%s (0x%x)\n", + k->id, + hidppReprogrammableTaskToStr (k->task), k->task, + (k->flags & HIDPP_RK_REPGROGRAMMABLE) ? " reprogrammable" : "", + (k->flags & HIDPP_RK_FNTOGGLE) ? " fntoggle" : "", + (k->flags & HIDPP_RK_HOTKEY) ? " hotkey" : "", + (k->flags & HIDPP_RK_FN) ? " fn" : "", + (k->flags & HIDPP_RK_MOUSE) ? " mouse" : "", + k->flags); + } +} + +void hidppReprogrammableGet (hidppDevice_t *d, void ** const retData) { + hidppReprogrammableData_t *data; + hidppPacket_t rp; + + assert (d != NULL); + assert (retData != NULL); + + data = calloc (1, sizeof (*data)); + + hidppUsbRequest (d, HIDPP_FEATURE_PROGRAMMABLE_KEYS, 0x0, NULL, 0, &rp); + data->count = rp.args[0]; + data->key = calloc (data->count, sizeof (*data->key)); + + for (uint8_t i = 0; i < data->count; i++) { + hidppReprogrammableKey_t * const k = &data->key[i]; + + hidppUsbRequest (d, HIDPP_FEATURE_PROGRAMMABLE_KEYS, 0x1, &i, 1, &rp); + + k->id = (rp.args[0] << 8) | rp.args[1]; + k->task = (rp.args[2] << 8) | rp.args[3]; + k->flags = rp.args[4]; + } + + *retData = data; +} + +bool hidppReprogrammableAvailable (const hidppDevice_t * const d) { + uint8_t ret; + return hidppFeatureIdToIdx (d, HIDPP_FEATURE_PROGRAMMABLE_KEYS, &ret); +} + +hidppModule_t moduleReprogrammable = {hidppReprogrammableAvailable, + hidppReprogrammableGet, hidppReprogrammablePrint}; + diff --git a/src/feature/reprogrammable.h b/src/feature/reprogrammable.h new file mode 100644 index 0000000..90ec325 --- /dev/null +++ b/src/feature/reprogrammable.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2012 + * Lars-Dominik Braun + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef _FEATURE_REPROGRAMMABLE_H +#define _FEATURE_REPROGRAMMABLE_H + +#include "common.h" + +extern hidppModule_t moduleReprogrammable; + +#endif /* _FEATURE_REPROGRAMMABLE_H */ + diff --git a/src/lshidpp.c b/src/lshidpp.c new file mode 100644 index 0000000..cbbaa5a --- /dev/null +++ b/src/lshidpp.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2012 + * Lars-Dominik Braun + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* getopt() */ +#define _POSIX_C_SOURCE 2 + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "device.h" +#include "usb.h" +#include "battery.h" +#include "reprogrammable.h" + +#define HIDPP_USB_VENDOR 0x46d +/* unifying receiver */ +#define HIDPP_USB_PRODUCT 0xc52b + +int main (int argc, char **argv) { + libusb_context *ctx; + int ret; + ssize_t devicesCount; + libusb_device **devices; + hidppModule_t modules[] = {moduleBattery, moduleReprogrammable}; + + int opt; + while ((opt = getopt (argc, argv, "s:")) != -1) { + switch (opt) { + /* bus:device, same flag as `lsusb` */ + case 's': + break; + + default: /* '?' */ + exit (EXIT_FAILURE); + break; + } + } + + ret = libusb_init (&ctx); + if (ret != LIBUSB_SUCCESS) { + return ret; + } + + libusb_set_debug (ctx, 3); + + devicesCount = libusb_get_device_list (ctx, &devices); + for (size_t i = 0; i < devicesCount; i++) { + struct libusb_device_descriptor desc; + int r = libusb_get_device_descriptor(devices[i], &desc); + if (r < 0) { + fprintf(stderr, "failed to get device descriptor"); + continue; + } + printf ("device bus=%x, address=%x, vendor=%x, product=%x\n", + libusb_get_bus_number (devices[i]), + libusb_get_device_address (devices[i]), + desc.idVendor, desc.idProduct); + if (desc.idVendor == HIDPP_USB_VENDOR && desc.idProduct == HIDPP_USB_PRODUCT) { + hidppDevice_t d; + libusb_device_handle *h; + + hidppUsbDeviceOpen (devices[i], &h); + hidppDeviceInit (&d, h, 0x01); + hidppDevicePrint (&d); + + for (size_t j = 0; j < sizeof (modules)/sizeof (*modules); j++) { + hidppModule_t *m = &modules[j]; + void *data; + assert (m->available != NULL); + if (m->read != NULL && m->available (&d)) { + assert (m->print != NULL); + m->read (&d, &data); + m->print (data); + free (data); + } + } + + hidppDeviceDestroy (&d); + hidppUsbDeviceClose (h); + } + } + libusb_free_device_list (devices, devicesCount); + + libusb_exit (ctx); + + return 0; +} + diff --git a/src/unbrick.c b/src/unbrick.c new file mode 100644 index 0000000..64ffefa --- /dev/null +++ b/src/unbrick.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2012 + * Lars-Dominik Braun + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* “Unbrick” device by reattaching the kernel driver. + */ + +#include +#include + +int main () { + libusb_context *ctx; + int ret; + ssize_t devicesCount; + libusb_device **devices; + + ret = libusb_init (&ctx); + if (ret != LIBUSB_SUCCESS) { + return ret; + } + + libusb_set_debug (ctx, 3); + + devicesCount = libusb_get_device_list (ctx, &devices); + for (size_t i = 0; i < devicesCount; i++) { + libusb_device_handle *handle; + + ret = libusb_open (devices[i], &handle); + printf ("open device: %s\n", libusb_error_name (ret)); + + ret = libusb_attach_kernel_driver (handle, 2); + printf ("reattaching kernel driver: %s\n", libusb_error_name (ret)); + + libusb_close (handle); + } + libusb_free_device_list (devices, devicesCount); + + libusb_exit (ctx); + + return 0; +} + diff --git a/src/usb.c b/src/usb.c new file mode 100644 index 0000000..050afc9 --- /dev/null +++ b/src/usb.c @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2012 + * Lars-Dominik Braun + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include + +#include "usb.h" + +/* our software identifier */ +#define HIDPP_SOFTWARE_ID 0xB +#define HIDPP_USB_INTERFACE 2 + +/* send packet to device, blocking + */ +int hidppUsbSend (libusb_device_handle * const dev, + const hidppPacket_t * const p) { + assert (dev != NULL); + assert (p != NULL); + //assert (p->devId >= 0x1); + assert (p->devId <= HIDPP_DEVID_ROOT); + + const unsigned int timeout = 500; + unsigned char packet[7] = {p->id, p->devId, p->featureIdx, + (p->function<<4) | p->softwareId}; + int ret; + memcpy (&packet[4], p->args, 3); + + /* FIXME: magic numbers */ + ret = libusb_control_transfer (dev, LIBUSB_REQUEST_TYPE_CLASS | + LIBUSB_RECIPIENT_INTERFACE, LIBUSB_REQUEST_SET_CONFIGURATION, + 0x0210, 0x0002, packet, sizeof (packet)/sizeof (*packet), timeout); + if (ret < 0) { + printf ("<< failed! %s", libusb_error_name (ret)); + return HIDPP_RET_USB_ERROR; + } + dumphex ("<< ", packet, sizeof (packet)); + + return HIDPP_RET_OK; +} + +/* receive single packet from device, blocking + */ +hidppReturn_t hidppUsbReceive (libusb_device_handle * const dev, + hidppPacket_t * const p) { + int len, ret; + unsigned char packet[HIDPP_MAX_PACKETLEN]; + const unsigned int timeout = 500; + + assert (dev != NULL); + assert (p != NULL); + + ret = libusb_bulk_transfer (dev, 0x83, packet, sizeof (packet), &len, + timeout); + if (ret != LIBUSB_SUCCESS) { + printf (">> failed! %s\n", libusb_error_name (ret)); + return HIDPP_RET_USB_ERROR; + } + dumphex (">> ", packet, len); + + memset (p, 0, sizeof (*p)); + p->id = packet[0]; + p->devId = packet[1]; + if (packet[2] == 0x8F || packet[2] == 0xFF) { + /* error */ + assert (len > 6); + p->featureIdx = packet[3]; + p->function = (packet[4] >> 4) & 0xf; + p->softwareId = packet[4] & 0xf; + p->error = packet[6]; + } else { + assert (len > 3); + p->featureIdx = packet[2]; + p->function = (packet[3] >> 4) & 0xf; + p->softwareId = packet[3] & 0xf; + if (len > 4) { + memcpy (p->args, &packet[4], len-4); + } + } + + return HIDPP_RET_OK; +} + +/* initialize packet + */ +void hidppUsbPacketInit (hidppPacket_t * const p, const hidppDeviceId_t devId, + const uint8_t featureIdx, const uint8_t function, + const uint8_t softwareId, const uint8_t * const args, + const size_t argsSize) { + assert (p != NULL); + assert (argsSize < 16); + + memset (p, 0, sizeof (*p)); + + if (argsSize < 4) { + p->id = 0x10; + } else if (argsSize < 16) { + p->id = 0x11; + } + p->devId = devId; + p->featureIdx = featureIdx; + /* function is just four bits */ + assert (function < (1<<4)); + p->function = function & 0xf; + /* software id is just four bits */ + assert (softwareId < (1<<4)); + p->softwareId = softwareId & 0xf; + if (args != NULL) { + assert (argsSize > 0); + assert (argsSize < sizeof (p->args)/sizeof (*p->args)); + memcpy (p->args, args, argsSize); + } +} + +/* high-level request wrapper, drops events/notifications from device + */ +hidppReturn_t hidppUsbRequest (hidppDevice_t * const d, + const hidppFeatureId_t feature, const uint8_t function, + const uint8_t * const args, const size_t argsCount, + hidppPacket_t * const rp) { + hidppReturn_t ret; + hidppPacket_t sp; + uint8_t featureIdx; + + if (!hidppFeatureIdToIdx (d, feature, &featureIdx)) { + assert (0); + return HIDPP_RET_ERROR; + } + hidppUsbPacketInit (&sp, d->id, featureIdx, function, HIDPP_SOFTWARE_ID, + args, argsCount); + ret = hidppUsbSend (d->usbDev, &sp); + + while (ret == HIDPP_RET_OK) { + if ((ret = hidppUsbReceive (d->usbDev, rp)) == HIDPP_RET_OK) { + if ((rp->id != 0x10 && rp->id != 0x11) || + rp->featureIdx != sp.featureIdx || + rp->function != sp.function || + rp->softwareId != sp.softwareId) { + /* ignore non-hid++ packets, events and packets not meant for us */ + continue; + } else { + return HIDPP_RET_OK; + } + } + } + + return ret; +} + +void hidppUsbDeviceOpen (libusb_device * const dev, libusb_device_handle **handle) { + int ret; + + ret = libusb_open (dev, handle); + assert (ret == LIBUSB_SUCCESS); + + if (libusb_kernel_driver_active (*handle, HIDPP_USB_INTERFACE) == 1) { + ret = libusb_detach_kernel_driver (*handle, HIDPP_USB_INTERFACE); + assert (ret == LIBUSB_SUCCESS); + } + ret = libusb_claim_interface (*handle, HIDPP_USB_INTERFACE); + assert (ret == LIBUSB_SUCCESS); +} + +void hidppUsbDeviceClose (libusb_device_handle *handle) { + int ret; + + ret = libusb_release_interface (handle, HIDPP_USB_INTERFACE); + assert (ret == LIBUSB_SUCCESS); + ret = libusb_attach_kernel_driver (handle, HIDPP_USB_INTERFACE); + assert (ret == LIBUSB_SUCCESS); + + libusb_close (handle); +} + diff --git a/src/usb.h b/src/usb.h new file mode 100644 index 0000000..9b093d0 --- /dev/null +++ b/src/usb.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2012 + * Lars-Dominik Braun + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef _USB_H +#define _USB_H + +#include + +#include "device.h" +#include "common.h" + +typedef enum { + HIDPP_PERROR_OK = 0, /* not an error */ + HIDPP_PERROR_UNKNOWN = 1, + HIDPP_PERROR_INVALID_ARGUMENT = 2, + HIDPP_PERROR_OUT_OF_RANGE = 3, + HIDPP_PERROR_HWERROR = 4, + HIDPP_PERROR_LOGITECH_INTERNAL = 5, + HIDPP_PERROR_INVALID_FEATURE = 6, + HIDPP_PERROR_INVALID_FUNCTION = 7, + HIDPP_PERROR_BUSY = 8, + HIDPP_PERROR_UNSUPPORTED = 9, +} hidppPacketError_t; + +#define HIDPP_MAX_PACKETLEN 32 + +/* HID++ packet sent over USB */ +typedef struct { + uint8_t id; + hidppDeviceId_t devId; + uint8_t featureIdx; + uint8_t function; + uint8_t softwareId; + /* make sure we have enough room for at least one \0 too */ + uint8_t args[HIDPP_MAX_PACKETLEN]; + hidppPacketError_t error; +} hidppPacket_t; + +void hidppUsbDeviceOpen (libusb_device * const dev, libusb_device_handle **handle); +void hidppUsbDeviceClose (libusb_device_handle *handle); +hidppReturn_t hidppUsbRequest (hidppDevice_t * const d, + const hidppFeatureId_t feature, const uint8_t function, + const uint8_t * const args, const size_t argsCount, + hidppPacket_t * const rp); + +#endif /* _USB_H */ + -- cgit v1.2.3