From e2faf04f8f6deefa10710b52756aaebc1ee1f6f2 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Tue, 30 Apr 2013 17:40:53 +0200 Subject: ltunify: preliminary HID++ 2.0 support, add TODO --- Makefile | 2 + README.txt | 4 ++ hidpp20.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ltunify.c | 24 +++++++++- 4 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 hidpp20.c diff --git a/Makefile b/Makefile index e4d026e..d93022f 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,8 @@ all: ltunify read-dev-usbmon read-dev-usbmon: read-dev-usbmon.c hidraw.c +ltunify: ltunify.c hidpp20.c + .PHONY: all clean install-home clean: rm -f ltunify read-dev-usbmon hidraw diff --git a/README.txt b/README.txt index ea9030e..8dc25dc 100644 --- a/README.txt +++ b/README.txt @@ -67,5 +67,9 @@ Usage of the pairing tool is pretty straight-forward. Example session: Connected devices: idx=1 Mouse M525 +TODO +- organize code in multiple files +- simplify code +- HID++ 2.0 debugging (transparent if possible) ~ Peter Wu diff --git a/hidpp20.c b/hidpp20.c new file mode 100644 index 0000000..06e8ad2 --- /dev/null +++ b/hidpp20.c @@ -0,0 +1,156 @@ +#include +#include +#include +#include +#include + +typedef unsigned char u8; + +#define SHORT_MESSAGE 0x10 +#define LONG_MESSAGE 0x11 + +struct hidpp2_message { + u8 report_id; + u8 device_index; + u8 feature_index; +#define HIDPP_SET_FUNC(msg, func) ((msg)->func_swId |= ((func) << 4)) + u8 func_swId; + u8 params[16]; // 3 or 16 params +} __attribute__((__packed__)); + +struct feature { + uint16_t featureId; +#define FEAT_TYPE_MASK 0xc0 +#define FEAT_TYPE_OBSOLETE 0x80 +#define FEAT_TYPE_SWHIDDEN 0x40 + u8 featureType; +}; + +#define SOFTWARE_ID 0x04 /* getRandom() -- guaranteed to be random. */ + +#define FEATURE_INDEX_IROOT 0x00 + +#define FID_IFEATURESET 0x0001 +static +const char * +get_feature_name(uint16_t featureId) { + switch (featureId) { + case 0x0000: return "IRoot"; + case FID_IFEATURESET: return "IFeatureSet"; + case 0x0003: return "IFirmwareInfo"; + case 0x0005: return "GetDeviceNameType"; + case 0x1000: return "batteryLevelStatus"; + case 0x1B00: return "SpecialKeysMSEButtons"; + case 0x1D4B: return "WirelessDeviceStatus"; + default: return "unknown"; + } +} + +/** + * Initialize common values of a HID++ 2.0 message: report_id, device_index and + * software ID. Remaining fields: feature_index, func, params. + */ +#define INIT_HIDPP_SHORT(msg, dev_index) \ + memset((msg), 0, sizeof *(msg)); \ + (msg)->report_id = SHORT_MESSAGE; \ + (msg)->device_index = dev_index; \ + (msg)->func_swId = SOFTWARE_ID + +static +bool +do_hidpp2_request(int fd, struct hidpp2_message *msg) { + struct hidpp_message *hid10_message = (struct hidpp_message *) msg; + + if (!do_write(fd, hid10_message)) { + return false; + } + + // use do_read_skippy in case there are interleaving notifications + // sub id is feature index in HID++ 2.0 + if (!do_read_skippy(fd, hid10_message, 0x11, hid10_message->sub_id)) { + puts("WTF"); + return false; + } + + return true; +} + +// Returns feature index for featureId +static +u8 +get_feature(int fd, u8 device_index, uint16_t featureId) { + struct hidpp2_message msg; + INIT_HIDPP_SHORT(&msg, device_index); + msg.feature_index = FEATURE_INDEX_IROOT; + HIDPP_SET_FUNC(&msg, 0); // GetFeature(featureId) + msg.params[0] = featureId >> 8; + msg.params[1] = featureId & 0xFF; + if (!do_hidpp2_request(fd, &msg)) { + return 0; + } + return msg.params[0]; +} + +// Returns number of features or 0 on error. +static +u8 +get_feature_count(int fd, u8 device_index, u8 ifeatIndex) { + struct hidpp2_message msg; + INIT_HIDPP_SHORT(&msg, device_index); + // XXX: is this variable? Can't it be hard-coded to 0x01? + msg.feature_index = ifeatIndex; + HIDPP_SET_FUNC(&msg, 0); // GetCount() + if (!do_hidpp2_request(fd, &msg)) { + fprintf(stderr, "Failed to request features count\n"); + return 0; + } + return msg.params[0]; +} + +// Get featureId and type for a given featureIndex. +static +bool +get_featureId(int fd, u8 device_index, u8 ifeatIndex, u8 featureIndex, struct feature *feat) { + struct hidpp2_message msg; + INIT_HIDPP_SHORT(&msg, device_index); + // XXX: is this variable? Can't it be hard-coded to 0x01? + msg.feature_index = ifeatIndex; + HIDPP_SET_FUNC(&msg, 1); // GetFeatureId(featureIndex) + msg.params[0] = featureIndex; + if (!do_hidpp2_request(fd, &msg)) { + return false; + } + feat->featureId = (msg.params[0] << 8) | msg.params[1]; + feat->featureType = msg.params[2]; + return true; +} + +void +hidpp20_print_features(int fd, u8 device_index) { + u8 i, count, ifeatIndex; + + ifeatIndex = get_feature(fd, device_index, FID_IFEATURESET); + if (!ifeatIndex) { + fprintf(stderr, "Failed to get feature information\n"); + return; + } + count = get_feature_count(fd, device_index, ifeatIndex); + + printf("Total number of HID++ 2.0 features: %i\n", count); + for (i = 0; i <= count; i++) { + struct feature feat; + if (get_featureId(fd, device_index, ifeatIndex, i, &feat)) { + printf(" %2i: [%04X] %c%c %s\n", i, feat.featureId, + feat.featureType & FEAT_TYPE_OBSOLETE ? 'O' : ' ', + feat.featureType & FEAT_TYPE_SWHIDDEN ? 'H' : ' ', + get_feature_name(feat.featureId)); + if (feat.featureType & ~FEAT_TYPE_MASK) { + printf("Warning: unrecognized feature flags: %#04x\n", + feat.featureType & ~FEAT_TYPE_MASK); + } + } else { + fprintf(stderr, "Failed to get feature, is device connected?\n"); + } + } + puts("(O = obsolete feature; H = SW hidden feature)"); +} diff --git a/ltunify.c b/ltunify.c index 2efe1bd..d39d795 100644 --- a/ltunify.c +++ b/ltunify.c @@ -410,6 +410,14 @@ static bool do_read_skippy(int fd, struct hidpp_message *msg, if (msg->report_id == exp_report_id && msg->sub_id == exp_sub_id) { return true; } + // guess: 0xFF is error message in HID++ 2.0? + if (msg->report_id == LONG_MESSAGE && msg->sub_id == 0xFF) { + if (debug_enabled) { + fprintf(stderr, "HID++ 2.0 error %#04x\n", + msg->msg_long.str[2]); + } + return false; + } if (msg->report_id == SHORT_MESSAGE && msg->sub_id == SUB_ERROR_MSG && msg->msg_error.sub_id == exp_sub_id) { struct msg_error *error = &msg->msg_error; @@ -459,6 +467,9 @@ static bool do_read_skippy(int fd, struct hidpp_message *msg, return true; } +// TODO: separate files +#include "hidpp20.c" + static bool set_register(int fd, u8 device_index, u8 address, u8 *params, struct hidpp_message *res, bool is_long_req) { u8 exp_sub_id; @@ -890,8 +901,12 @@ void gather_device_info(int fd, u8 device_index) { get_hidpp_version(fd, device_index, &dev->hidpp_version); get_device_ext_pair_info(fd, device_index); get_device_name(fd, device_index); - if (get_device_versions(fd, device_index, &dev->version)) { - dev->device_available = true; + if (dev->hidpp_version.major == 0 && dev->hidpp_version.minor == 0) { + if (get_device_versions(fd, device_index, &dev->version)) { + dev->device_available = true; + } + } else { + // TODO: hid++20 support } } else { // retrieve some information from notifier @@ -1240,8 +1255,13 @@ int main(int argc, char **argv) { device_index = find_device_index_for_type(fd, args[1], NULL); if (device_index) { + struct device *dev = &devices[device_index - 1]; gather_device_info(fd, device_index); print_detailed_device(device_index); + if (dev->hidpp_version.major == 2 && dev->hidpp_version.minor == 0) { + // TODO: separate fetch/print + hidpp20_print_features(fd, device_index); + } } else { fprintf(stderr, "Device %s not found\n", args[1]); } -- cgit v1.2.1