diff --git a/build/skiller-sgk3.pdf b/build/skiller-sgk3.pdf new file mode 100644 index 0000000..d811903 Binary files /dev/null and b/build/skiller-sgk3.pdf differ diff --git a/doc/color-keyboard.tex b/doc/color-keyboard.tex new file mode 100644 index 0000000..61bfe9a --- /dev/null +++ b/doc/color-keyboard.tex @@ -0,0 +1,125 @@ +\def\row{0} +\node[key] at (0,\row) {00\\00}; +\node[key] at (2,\row) {00\\03}; +\node[key] at (3,\row) {00\\06}; +\node[key] at (4,\row) {00\\09}; +\node[key] at (5,\row) {00\\0C}; +\node[key] at (6.5,\row) {00\\0F}; +\node[key] at (7.5,\row) {00\\12}; +\node[key] at (8.5,\row) {00\\15}; +\node[key] at (9.5,\row) {00\\18}; +\node[key] at (11,\row) {00\\1B}; +\node[key] at (12,\row) {00\\1E}; +\node[key] at (13,\row) {00\\21}; +\node[key] at (14,\row) {00\\24}; +\node[key] at (15.25,\row) {00\\2a}; +\node[key] at (16.25,\row) {00\\2d}; +\node[key] at (17.25,\row) {00\\30}; + +\def\row{1.5} +\node[key] at (0,\row) {00\\3f}; +\node[key] at (1,\row) {00\\42}; +\node[key] at (2,\row) {00\\45}; +\node[key] at (3,\row) {00\\48}; +\node[key] at (4,\row) {00\\4b}; +\node[key] at (5,\row) {00\\4e}; +\node[key] at (6,\row) {00\\51}; +\node[key] at (7,\row) {00\\54}; +\node[key] at (8,\row) {00\\57}; +\node[key] at (9,\row) {00\\5A}; +\node[key] at (10,\row) {00\\5D}; +\node[key] at (11,\row) {00\\60}; +\node[key] at (12,\row) {00\\63}; +\node[key, text width=14.5mm] at (13,\row) {00\\66}; + +\node[key] at (15.25,\row) {F12}; +\node[key] at (16.25,\row) {F12}; +\node[key] at (17.25,\row) {F12}; + +\node[key] at (18.5,\row) {F12}; +\node[key] at (19.5,\row) {F12}; +\node[key] at (20.5,\row) {F12}; +\node[key] at (21.5,\row) {F12}; + +\def\row{2.5} +\node[key, text width=10.75mm] at (0,\row) {0000}; +\node[key] at (1.5,\row) {0000}; +\node[key] at (2.5,\row) {0003}; +\node[key] at (3.5,\row) {0006}; +\node[key] at (4.5,\row) {0009}; +\node[key] at (5.5,\row) {000C}; +\node[key] at (6.5,\row) {000F}; +\node[key] at (7.5,\row) {F6}; +\node[key] at (8.5,\row) {F7}; +\node[key] at (9.5,\row) {F8}; +\node[key] at (10.5,\row) {F9}; +\node[key] at (11.5,\row) {F10}; +\node[key] at (12.5,\row) {F11}; +\node[key, text width=10.75mm] at (13.5,\row) {F12}; + +\node[key] at (15.25,\row) {F12}; +\node[key] at (16.25,\row) {F12}; +\node[key] at (17.25,\row) {F12}; + +\node[key] at (18.5,\row) {F12}; +\node[key] at (19.5,\row) {F12}; +\node[key] at (20.5,\row) {F12}; +\node[key, minimum height=12.5mm] at (21.5,\row) {F12}; + +\def\row{3.5} +\node[key, text width=13.0mm] at (0,\row) {0000}; +\node[key] at (1.8,\row) {0000}; +\node[key] at (2.8,\row) {0003}; +\node[key] at (3.8,\row) {0006}; +\node[key] at (4.8,\row) {0009}; +\node[key] at (5.8,\row) {000C}; +\node[key] at (6.8,\row) {000F}; +\node[key] at (7.8,\row) {F6}; +\node[key] at (8.8,\row) {F7}; +\node[key] at (9.8,\row) {F8}; +\node[key] at (10.8,\row) {F9}; +\node[key] at (11.8,\row) {F10}; +\node[key, text width=16mm] at (12.8,\row) {F12}; + +\node[key] at (18.5,\row) {F12}; +\node[key] at (19.5,\row) {F12}; +\node[key] at (20.5,\row) {F12}; + +\def\row{4.5} +\node[key, text width=16.75mm] at (0,\row) {0000}; +\node[key] at (2.3,\row) {0000}; +\node[key] at (3.3,\row) {0003}; +\node[key] at (4.3,\row) {0006}; +\node[key] at (5.3,\row) {0009}; +\node[key] at (6.3,\row) {000C}; +\node[key] at (7.3,\row) {000F}; +\node[key] at (8.3,\row) {F6}; +\node[key] at (9.3,\row) {F7}; +\node[key] at (10.3,\row) {F8}; +\node[key] at (11.3,\row) {F9}; +\node[key, text width=19.75mm] at (12.3,\row) {F12}; + +\node[key] at (16.25,\row) {F12}; + +\node[key] at (18.5,\row) {F12}; +\node[key] at (19.5,\row) {F12}; +\node[key] at (20.5,\row) {F12}; +\node[key, minimum height=12.5mm] at (21.5,\row) {F12}; + +\def\row{5.5} +\node[key, text width=8.875mm] at (0,\row) {0000}; +\node[key, text width=8.875mm] at (1.25,\row) {0000}; +\node[key, text width=8.875mm] at (2.5,\row) {0003}; +\node[key, text width=46.375mm] at (3.75,\row) {0006}; +\node[key, text width=8.875mm] at (10,\row) {F12}; +\node[key, text width=8.875mm] at (11.25,\row) {F12}; +\node[key, text width=8.875mm] at (12.5,\row) {F12}; +\node[key, text width=8.875mm] at (13.75,\row) {F12}; + +\node[key] at (15.25,\row) {F12}; +\node[key] at (16.25,\row) {F12}; +\node[key] at (17.25,\row) {F12}; + +\node[key] at (18.5,\row) {F12}; +\node[key] at (19.5,\row) {F12}; +\node[key] at (20.5,\row) {F12}; diff --git a/doc/random.txt b/doc/random.txt deleted file mode 100644 index 144407a..0000000 --- a/doc/random.txt +++ /dev/null @@ -1,111 +0,0 @@ -?? ?? ?? ???? ?? ?? rrggbb -04 79 01 1103 65 0100 ff0000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -04 fa 00 1103 65 0100 008000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - -04 7c 01 1103 68 0100 ff0000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - -?? ???? com? keyc ?? rrggbb -04 0100 0100 0000 00 000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -04 be03 1103 c000 00 fffbf0 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ; a -04 0200 0200 0000 00 000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - -04 0d03 11 03 0e01 00 fffbf0 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ; b -04 0703 11 03 0801 00 fffbf0 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ; c -04 c403 11 03 c600 00 fffbf0 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ; d -04 9703 11 03 9900 00 fffbf0 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ; o -04 d202 11 03 c000 00 ffff00 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ; a -04 5401 11 03 c000 00 000080 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -04 9700 11 03 0201 00 000080 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -04 5701 11 03 c300 00 000080 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -04 d000 11 03 3b01 00 000080 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - -04 9400 11 03 0000 00 000080 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - -04 4409 11 36 0000 00 0000fe 00800000800000800000800000800000800000800000800000800000800000800000800000000000800000800000ff000000000000 - -04 1800 06 01 0f00 00 020000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - -04 4402 04 2c 0000 00 aa5502150000000000000050000000000102030405060708090a0b0c0d0e0f00101214000000000000000000000000000000000000000000 - -esc == 00, left to right, top to bottom. - -Magic Number: 0x04 -Control sum: Sum of all the other bytes -Some kind of command: 0x0311 -Some kind of address: 0x0000 -Some kind of 0x00: 0x00 - -Payload: - bbggrr: 0x800000 - - -Command types: - - 0x01 - lights off - - 0x02 - lights on - - 0x06 - change settings - - 0x00 - mode - - 0x01 - brightness - - 0x02 - pulse speed - - 0x03 - direction - - 0x04 - Is RGB - - 0x05 - color (3 bytes) - - - 0x11 - change color - -02 // number of repetitions -03 // a chuj wie - -delay ?? ?? Key code -05 A0 02 0F -05 20 02 0F -05 A0 02 12 -05 20 02 12 -05 A0 02 0F -00 20 02 0F - -6D 00 65 00 68 00 // meh - -0000 AA 55 34 00 01 00 01 00 00 00 00 00 00 00 00 00 -0010 12 00 06 00 01 03 05 A0 02 0F 05 20 02 0F 05 A0 -0020 02 12 05 20 02 12 05 A0 02 0F 00 20 02 0F 6D 00 -0030 65 00 68 00 - -0000 AA 55 -60 00 // długość -02 00 // ilość wpisów ? -01 00 -0E 00 -00 0E -32 76 -03 00 -14 00 -36 00 -06 00 - -06 00 // number of keystrokes -02 // number of repetitions -03 // ?? - -05 A0 02 0F -05 20 02 0F -05 A0 02 12 -05 20 02 12 -05 A0 02 0F -00 20 02 0F - -6D 00 65 00 68 00 // name: meh - -08 00 -01 -03 -05 A0 01 20 // 05 = delay, 05*10ms A0 - press, 0x01 - modifier 0x20 - keycode -05 A0 02 0F -05 20 02 0F -05 A0 02 12 -05 20 02 12 -05 A0 02 0F -05 20 02 0F -00 20 01 20 - -6B 00 65 00 6B 00 - diff --git a/doc/skiller-sgk3.tex b/doc/skiller-sgk3.tex index 11c80fe..886440d 100644 --- a/doc/skiller-sgk3.tex +++ b/doc/skiller-sgk3.tex @@ -1,5 +1,5 @@ \documentclass[]{article} -\usepackage[T1]{fontenc} +\usepackage{fontspec} \usepackage{listings} \usepackage{titling} \usepackage[utf8]{inputenc} @@ -12,15 +12,153 @@ \usepackage{amstext} \usepackage{tikz} \usepackage{pgfplots} +\usepackage{bytefield} + +\usetikzlibrary{intersections,calc,positioning,arrows} %opening \title {Sharkoon Skiller MECH SGK3 USB control protocol} \author {Kacper Donat } +\tikzstyle{key}=[draw, rounded corners=.3mm, outer sep=2pt] +\DeclareRobustCommand{\key}[1]{\tikz[baseline={(K.base)}]{\node[key] (K) {#1}}} + \begin{document} {\huge \noindent \textbf{\thetitle} \vspace{5mm}} \\ {\large \theauthor} \\ - \section{Basic hardware information} - Port: USB, ID \texttt{0c45:8513} + \section{Basic info} + \begin{table}[H] + \begin{tabular}{l|r} + \textbf{Property} & \textbf{Value} \\ \hline + \textbf{VendorID} & 0c45\\ + \textbf{ProductID} & 8513 \\ + \textbf{Control Interface Index} & 1 \\ \hline + \textbf{Endianness} & Big Endian + \end{tabular} + \end{table} + + \section{Protocol} + Commands are submited to keybord via USB control transfer, state updates are send by keyboard via USB interrupts. + After every command, keyboard should send state update. + + Every packet has length of 64 bytes, and consist of 8 bytes header followed by payload, which can be up to 56 bytes + long. It's not possible to split packet into two, instead we should use addressing feature described later. + + \begin{figure}[H] + \centering + \begin{bytefield}[bitwidth=4em]{8} + \bitheader{0-7} \\ + \bitbox{1}{04h} & \bitbox{2}{checksum} & \bitbox{1}{cmd} & \bitbox{1}{len} & \bitbox{2}{addr} & \bitbox{1}{00h} \\ + \wordbox{3}{payload \\ up to len bytes} + \end{bytefield} + \end{figure} + + \subsection{Header} + Every packet starts with magic byte \texttt{04h}, probably used to determine control packets. Every value is written + in Big Endian manner, ie. LOW byte commes first. Header is ended with one null byte used for padding. + + \subsubsection{checksum} + Magic value is followed by 2-byte long checksum. Checksum calculation method is really trivial - it's just a sum of all + bytes after checksum. + + \begin{equation} + \mathtt{checksum} = \sum_{i=3}^{64} \mathtt{Byte}_i + \end{equation} + + \subsubsection{command} + Command is 1-byte long identifier, which determines payload kind and action taken by keyboard. Commands can be used + to update keyboard state, or to read state. + + \begin{table}[H] + \centering + \begin{tabular}{l|r|l} + \textbf{Command} & \textbf{Value} & \textbf{Description} \\ \hline + \texttt{SK\_CMD\_DISABLE\_LED} & \texttt{01h} & Disables key backlight, takes some time. \\ + \texttt{SK\_CMD\_ENABLE\_LED} & \texttt{02h} & Enables key backlight, takes some time. \\ + \texttt{SK\_CMD\_READ\_???} & \texttt{03h} & Reads some state. \\ + \texttt{SK\_CMD\_READ\_???} & \texttt{05h} & Reads some state. \\ + \texttt{SK\_CMD\_SET\_MODE} & \texttt{06h} & Changes keyboard mode or mode settings. \\ + \texttt{SK\_CMD\_MAP\_KEYS} & \texttt{08h} & Writes key mapping to controller. \\ + \texttt{SK\_CMD\_WRITE\_MACRO} & \texttt{0Ah} & Saves macros in controller memory. \\ + \texttt{SK\_CMD\_SET\_COLOR} & \texttt{11h} & Changes LED RGB values in key given keys, or key range. \\ + \end{tabular} + \end{table} + + All commands will be described more precisely in later sections. + + \subsubsection{len} + Length of payload, up to 56. + + \subsubsection{addr} + Address of changed value/register, command specific, if not needed should be set to \texttt{0000h}. + + \section{commands} + \subsection{\texttt{SK\_CMD\_DISABLE\_LED} (\texttt{01h})} + This command disables all LEDs\footnote{And maybe other things, hard to test.} on keyboard. It's useful when we + need to change few values and don't want to constantly blink diodes. + + \subsection{\texttt{SK\_CMD\_ENABLE\_LED} (\texttt{02h})} + This command re-enables all LEDs on keyboard. It's useful when we need to change few values and don't want to + constantly blink diodes. Both operations are quite long to execute, they can took about 1s. + + \subsection{\texttt{SK\_CMD\_SET\_COLOR} (\texttt{11h})} + This command can be used to change color of 1 or few LEDs at once. This operation takes less than 100ms, and + refreshes all diodes (in simpler words - they blink). + + \subsubsection{Addressing} + The \texttt{addr} header value identifies starting address of specific LED in keyboard. Rule of thumb is that keys + are addressed from left to right, top to bottom staring with \key{Esc} addressed \texttt{0000h}. Key address map can + be seen below. + + \begin{figure}[H] + \resizebox{\linewidth}{!}{ + \begin{tikzpicture}[ + x=.75cm, y=-.65cm, + key/.append style={ + text width=.7cm, + minimum height=6mm, + inner sep=0, + align=center, + font=\scriptsize\tt, + anchor=north west + } + ] + \input{color-keyboard.tex} + \end{tikzpicture} + } + \end{figure} + + \subsubsection{Payload} + Payload of this command consist of consecutive RGB values, 1-byte per channel, we could describe this struct like that: + \begin{verbatim} +typedef struct { + struct { + uint8_t r; + uint8_t g; + uint8_t b; + } color[SK_MAX_PAYLOAD / 3]; +} skillerctl_payload_color_t; + \end{verbatim} + + \subsubsection{Examples} + \begin{figure}[h] + \begin{verbatim} +0000 04 13 01 11 03 00 00 01 ff 00 00 00 00 00 00 00 +0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + \end{verbatim} + \caption{setting color of \key{Esc} to red (\texttt{ff0000h})} + \end{figure} + \begin{figure}[h] + \begin{verbatim} +0000 04 13 01 11 09 00 00 01 ff ff ff ff ff ff ff ff +0010 ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + \end{verbatim} + \caption{setting color of \key{Esc}, \key{F1}, \key{F2} to white (\texttt{ffffffh})} + \end{figure} + \end{document} diff --git "a/src/\\" "b/src/\\" deleted file mode 100644 index efe5286..0000000 --- "a/src/\\" +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef KEYS_H -#define KEYS_H - -#include - -#define SK_KEY_BAD 0xffff -#define SK_KEY_ESC 0x0000 -#define SK_KEY_F1 0x0003 -#define SK_KEY_F2 0x0006 -#define SK_KEY_F3 0x0009 -#define SK_KEY_F4 0x000C -#define SK_KEY_F5 0x000F -#define SK_KEY_F6 0x0012 -#define SK_KEY_F7 0x0015 -#define SK_KEY_F8 0x0018 -#define SK_KEY_F9 0x001B -#define SK_KEY_F10 0x001E -#define SK_KEY_F11 0x0021 -#define SK_KEY_F12 0x0024 -#define SK_KEY_PRTSC 0x002a -#define SK_KEY_SCROLL_LOCK 0x002d -#define SK_KEY_BREAK 0x0030 -#define SK_KEY_TILDE 0x003f -#define SK_KEY_1 0x003f -#define SK_KEY_2 0x003f -#define SK_KEY_3 0x003f -#define SK_KEY_4 0x003f -#define SK_KEY_5 0x003f -#define SK_KEY_6 0x003f -#define SK_KEY_7 0x003f -#define SK_KEY_8 0x003f -#define SK_KEY_9 0x003f -#define SK_KEY_0 0x003f -#define SK_KEY_MINUS 0x003f -#define SK_KEY_EQUALS 0x003f -#define SK_KEY_BACKSPACE 0x003f -#define SK_KEY_INSERT 0x003f -#define SK_KEY_HOME 0x003f -#define SK_KEY_PGUP 0x003f -#define SK_KEY_NUMLOCK 0x003f -#define SK_KEY_NUMDIV 0x003f -#define SK_KEY_NUMASTERIX 0x003f -#define SK_KEY_NUMMINUS 0x003f - -typedef uint16_t skillerctl_keyaddr_t; - -skillerctl_keyaddr_t skillerctl_keyaddr(const char* name); - -#endif /* KEYS_H */ - diff --git a/src/keys.h b/src/keys.h index 42c606b..bfa6173 100644 --- a/src/keys.h +++ b/src/keys.h @@ -112,6 +112,7 @@ typedef uint16_t skillerctl_keyaddr_t; skillerctl_keyaddr_t skillerctl_keyaddr(const char* name); + char* skillerctl_keystr(skillerctl_keyaddr_t key); #endif /* KEYS_H */ diff --git a/src/usb.c b/src/usb.c index 8b798d2..cd5f3c0 100644 --- a/src/usb.c +++ b/src/usb.c @@ -31,12 +31,19 @@ void skillerctl_send_command(libusb_device_handle *handle, skillerctl_packet_t * printf("Sending packet to keyboard: \n"); hexdump(buffer, SK_PACKET_SIZE); - libusb_control_transfer(handle, 0x21, 9, 0x0204, 0x0001, buffer, SK_PACKET_SIZE, 250); - libusb_interrupt_transfer(handle, SK_PORT_STATE, buffer, sizeof(buffer), &length, 250); + ASSERT(libusb_control_transfer( + handle, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + LIBUSB_REQUEST_SET_CONFIGURATION, + SK_CONFIGURATION, + SK_INTERFACE_CONTROL, + buffer, SK_PACKET_SIZE, + 250 + )); + + ASSERT(libusb_interrupt_transfer(handle, SK_PORT_STATE, buffer, sizeof(buffer), &length, 250)); printf("State from keyboard: \n"); - hexdump(buffer, SK_PACKET_SIZE); - - // we need to pretend that we're doing important stuff - usleep(100000); + hexdump(buffer, length); + usleep(5000); } diff --git a/src/usb.h b/src/usb.h index c0fc658..a5b2b7f 100644 --- a/src/usb.h +++ b/src/usb.h @@ -10,6 +10,11 @@ #define SK_PORT_STATE 0x82 #define SK_PORT_HID 0x81 +#define SK_INTERFACE_HID 0 +#define SK_INTERFACE_CONTROL 1 + +#define SK_CONFIGURATION 0x0204 + libusb_device* skillerctl_find_device(libusb_device **devices, size_t count); void skillerctl_send_command(libusb_device_handle *handle, skillerctl_packet_t *packet); diff --git a/src/utils.h b/src/utils.h index aaffe17..ceb3efa 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,8 +1,24 @@ #ifndef UTILS_H_ #define UTILS_H_ +#define ASSERT(line) do { \ + int r = line; \ + if (r < LIBUSB_SUCCESS && r < LIBUSB_ERROR_COUNT) { \ + printf("%s (%d) on %s:%d\n%s\n", libusb_error_name(r), r, __FILE__, __LINE__, #line);\ + } \ +} while (0) + +#define ASSERT_RETURN(line, ret) do { \ + int r = line; \ + if (r < LIBUSB_SUCCESS) { \ + printf("%s on %s:%d\n%s\n", libusb_error_name(r),__FILE__, __LINE__, #line);\ + return ret;\ + } \ +} while (0) + #include + void hexdump(void* bytes, size_t length); #endif