diff options
author | Peter Wu <lekensteyn@gmail.com> | 2013-04-08 17:00:42 +0200 |
---|---|---|
committer | Peter Wu <lekensteyn@gmail.com> | 2013-04-08 17:02:24 +0200 |
commit | 99bcaad18aad5c41997b94a66862e19c841c2986 (patch) | |
tree | 94c3d3568dda0082a6d665595687041f038d21b5 | |
download | ltunify-99bcaad18aad5c41997b94a66862e19c841c2986.tar.gz |
Initial commit of RE/debug programs/notes
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | README.txt | 42 | ||||
-rwxr-xr-x | hidraw.c | 237 | ||||
-rw-r--r-- | notes.txt | 207 | ||||
-rw-r--r-- | read-dev-usbmon.c | 150 | ||||
-rw-r--r-- | registers.txt | 284 | ||||
-rwxr-xr-x | usbmon.awk | 132 |
7 files changed, 1055 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2a62954 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.*.sw* +read-dev-usbmon +hidraw diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..56f85cf --- /dev/null +++ b/README.txt @@ -0,0 +1,42 @@ +Logitech documents + +2200_mousepointer.pdf, 4301_k750_solarkeyboard_lightandbattery.pdf, +6100_touchpadraw.pdf, 6110_touchmouseraw.pdf, +logitech_hidpp10_specification_for_Unifying_Receivers.pdf have been created +from .doc(x) files using LibreOffice. +logitech_hidpp_2.0_specification_draft_2012-06-04.pdf was already a PDF, so no +conversion was necessary. + +The contents of the aforementioned files are (C) Logitech. +Retrieved from https://drive.google.com/?tab=mo&pli=1&authuser=0#folders/0BxbRzx7vEV7eWmgwazJ3NUFfQ28 +(found at http://code.google.com/p/chromium/issues/detail?id=175572) + +usbmon.awk was a RE attempt before I found the spec, it helped me understand +the packets when the specification was not available. + +You will find the HID++ 1.0 spec the most interesting, I hope to produce a +pairing program soon. + + +Debuggers +usbmon.awk - initial debugging tool used for tapping usbmon from debugfs +hidraw.c - successor of usbmon.awk that can parse packets of usb payload. +read-dev-usbmon.c - Reads data from /dev/usbmonX and show interpreted data in a + more human-readable way. + +Note: as a quick-n-dirty hack, I included hidraw.c at some point into the +read-dev-usbmon program. Otherwise, I had no way to show the difference between +a send or receive packet without adding to the same stdout stream. If I included +it in the stderr pipe, then it would be interleaved with stdout in an +unpredictable manner. This means that hidraw.c is currently unusable, it does +not process data correctly. + +Usage of USB debugger: +1. Use `lsusb -d 046d:c52b` to determine the bus number. If the output is "Bus + 001 ..", your usb monitor device is at /dev/usbmon1. +2. sudo chgrp $USER /dev/usbmon1 +3. sudo chmod g+r /dev/usbmon1 +4. ./read-dev-usbmon /dev/usbmon1 +5. Profit! + +~ Peter Wu <lekensteyn@gmail.com> diff --git a/hidraw.c b/hidraw.c new file mode 100755 index 0000000..dd35051 --- /dev/null +++ b/hidraw.c @@ -0,0 +1,237 @@ +/* + * Displays a more human-readable interpretation of the USB data payload + * for Logitech Unifying Receiver. + * + * Example usage: read-dev-usbmon /dev/usbmon0 | hidraw + * + * Copyright (C) 2013 Peter Wu <lekensteyn@gmail.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <fcntl.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> + +typedef unsigned char u8; + +#define SHORT_MSG 0x10 +#define SHORT_MSG_LEN 7 +#define LONG_MSG 0x11 +#define LONG_MSG_LEN 20 + +struct payload_short { + u8 address; + u8 value[3]; +}; +struct payload_long { + u8 address; + u8 str[16]; +}; +struct report { + u8 report_id; + u8 device_index; + u8 sub_id; + union { + struct payload_long l; + struct payload_short s; + }; +} __attribute__((__packed__)); + +static const char * report_types[0xFF] = { + // 0x00 - 0x3F HID reports + [0x01] = "KEYBOARD", + [0x02] = "MOUSE", + [0x03] = "CONSUMER_CONTROL", + [0x04] = "SYSTEM_CONTROL", + + [0x08] = "MEDIA_CENTER", + + [0x0E] = "LEDS", + + // 0x40 - 0x7F enumerator notifications + [0x40] = "NOTIF_DEVICE_UNPAIRED", + [0x41] = "NOTIF_DEVICE_PAIRED", + [0x42] = "NOTIF_CONNECTION_STATUS", + [0x4A] = "NOTIF_RECV_LOCK_CHANGED", + [0x4B] = "?NOTIF_PAIR_ACCEPTED", + + [0x7F] = "NOTIF_ERROR", + + // 0x80 - 0xFF enumerator commands; Register Access + [0x80] = "SET_REG", // was CMD_SWITCH + [0x81] = "GET_REG", // was CMD_GET_PAIRED_DEVICES + [0x82] = "SET_LONG_REG", + [0x83] = "GET_LONG_REG", + [0x8F] = "_ERROR_MSG", +}; + +static const char * error_messages[0xFF] = { + // error messages for type=8F (ERROR_MSG) + [0x01] = "SUCCESS", + [0x02] = "INVALID_SUBID", + [0x03] = "INVALID_ADDRESS", + [0x04] = "INVALID_VALUE", + [0x05] = "CONNECT_FAIL", + [0x06] = "TOO_MANY_DEVICES", + [0x07] = "ALREADY_EXISTS", + [0x08] = "BUSY", + [0x09] = "UNKNOWN_DEVICE", + [0x0a] = "RESOURCE_ERROR", + [0x0b] = "REQUEST_UNAVAILABLE", + [0x0c] = "INVALID_PARAM_VALUE", + [0x0d] = "WRONG_PIN_CODE", +}; + +static const char * registers[0xFF] = { + [0x00] = "ENABLED_NOTIFS", + [0x01] = "KBD_HAND_DETECT?", + [0x02] = "CONNECTION_STATE", + [0x03] = "FN_KEY_SWAP?", + [0x17] = "ILLUMINATION_INFO?", + [0xb2] = "DEVICE_PAIRING", + [0xb3] = "DEVICE_ACTIVITY", + [0xb5] = "PAIRING_INFO", + [0xf1] = "VERSION_INFO?", /* guessed */ +}; + +const char * report_type_str(u8 type) { + const char * str = report_types[type]; + return str ? str : ""; +} +const char * device_type_str(u8 type) { + switch (type) { + case 0x01: return "DEV1"; + case 0x02: return "DEV2"; + case 0x03: return "DEV3"; + case 0x04: return "DEV4"; + case 0x05: return "DEV5"; + case 0x06: return "DEV6"; + case 0xFF: return "RECV"; + default: return ""; + } +} +const char *error_str(u8 er) { + const char * str = error_messages[er]; + return str ? str : ""; +} +const char *register_str(u8 reg) { + const char * str = registers[reg]; + return str ? str : ""; +} + +void process_msg_payload(struct report *r, u8 data_len) { + u8 pos, i; + u8 * bytes = (u8 *) &r->s; + + switch (r->sub_id) { + case 0x8F: // error + // TODO: length check + printf("SubID=%02X %s ", bytes[0], report_type_str(bytes[0])); + printf("reg=%02X %s ", bytes[1], register_str(bytes[1])); + printf("err=%02X %s ", bytes[2], error_str(bytes[2])); + pos = 4; // everything is processed + break; + case 0x80: + case 0x81: + case 0x82: /* long */ + case 0x83: /* long */ + printf("reg=%02X %s ", bytes[0], register_str(bytes[0])); + pos = 1; + break; + default: + pos = 0; // nothing has been processed + break; + } + + if (pos < data_len) { + printf("params="); + //printf("params(len=%02X)=", data_len); + } + for (i = 0; pos < data_len; pos++, i++) { + printf("%02X ", bytes[pos]); + if (i % 4 == 3 && pos + 1 < data_len) { + putchar(' '); + } + } +} + +void process_msg(struct report *report, ssize_t size) { + const char * report_type; + + switch (report->report_id) { + case SHORT_MSG: + report_type = "short"; + if (size != SHORT_MSG_LEN) { + fprintf(stderr, "Invalid short msg len %zi\n", size); + return; + } + break; + case LONG_MSG: + report_type = "long"; + if (size != LONG_MSG_LEN) { + fprintf(stderr, "Invalid long msg len %zi\n", size); + return; + } + break; + default: + report_type = "unkn"; + //fprintf(stderr, "Unknown report ID %02x, len=%zi\n", report->report_id, size); + if (size < 3) { + return; + } + break; + } + + printf("report_id=%02X %-5s ", report->report_id, report_type); + printf("device=%02X %-4s ", report->device_index, + device_type_str(report->device_index)); + printf("type=%02X %-23s ", report->sub_id, + report_type_str(report->sub_id)); + + if (size > 3) { + process_msg_payload(report, size - 3); + } + putchar('\n'); +} + +#ifndef NO_MAIN +int main(int argc, char ** argv) { + int fd = STDIN_FILENO; + ssize_t r; + struct report report; + + if (argc >= 2 && (fd = open(argv[1], O_RDONLY)) < 0) { + perror(argv[1]); + return 1; + } + + do { + memset(&report, 0xCC, sizeof report); // for debugging purposes + r = read(fd, &report, sizeof report); + if (r > 0) { + process_msg(&report, r); + } + } while (r >= 0); + + if (r < 0) { + perror("read"); + } + + close(fd); + + return 0; +} +#endif /* ! NO_MAIN */ diff --git a/notes.txt b/notes.txt new file mode 100644 index 0000000..b9e398f --- /dev/null +++ b/notes.txt @@ -0,0 +1,207 @@ +// vim:syntax=c: +// docs at https://drive.google.com/?tab=mo&pli=1&authuser=0#folders/0BxbRzx7vEV7eWmgwazJ3NUFfQ28 (found at http://code.google.com/p/chromium/issues/detail?id=175572) +struct dj_report { + u8 report_id; + u8 device_index; + u8 report_type; + u8 report_params[DJREPORT_SHORT_LENGTH - 3]; +}; + +// char magic_sequence[] = {0x10, 0xFF, 0x80, 0xB2, 0x01, 0x00, 0x00}; + +#define REPORT_TYPE_KEYBOARD 0x01 +#define REPORT_TYPE_MOUSE 0x02 +#define REPORT_TYPE_CONSUMER_CONTROL 0x03 +#define REPORT_TYPE_SYSTEM_CONTROL 0x04 + +#define REPORT_TYPE_MEDIA_CENTER 0x08 + +#define REPORT_TYPE_LEDS 0x0E + +#define REPORT_TYPE_NOTIF_DEVICE_UNPAIRED 0x40 +#define REPORT_TYPE_NOTIF_DEVICE_PAIRED 0x41 +#define REPORT_TYPE_NOTIF_CONNECTION_STATUS 0x42 + +#define REPORT_TYPE_NOTIF_ERROR 0x7F + +#define REPORT_TYPE_CMD_SWITCH 0x80 +#define REPORT_TYPE_CMD_GET_PAIRED_DEVICES 0x81 + + dj_report->report_id = REPORT_ID_DJ_SHORT; + dj_report->device_index = 0xFF; + dj_report->report_type = REPORT_TYPE_CMD_GET_PAIRED_DEVICES; + retval = logi_dj_recv_send_report(djrcv_dev, dj_report); + +report_id = 0x10; +device_index = 0xFF; +report_type = 0x80; // REPORT_TYPE_CMD_SWITCH +report_params = {0xB2, 0x01, 0x00, 0x00}; +// { 0xB2 , Connect Devices, Device Number, Open Lock Timeout } +// = {0xb2, 0x01, 0x50, 0x3c} +// observation: S b203 0100 perform unpair +// S b201 533c when discovery is enabled (with no paired devices) +// R b200 0000 when discovery is enabled (no paired devices, waiting; also recvd when unpaired while in Advanced mode) +// S b202 5394 when discovery is disabled (both with 1 paired kbd and no paired kbd; explicitly issued when closing pair program) +// 0:1 1 address +// 1:3 3 value (0 is returned on succesfully setting register) +// Related to type 0x4A + +// observations + guesses +// issued after 10 ff CMD_SWITCH b2 .. .. .. +report_id = 0x10 +device_index = 0xFF +report_type = 0x4A // receiver status - open for new devices? +// R 0100 0000 discovery enabled +// R 0001 0000 discovery disabled (issued after timeout) +// R 0000 0000 discovery disabled (after CMD_SWITCH b200 0000; succesful pair) + +// 00000000 00000000 is sent when device is turned off, "null report"? + +// No paired devices, just started program: +// output report_id=10 dev_idx=ff RECV type=83 parms=b5030000 +// ep3 report_id=11 dev_idx=ff RECV type=83 parms=b503af4f95 ea150609 00000000 00000000 +// No paired devices (same for unpaired kbd off/on), just started program: +// output report_id=10 dev_idx=ff RECV type=83 ?CMD_DEVICE_INFO parms=b5030000 +// ep3 report_id=11 dev_idx=ff RECV type=83 ?CMD_DEVICE_INFO parms=b503af4f95 ea15060a 00000000 00000000 +// Press "Advanced", unpaired (same for unpaired kbd on/off): +// output report_id=10 dev_idx=ff RECV type=83 ?CMD_DEVICE_INFO parms=b3000000 +// ep3 report_id=11 dev_idx=ff RECV type=83 ?CMD_DEVICE_INFO parms=b308000000 00000000 00000000 00000000 + +/* +report_id=10; dev_id=01 for Sent0+Recv0 and Sent1+Recv1, but ff for Sent1+Recv2 + Sent0 Recv0 + 00120100* +0d000000 810d0200* +07000000 07050000 + + Sent1 Recv1 Recv2 +f1010000 f1012201 f1011201 +f1020000 f1020019 f1020019 +f1030000 f1030007 81f10300* +f1040000 f1040201 f1040214 + +*) type=8f instead of 81 CMD_GET_PAIRED_DEVICES +Order = Sent0+Recv0, Sent1+Recv1, Sent1+Recv2 (+ = interleaved) +*/ + + +// Discover? (click Advanced and get a lot of this spam) +// send: report_id=10 dev_idx=ff type=83 parms=b3000000 # report yourself guys? +// +// recv: report_id=11 dev_idx=ff type=83 parms=b3a1000000 00000000 00000000 00000000 +// ep3 report_id=11 dev_idx=ff type=83 parms=b32a000000 00000000 00000000 00000000 # byte 2 is channel/encrypt key??? +// ep3 report_id=11 dev_idx=ff type=83 parms=b331000000 00000000 00000000 00000000 # No devices turned on +// ep3 report_id=11 dev_idx=ff type=83 parms=b334000000 00000000 00000000 00000000 # Device pair step 1 (after Sent0+Recv0) +// ep3 report_id=11 dev_idx=ff type=83 parms=b336000000 00000000 00000000 00000000 # Device pair step 2 (after Sent1+Recv1) +// ep3 report_id=11 dev_idx=ff type=83 parms=b338000000 00000000 00000000 00000000 # Device just paired (after Sent1+Recv2) + + +// Unpair: +// send: report_id=10 dev_idx=ff type=80 CMD_SWITCH parms=b2030100 # R: Unpair device request +// +// recv: report_id=03 dev_idx=00 type=00 parms=0000 +// recv: report_id=10 dev_idx=01 type=40 NOTIF_DEVICE_UNPAIRED parms=02000000 +// recv: report_id=00 dev_idx=00 type=00 parms=0000000000 +// +// recv: report_id=20 dev_idx=01 type=40 NOTIF_DEVICE_UNPAIRED parms=0000000000 00000000 000000 +// +// recv: report_id=10 dev_idx=ff type=80 CMD_SWITCH parms=b2000000 # K: Ready to accept other receiver? + +// Prepare switch? +// send: report_id=10 dev_idx=ff type=80 CMD_SWITCH parms=b201533c # R: Looking for devices? +// recv: +// recv: report_id=10 dev_idx=ff type=4a parms=01000000 # K: Hi I am a device +// recv: +// recv: report_id=10 dev_idx=ff type=80 CMD_SWITCH parms=b2000000 # K: I want to pair with you +// Switch timeout? (+/- 60 seconds) +// recv: report_id=10 dev_idx=ff type=4a parms=00010000 # K: Nevermind, nobody responded + +// Turn on kbd while pair program is waiting for recv +// recv: report_id=10 dev_idx=01 type=41 NOTIF_DEVICE_PAIRED parms=04611020 # L: I just turned myself on +// +// send: report_id=10 dev_idx=ff type=83 parms=b5400000 # R: Hey, wanna join me? +// recv: report_id=20 dev_idx=01 type=41 NOTIF_DEVICE_PAIRED parms=0010201a40 00000000 000000 +// +// recv: report_id=10 dev_idx=ff type=4a parms=00000000 +// +// recv: report_id=11 dev_idx=ff type=83 parms=b540044b38 30300000 00000000 00000000 +// +// send: report_id=10 dev_idx=ff type=83 parms=b5300000 +// +// recv: report_id=11 dev_idx=ff type=83 parms=b530fb841b 861a4000 00070000 00000000 +// b530 +// fb841b 86 Serial No +// 1a4000 00070000 00000000 ??? +// +// recv: report_id=10 dev_idx=01 type=41 NOTIF_DEVICE_PAIRED parms=04a11020 # I am already paired? (0x6_ -> 0xa_, 0110 -> 1010); Also sent when turning on paired kbd +// type=41 Device Connection +// 04 0000 0100 - Unifying protocol +// a1 1010 0001 - DeviceType=Keyboard; Link is encrypted; Link is established; Packet with payload +// 10 0001 0000 - Wireless PID LSB +// 20 0010 0000 - Wireless PID MSB + +// continued switch. +// send: report_id=10 dev_idx=01 type=00 parms=12283f94 +// : report_id=10 dev_idx=01 type=4b parms=01000000 +// +// : report_id=10 dev_idx=01 type=8f parms=00120100 +// +// send: report_id=10 dev_idx=01 type=81 CMD_GET_PAIRED_DEVICES parms=0d000000 +// +// : report_id=10 dev_idx=01 type=8f parms=810d0200 +// +// send: report_id=10 dev_idx=01 type=81 CMD_GET_PAIRED_DEVICES parms=07000000 +// +// : report_id=10 dev_idx=01 type=81 CMD_GET_PAIRED_DEVICES parms=07050000 +// +// send: report_id=10 dev_idx=01 type=81 CMD_GET_PAIRED_DEVICES parms=f1010000 +// +// : report_id=10 dev_idx=01 type=81 CMD_GET_PAIRED_DEVICES parms=f1012201 +// +// send: report_id=10 dev_idx=01 type=81 CMD_GET_PAIRED_DEVICES parms=f1020000 +// +// : report_id=10 dev_idx=01 type=81 CMD_GET_PAIRED_DEVICES parms=f1020019 +// +// send: report_id=10 dev_idx=01 type=81 CMD_GET_PAIRED_DEVICES parms=f1030000 +// +// : report_id=10 dev_idx=01 type=81 CMD_GET_PAIRED_DEVICES parms=f1030007 +// +// send: report_id=10 dev_idx=01 type=81 CMD_GET_PAIRED_DEVICES parms=f1040000 +// +// : report_id=10 dev_idx=01 type=81 CMD_GET_PAIRED_DEVICES parms=f1040201 +// +// send: report_id=10 dev_idx=ff type=81 CMD_GET_PAIRED_DEVICES parms=f1010000 +// : report_id=10 dev_idx=ff type=81 CMD_GET_PAIRED_DEVICES parms=f1011201 +// +// send: report_id=10 dev_idx=ff type=81 CMD_GET_PAIRED_DEVICES parms=f1020000 +// : report_id=10 dev_idx=ff type=81 CMD_GET_PAIRED_DEVICES parms=f1020019 +// +// send: report_id=10 dev_idx=ff type=81 CMD_GET_PAIRED_DEVICES parms=f1030000 +// : +// : report_id=10 dev_idx=ff type=8f parms=81f10300 +// : +// send: report_id=10 dev_idx=ff type=81 CMD_GET_PAIRED_DEVICES parms=f1040000 +// : report_id=10 dev_idx=ff type=81 CMD_GET_PAIRED_DEVICES parms=f1040214 + + +// http://tequals0.wordpress.com/2011/11/01/reverse-engineering-logitech-unifying-usb-protocol/ +/* +T: Bus=05 Lev=01 Prnt=01 Port=01 Cnt=02 Dev#= 3 Spd=12 MxCh= 0 +D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 +P: Vendor=046d ProdID=c52b Rev=12.01 +S: Manufacturer=Logitech +S: Product=USB Receiver +C:* #Ifs= 3 Cfg#= 1 Atr=a0 MxPwr= 98mA +I:* If#= 0 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=01 Prot=01 Driver=usbhid +E: Ad=81(I) Atr=03(Int.) MxPS= 8 Ivl=8ms +I:* If#= 1 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=01 Prot=02 Driver=usbhid +E: Ad=82(I) Atr=03(Int.) MxPS= 8 Ivl=2ms +I:* If#= 2 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=00 Prot=00 Driver=usbhid +E: Ad=83(I) Atr=03(Int.) MxPS= 32 Ivl=2ms + +b=$'\e[1;32m';e=$'\e[m';sudo cat /sys/kernel/debug/usb/usbmon/5u | sed -ur "s/= (..)(..)(..)(..) (..)(....)(..)/= report_id=$b\1$e dev_idx=$b\2$e type=$b\3$e parms=$b\4\5 \6 \7$e/" + +see lt/usbmon.awk +awk -vOFS=' ' 'function l(s){return "\033[1;32m" s "\033[m"}{if(match($0,/(.*? = )(..)(..)(..)(..) (.*)/,a)){printf("%-85s",$0);print "report_id=" l(a[2]), "dev_idx=" l(a[3]), "type=" l(a[4]), "parms=" l(a[5] a[6])}else print "\033[1;30m" $0 "\033[m"}' + +*/ diff --git a/read-dev-usbmon.c b/read-dev-usbmon.c new file mode 100644 index 0000000..916bf37 --- /dev/null +++ b/read-dev-usbmon.c @@ -0,0 +1,150 @@ +/* + * Tool for reading usbmon messages and writing non-empty data to stdout. + * Because of limitations of a single output stream, there is currently a hack + * that directly includes hidraw.c. + * + * Copyright (C) 2013 Peter Wu <lekensteyn@gmail.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <sys/ioctl.h> +#include <string.h> +#include <stdint.h> +#include <stdlib.h> /* getenv */ + +typedef uint16_t u16; +typedef int32_t s32; +typedef uint64_t u64; +typedef int64_t s64; +#define SETUP_LEN 8 + +/* taken from Linux, Documentation/usb/usbmon.txt */ +struct usbmon_packet { + u64 id; /* 0: URB ID - from submission to callback */ + unsigned char type; /* 8: Same as text; extensible. */ + unsigned char xfer_type; /* ISO (0), Intr, Control, Bulk (3) */ + unsigned char epnum; /* Endpoint number and transfer direction */ + unsigned char devnum; /* Device address */ + u16 busnum; /* 12: Bus number */ + char flag_setup; /* 14: Same as text */ + char flag_data; /* 15: Same as text; Binary zero is OK. */ + s64 ts_sec; /* 16: gettimeofday */ + s32 ts_usec; /* 24: gettimeofday */ + int status; /* 28: */ + unsigned int length; /* 32: Length of data (submitted or actual) */ + unsigned int len_cap; /* 36: Delivered length */ + union { /* 40: */ + unsigned char setup[SETUP_LEN]; /* Only for Control S-type */ + struct iso_rec { /* Only for ISO */ + int error_count; + int numdesc; + } iso; + } s; + int interval; /* 48: Only for Interrupt and ISO */ + int start_frame; /* 52: For ISO */ + unsigned int xfer_flags; /* 56: copy of URB's transfer_flags */ + unsigned int ndesc; /* 60: Actual number of ISO descriptors */ +}; + +struct mon_get_arg { + struct usbmon_packet *hdr; + void *data; + size_t alloc; /* Length of data (can be zero) */ +}; + +#define MON_IOC_MAGIC 0x92 +#define MON_IOCQ_URB_LEN _IO(MON_IOC_MAGIC, 1) +#define MON_IOCX_GET _IOW(MON_IOC_MAGIC, 6, struct mon_get_arg) + +#define NO_MAIN +// HACK - otherwise there is no easy wat to tell whether a packet is read or +// written from the usbmon +#include "hidraw.c" +#undef NO_MAIN + +int main(int argc, char ** argv) { + unsigned char data[1024]; + struct usbmon_packet hdr; + struct mon_get_arg event; + int fd, r; + + if (argc < 2) { + fprintf(stderr, "Usage: %s /dev/usbmonX\n", argv[0]); + return 1; + } + + fd = open(argv[1], O_RDONLY); + if (fd < 0) { + perror(argv[1]); + return 1; + } + + memset(&hdr, 0, sizeof hdr); + event.hdr = &hdr; // hopefully it is OK to use stack for this + event.data = &data; + event.alloc = sizeof data; + + //r = ioctl(fd, MON_IOCQ_URB_LEN); + //printf("%i\n", r); + for (;;) { + memset(&data, 0xCC, sizeof data); // for debugging purposes + r = ioctl(fd, MON_IOCX_GET, &event); + if (r < 0) { + perror("ioctl"); + break; + } + + // ignore non-data packets + if (hdr.len_cap) { + if (getenv("HEX")) { + unsigned int i; + printf("Type=%c\n", hdr.type); + for (i=0; i<hdr.len_cap; i++) { + printf("%02X%c", data[i], + i + 1 == hdr.len_cap ? '\n' : ' '); + } + } else { + struct report *report = (struct report *)&data; + if (hdr.len_cap < 3) { + fprintf(stderr, "Short data len: %i\n", hdr.len_cap); + continue; + } +#define COLOR(c, cstr) "\033[" c "m" cstr "\033[m" + if (hdr.type == 'C') { + printf(COLOR("1;32", "Recv\t")); + } else if (hdr.type == 'S') { + printf(COLOR("1;31", "Send\t")); + } else { + printf(COLOR("1;35", "Type=%c\t") "\n", hdr.type); + } + process_msg(report, hdr.len_cap); + fflush(NULL); +#if 0 + if (write(STDOUT_FILENO, &data, hdr.len_cap) < 0) { + perror("write"); + break; + } +#endif + } + } + } + + close(fd); + + return 0; +} diff --git a/registers.txt b/registers.txt new file mode 100644 index 0000000..6907eef --- /dev/null +++ b/registers.txt @@ -0,0 +1,284 @@ +Overview + +USB VID 0x046d +USB PID 0xc52b + +Message Sub ID +0x80 SET_REGISTER +0x81 GET_REGISTER +0x82 SET_LONG_REGISTER +0x83 GET_LONG_REGISTER +0x8F ERROR_MSG + +Notifications +0x40 Device Disconnection +0x41 Device Connection +0x4A Unifying Receiver Locking Change information + +Registers +0x00 Enable HID++ Notifications +0x02 Connection State +0xB2 Device Connection and Disconnection (Pairing) +0xB3 Device Activity +0xB5 Pairing Information +(more undocumented below) + +Not documented: +Long register B5 (Pairing Information) - nn=03 "Receiver information?" +Send: 10 FF 83 B5 03 00 00 00 - Long (10) message for receiver (FF) to retrieve + long register (83) PairingInfo (B5) with special param 03 00 00 00. +Recv: 11 FF 83 B5 03 AF 4F 95 EA 05 06 0E 00 00 00 00 00 00 00 00 +11 - Long message +FF - Receiver target +83 - LONG_REGISTER_RESPONSE +B5 - Pairing Info +03 - "Receiver information"? +AF 4F 95 EA - Serial Number of receiver +05 +06 - Max Device Capability? (not sure, but it is six) +0E 00 00 00 00 00 00 00 00 +Remaining information: +- Wireless Status (0x03) +- ModelId (0x46d c52b) +- Handle: 0xff000001 (0xff is device ID, 01 is internal to the software, ordered) +- Dfu Status (0x1) +- Is Dfu Cancellable (yes) + +Short register F1 - "Version information" (guessed) +Header: 10 FF 81 (short msg, receiver is target, GET_REGISTER) +Data: F1 nn xx yy (xx yy is empty for request and contains version for response) +nn=01 xx,yy=12 01 +nn=02 xx,yy=00 19 +nn=03 error 03 (Invalid address) (but returns 00 07 for keyboard) +nn=04 xx,yy=02 14 +Displayed firmware version: 012.001.00019 (x1 . y1 . x2 y2) +Displayed bootloader version: BL.002.014 (BL . x4 . y4) + +10 ix 17 3C 00 00 0n +received when the keyboard illumination level is changed. (n = 1..5) +10 ix 07 07 00 00 00 +received when battery level is requested using keyboard Fn+F7 + +More undocumented short regs: +17 rw Illumination info +get 00 00 00 - Retrieve illimunation status? +rsp: 3C 00 02 - (illimunation is disabled) +set 3C 00 01 - Activate illumination only when I start typing +set 3C 00 02 - Disable keyboard illumination + +01 rw "Keyboard hand detection?" +get 00 00 00 - retrieve keyboard hand detection status? +rsp: 00 00 20 - (hand detection is disabled) +set 00 00 00 - Enable hand detection +set 00 00 20 - Disable hand detection + +09 rw "F key functions" +get 00 00 00 - Retrieve F key function state +rsp: 00 00 00 - (F key functions are not swapped) +set 00 01 00 - Swap F key functions +set 00 00 00 - Do not swap F key functions + +00 rw ENABLED_NOTIFS, 10 02 00, 10 is Battery info, buy what is 02? + +07 r Likely the battery status of the kbd, not observed for M525 mouse +get 00 00 00 +rsp: 07 00 00 - (Battery full?) + +17 rw ??? + +Send report_id=10 short device=02 DEV2 type=81 GET_REG reg=17 params=00 00 00 + report_id=10 short device=02 DEV2 type=81 GET_REG reg=17 params=3C 00 01 +Send report_id=10 short device=02 DEV2 type=80 SET_REG reg=17 params=3C 00 01 + report_id=10 short device=02 DEV2 type=80 SET_REG reg=17 params=00 00 00 +Send report_id=10 short device=02 DEV2 type=81 GET_REG reg=01 params=00 00 00 + report_id=10 short device=02 DEV2 type=81 GET_REG reg=01 params=00 00 00 +Send report_id=10 short device=02 DEV2 type=80 SET_REG reg=01 params=00 00 00 + report_id=10 short device=02 DEV2 type=80 SET_REG reg=01 params=00 00 00 +Send report_id=10 short device=02 DEV2 type=81 GET_REG reg=09 params=00 00 00 + report_id=10 short device=02 DEV2 type=81 GET_REG reg=09 params=00 00 00 +Send report_id=10 short device=02 DEV2 type=80 SET_REG reg=09 params=00 00 00 + report_id=10 short device=02 DEV2 type=80 SET_REG reg=09 params=00 00 00 +Send report_id=10 short device=02 DEV2 type=81 GET_REG reg=07 params=00 00 00 + report_id=10 short device=02 DEV2 type=81 GET_REG reg=07 params=07 00 00 +Send report_id=10 short device=02 DEV2 type=81 GET_REG reg=00 ENABLED_NOTIFS params=00 00 00 + report_id=10 short device=02 DEV2 type=81 GET_REG reg=00 ENABLED_NOTIFS params=10 02 00 +Send report_id=10 short device=02 DEV2 type=80 SET_REG reg=00 ENABLED_NOTIFS params=10 02 00 + report_id=10 short device=02 DEV2 type=80 SET_REG reg=00 ENABLED_NOTIFS params=00 00 00 +Send report_id=10 short device=02 DEV2 type=81 GET_REG reg=17 params=00 00 00 + report_id=10 short device=02 DEV2 type=81 GET_REG reg=17 params=3C 00 01 +Send report_id=10 short device=02 DEV2 type=80 SET_REG reg=17 params=3C 00 01 + report_id=10 short device=02 DEV2 type=80 SET_REG reg=17 params=00 00 00 +Send report_id=10 short device=02 DEV2 type=81 GET_REG reg=01 params=00 00 00 + report_id=10 short device=02 DEV2 type=81 GET_REG reg=01 params=00 00 00 +Send report_id=10 short device=02 DEV2 type=80 SET_REG reg=01 params=00 00 00 + report_id=10 short device=02 DEV2 type=80 SET_REG reg=01 params=00 00 00 +Send report_id=10 short device=02 DEV2 type=81 GET_REG reg=09 params=00 00 00 + report_id=10 short device=02 DEV2 type=81 GET_REG reg=09 params=00 00 00 +Send report_id=10 short device=02 DEV2 type=80 SET_REG reg=09 params=00 00 00 + report_id=10 short device=02 DEV2 type=80 SET_REG reg=09 params=00 00 00 +Send report_id=10 short device=02 DEV2 type=81 GET_REG reg=07 params=00 00 00 + report_id=10 short device=02 DEV2 type=81 GET_REG reg=07 params=07 00 00 + +Documented in hidpp10: +Allows for testing protocol version. +Request: 10 DeviceIndex 00 1n 00 00 uu +- n is SwId +- UU is "ping data" defined by SW +Responses: +HID++ 1.0: 10 ix 8F 00 Fn 01 00 (ERR_INVALID_SUBID) +HID++ 2.0: 10 ix 00 1n 02 00 UU +HID++ X.Y: 10 ix 00 1n XX YY UU + +Discovery: +1. Get Reg 00 +2. Set Reg 00 to value from (1) with bit 0 of the second byte enabled (v1[1] |= 1) +3. Send read CONNECTION_STATE register for total number of devices +4. Write CONNECTION_STATE 02 00 00 register +5. (4) triggers a 0x41 notification (Device Paired notification) for each + device. Note, device index does not have to start at 1 (if device got + unpaired before). +5. Got response for (3). +6. (disable discovery) Get Reg 00 +7. Set Reg 00 to value from (6) with bit 0 of the second byte disabled (v1[1] &= ~1) + +Startup (all targeted at receiver, notifications come from device 1..6): + 1. Get receiver details from pairing info register (serial number, etc) + 2. Get firmware version (recv) + 3. Get bootloader version (recv) + 4. Get enabled notifications + 5. Enable wireless notifications (set enabled notifs reg) + 6. Read connection state register for number of machines + for each machine from (6): + 7. Write params to connection state register (02 00 00 was written) (read + returns after (8)) (possibly a "trigger report all paired devices") + 8. (7) immediately triggers a Device Paired notification for a previously paired dev + 9. Send/Read request for paired device device name + 10. Send/Read request for paired device extended info (serial, .., location of power switch) +11. Read fw+bootloader version information again (this seems useless?) + +A1. Pairing starts: write DEVICE_PAIRING register, enable pairing with timeout (read returns after (A2)) +A2. (A1) triggers Receiver Lock Changed notification (subid=0x41) (reason: no error) +A3. On timeout, Receiver Lock Changed notification is received (reason: timeout) +A4. (close button) On close, DEVICE_PAIRING register is written to disable pairing discovery +A5. Receiver Lock Changed notification is immediately received (reason: timeout) + +Advanced: +B1. Advanced button is pressed. Polling starts, every x time, DEVICE_ACTIVITY register request is sent/read +B2. Unpair device, Device Unpaired notification (subid=0x40) received (reason: + device disconnected) (note, report id 10 and 20 received, ignore 20) + +Send report_id=10 short device=FF RECV type=83 GET_LONG_REG reg=B5 PAIRING_INFO params=03 00 00 + report_id=11 long device=FF RECV type=83 GET_LONG_REG reg=B5 PAIRING_INFO params=03 AF 4F 95 EA 05 06 0E 00 00 00 00 00 00 00 00 +Send report_id=10 short device=FF RECV type=81 GET_REG reg=F1 VERSION_INFO? params=01 00 00 + report_id=10 short device=FF RECV type=81 GET_REG reg=F1 VERSION_INFO? params=01 12 01 +Send report_id=10 short device=FF RECV type=81 GET_REG reg=F1 VERSION_INFO? params=02 00 00 + report_id=10 short device=FF RECV type=81 GET_REG reg=F1 VERSION_INFO? params=02 00 19 +Send report_id=10 short device=FF RECV type=81 GET_REG reg=F1 VERSION_INFO? params=03 00 00 + report_id=10 short device=FF RECV type=8F _ERROR_MSG SubID=81 GET_REG reg=F1 VERSION_INFO? err=03 INVALID_ADDRESS +Send report_id=10 short device=FF RECV type=81 GET_REG reg=F1 VERSION_INFO? params=04 00 00 + report_id=10 short device=FF RECV type=81 GET_REG reg=F1 VERSION_INFO? params=04 02 14 +Send report_id=10 short device=FF RECV type=81 GET_REG reg=00 ENABLED_NOTIFS params=00 00 00 + report_id=10 short device=FF RECV type=81 GET_REG reg=00 ENABLED_NOTIFS params=00 01 00 +Send report_id=10 short device=FF RECV type=80 SET_REG reg=00 ENABLED_NOTIFS params=00 01 00 + report_id=10 short device=FF RECV type=80 SET_REG reg=00 ENABLED_NOTIFS params=00 00 00 +Send report_id=10 short device=FF RECV type=81 GET_REG reg=02 CONNECTION_STATE params=00 00 00 + report_id=10 short device=FF RECV type=81 GET_REG reg=02 CONNECTION_STATE params=00 01 00 +Send report_id=10 short device=FF RECV type=80 SET_REG reg=02 CONNECTION_STATE params=02 00 00 + report_id=10 short device=01 DEV1 type=41 NOTIF_DEVICE_PAIRED params=04 61 10 20 + report_id=10 short device=FF RECV type=80 SET_REG reg=02 CONNECTION_STATE params=00 00 00 +Send report_id=10 short device=FF RECV type=83 GET_LONG_REG reg=B5 PAIRING_INFO params=40 00 00 + report_id=11 long device=FF RECV type=83 GET_LONG_REG reg=B5 PAIRING_INFO params=40 04 4B 38 30 30 00 00 00 00 00 00 00 00 00 00 +Send report_id=10 short device=FF RECV type=83 GET_LONG_REG reg=B5 PAIRING_INFO params=30 00 00 + report_id=11 long device=FF RECV type=83 GET_LONG_REG reg=B5 PAIRING_INFO params=30 FB 84 1B 86 1A 40 00 00 07 00 00 00 00 00 00 +Send report_id=10 short device=FF RECV type=81 GET_REG reg=F1 VERSION_INFO? params=01 00 00 + report_id=10 short device=FF RECV type=81 GET_REG reg=F1 VERSION_INFO? params=01 12 01 +Send report_id=10 short device=FF RECV type=81 GET_REG reg=F1 VERSION_INFO? params=02 00 00 + report_id=10 short device=FF RECV type=81 GET_REG reg=F1 VERSION_INFO? params=02 00 19 +Send report_id=10 short device=FF RECV type=81 GET_REG reg=F1 VERSION_INFO? params=03 00 00 + report_id=10 short device=FF RECV type=8F _ERROR_MSG SubID=81 GET_REG reg=F1 VERSION_INFO? err=03 INVALID_ADDRESS +Send report_id=10 short device=FF RECV type=81 GET_REG reg=F1 VERSION_INFO? params=04 00 00 + report_id=10 short device=FF RECV type=81 GET_REG reg=F1 VERSION_INFO? params=04 02 14 + +No paired devices, start pairing: + 1. Enable device pairing (response read returns after (2)) + 2. Got receiver lock notification (locking open) + + 3. Enable device, got Device Paired notif (kbd, link encrypted, link not established) + 4. Send read request for device name (response in (6)) + (5. got report_id=20 for device paired notif) + 5. Got Receiver Lock changed notification (lock closed) + 6. Response for device name (sent in (4)), Send/Read request for paired device + extended info (serial, .., location of power switch) + 7. Got Device Paired notif (kbd, link encrypted, link established, link with payload) + 8. (??) Send dev1 [header 10 01 00] 12 28 3F 94 + 9. Got notification ("Pair accepted"?) [header 10 01 4B] 01 00 00 00 +10. Dev1 request (8) returned error message (err=01 SUCCESS) +11. Read dev1 register 0D, but it returns an error (err=02 INVALID_SUBID) +12. Request/read dev1 register 07 +13. Read dev1 firmware, bootloader version +14. Read recv firmware, bootloader version +15. Write DEVICE_PAIRING register (close lock) +16. Got Receiver Lock changed notification (lock closed) (was already closed in (5) though) + +Send report_id=10 short device=FF RECV type=80 SET_REG reg=B2 DEVICE_PAIRING params=01 53 3C + report_id=10 short device=FF RECV type=4A NOTIF_RECV_LOCK_CHANGED params=01 00 00 00 + report_id=10 short device=FF RECV type=80 SET_REG reg=B2 DEVICE_PAIRING params=00 00 00 + + report_id=10 short device=01 DEV1 type=41 NOTIF_DEVICE_PAIRED params=04 61 10 20 +Send report_id=10 short device=FF RECV type=83 GET_LONG_REG reg=B5 PAIRING_INFO params=40 00 00 + report_id=20 unkn device=01 DEV1 type=41 NOTIF_DEVICE_PAIRED params=00 10 20 1A 40 00 00 00 00 00 00 00 + report_id=10 short device=FF RECV type=4A NOTIF_RECV_LOCK_CHANGED params=00 00 00 00 + report_id=11 long device=FF RECV type=83 GET_LONG_REG reg=B5 PAIRING_INFO params=40 04 4B 38 30 30 00 00 00 00 00 00 00 00 00 00 +Send report_id=10 short device=FF RECV type=83 GET_LONG_REG reg=B5 PAIRING_INFO params=30 00 00 + report_id=11 long device=FF RECV type=83 GET_LONG_REG reg=B5 PAIRING_INFO params=30 FB 84 1B 86 1A 40 00 00 07 00 00 00 00 00 00 + report_id=10 short device=01 DEV1 type=41 NOTIF_DEVICE_PAIRED params=04 A1 10 20 +Send report_id=10 short device=01 DEV1 type=00 params=12 28 3F 94 + report_id=10 short device=01 DEV1 type=4B ?NOTIF_PAIR_ACCEPTED params=01 00 00 00 + report_id=10 short device=01 DEV1 type=8F _ERROR_MSG SubID=00 reg=12 err=01 SUCCESS +Send report_id=10 short device=01 DEV1 type=81 GET_REG reg=0D params=00 00 00 + report_id=10 short device=01 DEV1 type=8F _ERROR_MSG SubID=81 GET_REG reg=0D err=02 INVALID_SUBID +Send report_id=10 short device=01 DEV1 type=81 GET_REG reg=07 params=00 00 00 + report_id=10 short device=01 DEV1 type=81 GET_REG reg=07 params=07 00 00 +Send report_id=10 short device=01 DEV1 type=81 GET_REG reg=F1 VERSION_INFO? params=01 00 00 + report_id=10 short device=01 DEV1 type=81 GET_REG reg=F1 VERSION_INFO? params=01 22 01 +Send report_id=10 short device=01 DEV1 type=81 GET_REG reg=F1 VERSION_INFO? params=02 00 00 + report_id=10 short device=01 DEV1 type=81 GET_REG reg=F1 VERSION_INFO? params=02 00 19 +Send report_id=10 short device=01 DEV1 type=81 GET_REG reg=F1 VERSION_INFO? params=03 00 00 + report_id=10 short device=01 DEV1 type=81 GET_REG reg=F1 VERSION_INFO? params=03 00 07 +Send report_id=10 short device=01 DEV1 type=81 GET_REG reg=F1 VERSION_INFO? params=04 00 00 + report_id=10 short device=01 DEV1 type=81 GET_REG reg=F1 VERSION_INFO? params=04 02 01 +Send report_id=10 short device=FF RECV type=81 GET_REG reg=F1 VERSION_INFO? params=01 00 00 + report_id=10 short device=FF RECV type=81 GET_REG reg=F1 VERSION_INFO? params=01 12 01 +Send report_id=10 short device=FF RECV type=81 GET_REG reg=F1 VERSION_INFO? params=02 00 00 + report_id=10 short device=FF RECV type=81 GET_REG reg=F1 VERSION_INFO? params=02 00 19 +Send report_id=10 short device=FF RECV type=81 GET_REG reg=F1 VERSION_INFO? params=03 00 00 + report_id=10 short device=FF RECV type=8F _ERROR_MSG SubID=81 GET_REG reg=F1 VERSION_INFO? err=03 INVALID_ADDRESS +Send report_id=10 short device=FF RECV type=81 GET_REG reg=F1 VERSION_INFO? params=04 00 00 + report_id=10 short device=FF RECV type=81 GET_REG reg=F1 VERSION_INFO? params=04 02 14 + +Send report_id=10 short device=FF RECV type=80 SET_REG reg=B2 DEVICE_PAIRING params=02 53 94 + report_id=10 short device=FF RECV type=80 SET_REG reg=B2 DEVICE_PAIRING params=00 00 00 + report_id=10 short device=FF RECV type=4A NOTIF_RECV_LOCK_CHANGED params=00 00 00 00 + +Somewhere in the below stream is the battery condition of the M525 mouse (good). +Recv report_id=10 short device=01 DEV1 type=41 NOTIF_DEVICE_PAIRED params=04 52 13 40 +Recv report_id=10 short device=FF RECV type=80 SET_REG reg=02 CONNECTION_STATE params=00 00 00 +Send report_id=10 short device=FF RECV type=83 GET_LONG_REG reg=B5 PAIRING_INFO params=30 00 00 +Recv report_id=11 long device=FF RECV type=83 GET_LONG_REG reg=B5 PAIRING_INFO params=30 DA FA 33 5E 04 00 00 00 01 00 00 00 00 00 00 +Recv report_id=10 short device=01 DEV1 type=41 NOTIF_DEVICE_PAIRED params=04 92 13 40 +Recv report_id=20 unkn device=01 DEV1 type=42 NOTIF_CONNECTION_STATUS params=00 00 00 00 00 00 00 00 00 00 00 00 +Send report_id=10 short device=FF RECV type=83 GET_LONG_REG reg=B5 PAIRING_INFO params=30 00 00 +Recv report_id=11 long device=01 DEV1 type=05 params=00 01 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 +Recv report_id=11 long device=FF RECV type=83 GET_LONG_REG reg=B5 PAIRING_INFO params=30 DA FA 33 5E 04 00 00 00 01 00 00 00 00 00 00 +Send report_id=10 short device=01 DEV1 type=00 params=05 00 03 00 +Recv report_id=11 long device=01 DEV1 type=00 params=05 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Send report_id=10 short device=01 DEV1 type=02 MOUSE params=05 00 00 00 +Recv report_id=11 long device=01 DEV1 type=02 MOUSE params=05 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Send report_id=10 short device=01 DEV1 type=02 MOUSE params=15 00 00 00 +Recv report_id=11 long device=01 DEV1 type=02 MOUSE params=15 00 52 51 4D 27 02 00 28 00 40 13 00 00 00 00 00 +Send report_id=10 short device=01 DEV1 type=04 SYSTEM_CONTROL params=05 00 00 00 +Recv report_id=11 long device=01 DEV1 type=04 SYSTEM_CONTROL params=05 5A 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Send report_id=10 short device=01 DEV1 type=0B params=15 01 00 00 +Recv report_id=11 long device=01 DEV1 type=0B params=15 01 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Recv report_id=20 unkn device=01 DEV1 type=02 MOUSE params=00 00 00 00 00 FF 00 00 00 00 00 00 +Recv report_id=20 unkn device=01 DEV1 type=02 MOUSE params=00 00 00 00 00 FF 00 00 00 00 00 00 diff --git a/usbmon.awk b/usbmon.awk new file mode 100755 index 0000000..712fb57 --- /dev/null +++ b/usbmon.awk @@ -0,0 +1,132 @@ +#!/usr/bin/gawk -f +# Formats the output of usbmon, assuming Logitech Unifying Receiver protocol +# Usage: sudo cat /sys/kernel/debug/usb/usbmon/5u | usbmon.awk +# (on older gawk versions, use gawk --re-interval -f usbmon.awk) +# +# Author: Peter Wu <lekensteyn@gmail.com> +# Date: 2013-04-04 + +BEGIN { + OFS=" "; +# Taken from Linux source, drivers/hid/hid-logitech-dj.h +# Catgegories are taken from patent description of US8386651 +# 0x00 - 0x3F HID reports +types["01"] = "KEYBOARD"; +types["02"] = "MOUSE"; +types["03"] = "CONSUMER_CONTROL"; +types["04"] = "SYSTEM_CONTROL"; + +types["08"] = "MEDIA_CENTER"; + +types["0E"] = "LEDS"; + +# 0x40 - 0x7F enumerator notifications +types["40"] = "NOTIF_DEVICE_UNPAIRED"; +types["41"] = "NOTIF_DEVICE_PAIRED"; +types["42"] = "NOTIF_CONNECTION_STATUS"; +types["4A"] = "NOTIF_RECV_LOCK_CHANGED"; # 0100 0000 = ready for connections, 0010 0000 = new connections disabled +types["4B"] = "?NOTIF_PAIR_ACCEPTED"; # 0100 0000 + +types["7F"] = "NOTIF_ERROR"; + +# 0x80 - 0xFF enumerator commands; Register Access +types["80"] = "SET_REG"; # CMD_SWITCH +types["81"] = "GET_REG"; # CMD_GET_PAIRED_DEVICES +types["82"] = "SET_LONG_REG"; +types["83"] = "GET_LONG_REG"; +types["8F"] = "_ERROR_MSG"; +# Align type name +maxlen=0; +for (i in types) { + if (maxlen < length(types[i])) { + maxlen = length(types[i]); + } +} + +# error messages for type=8F (ERROR_MSG) +errmsgs["01"] = "SUCCESS"; +errmsgs["02"] = "INVALID_SUBID"; +errmsgs["03"] = "INVALID_ADDRESS"; +errmsgs["04"] = "INVALID_VALUE"; +errmsgs["05"] = "CONNECT_FAIL"; +errmsgs["06"] = "TOO_MANY_DEVICES"; +errmsgs["07"] = "ALREADY_EXISTS"; +errmsgs["08"] = "BUSY"; +errmsgs["09"] = "UNKNOWN_DEVICE"; +errmsgs["0a"] = "RESOURCE_ERROR"; +errmsgs["0b"] = "REQUEST_UNAVAILABLE"; +errmsgs["0c"] = "INVALID_PARAM_VALUE"; +errmsgs["0d"] = "WRONG_PIN_CODE"; + +regs["00"] = "ENABLED_NOTIFS"; +regs["02"] = "CONNECTION_STATE"; +regs["b2"] = "DEVICE_PAIRING"; +regs["b3"] = "DEVICE_ACTIVITY"; +regs["b5"] = "PAIRING_INFO"; +} # end of BEGIN +function colorize(col, s) { + return "\033[" col "m" s "\033[m"; +} +# global color +function c(s) { + return colorize(color, s); +} +function endPoint(ep) { + if (ep == "0") return "output"; + if (ep == "1") return " input"; + if (ep == "2") return colorize("1;33", "enumIf"); + # if (ep == "3") return " ???"; # seen in the output of usbmon + return sprintf("%6s", "ep" ep); +} +function dev(hex) { + if (hex == "ff") { + return "RECV"; + } + if (int(hex) >= 1 && int(hex) <= 6) { + return "DEV" int(hex) + } + return " "; +} +function typeStr(hex) { + return sprintf("%-" maxlen "s", types[toupper(hex)]); +} +function payload(type, p) { + v1 = substr(p, 1, 2); + if (type == "8f") { # error + er=substr(p, 5, 2); + reg=substr(p, 3, 2); + parms = "SubID=" v1 + parms = parms ", Reg=" c(reg) " " regs[reg]; + parms = parms ", er=" c(er); + parms = parms " " errmsgs[er]; + } else if (type == "80" || type == "81" || type == "82" || type == "83") { + parms = "reg=" c(v1) " " regs[v1]; + parms = parms " parms=" c(substr(p, 3)); + } else { + parms = "parms=" c(p); + } + return parms; +} + +{ + if (match($0, /.*?:[0-9]+:[0-9]{3,}:([0-9]+) .*? = (..)(..)(..)(..) (.*)/, a)) { + # length 85 is ok for most, but not when starting logitech program + if (length($0) > 100) { + print $0; + $0 = ""; + } + printf("%-100s", $0); + color = "1;32"; + # sending data instead of receiving data + if ($0 ~ " s ") color = "1;31"; + + print " " endPoint(a[1]), + "report_id=" c(a[2]), + "dev_idx=" c(a[3]) " " dev(a[3]), + "type=" c(a[4]) " " typeStr(a[4]), + payload(a[4], a[5] a[6]); + } else { + print colorize("1;30", $0); + } + fflush(); +} |