summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--README.txt42
-rwxr-xr-xhidraw.c237
-rw-r--r--notes.txt207
-rw-r--r--read-dev-usbmon.c150
-rw-r--r--registers.txt284
-rwxr-xr-xusbmon.awk132
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();
+}