summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--COPYING21
-rw-r--r--Makefile24
-rw-r--r--src/common.c37
-rw-r--r--src/common.h50
-rw-r--r--src/device.c265
-rw-r--r--src/device.h120
-rw-r--r--src/feature/battery.c148
-rw-r--r--src/feature/battery.h32
-rw-r--r--src/feature/reprogrammable.c162
-rw-r--r--src/feature/reprogrammable.h32
-rw-r--r--src/lshidpp.c115
-rw-r--r--src/unbrick.c61
-rw-r--r--src/usb.c194
-rw-r--r--src/usb.h67
14 files changed, 1328 insertions, 0 deletions
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 <lars@6xq.net>
+
+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 <lars@6xq.net>
+ *
+ * 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 <stdio.h>
+
+#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 <lars@6xq.net>
+ *
+ * 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 <stdbool.h>
+
+#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 <lars@6xq.net>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#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 <lars@6xq.net>
+ *
+ * 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 <stdbool.h>
+
+#include <libusb.h>
+
+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 <lars@6xq.net>
+ *
+ * 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 <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <assert.h>
+
+#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 <lars@6xq.net>
+ *
+ * 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 <lars@6xq.net>
+ *
+ * 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 <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <assert.h>
+
+#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: <name> (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 <lars@6xq.net>
+ *
+ * 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 <lars@6xq.net>
+ *
+ * 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 <stdio.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <libusb.h>
+
+#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 <lars@6xq.net>
+ *
+ * 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 <stdio.h>
+#include <libusb.h>
+
+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 <lars@6xq.net>
+ *
+ * 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 <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#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 <lars@6xq.net>
+ *
+ * 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 <stdint.h>
+
+#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 */
+