My keyboard arrived with firmware version 0.61b, and the Configurator (I use version 0.0.0.6) requested an update to 0.62b for which Windows drivers and tools are available from the vendor website. Updating first failed when I tried it on Windows 2000 running inside VMware, but succeded under native Windows Vista.
All further tests were done with the 0.62b firmware, Linux 2.6.23.11 and when necessary Win 2k in a VMware 5.5.4 build-44386 (i686 in 32-bit mode).
Besides a standard USB HID v1.10 Device, a USB Mass Storage devices is available (NXP Semi LPC288x Disk), which provides a FAT file system that simply can be mounted.
I have usbmount running, which picked it up and provided a symlink in '/var/run/usbmount/NXP_Semi_LPC288x_Disk' which is what I will use further. The root directory contains only one entry: a subdirectory named 'vOptimus'.
The vOptimus directory contains:
Name | Size | |
---|---|---|
dynamic/ | dynamic layout images | |
normal/ | normal layout images | |
shift/ | shifted layout images | |
layout.sys | 114 | dynamic layout selection? |
order.sys | 4 at boot, 512 in win | ??? |
present.sys | 512 | key display availability? |
scancode.sys | 9216 | scancodes? |
upgrade.sys | 65536 | firmware upgrade facility? |
version.sys | 512 | firmware version string |
There are apparently two default image sets for the keyboard: 'normal/' and 'shift/' contain each 114 files with images for each key, unshifted and shifted. 'dynamic' contains a third set of images that can be switched on separately for every key.
'layout.sys' contains one byte for every key:
0x00 | Normal/shift layout active |
0x01 | Dynamic layout active |
'present.sys' could contain a 0x01 for every actually present key display, but as I have a fully populated keyboard, I am not sure.
'order.sys' is still a mystery. I had hoped for brightness and sleeptime control, but the values are inconclusive. On boot, only 4 zero-bytes can be read; after using the configurator, the first 6 bytes seem to have meaning, they are followed by 0x77 until EOF. Interestingly, the values I have seen are in the range of readable ASCII characters (mainly numbers).
'scancode.sys' contains scancodes. There is space for 0x40 bytes for each key. My first guess was that the first byte is the length, but that's wrong. [This part is from the preliminary spec] Every word is a movement:
Byte 1 | Byte 2 |
---|---|
0xFF = end marker else it's a bit field: Bit 0 = standard key function Bit 1 = key make (press) Bit 2 = key break |
Scancode |
'upgrade.bin' is a write-only file for firmware upgrades: "_upgrade" + firmware data + checksum [This part is from the preliminary spec]
The image format is simple: 2 bytes for every pixel, 2 * 48 * 48 = 4608, which is the size of a single image file.
The bytes are used like that (green gets one bit more):
hi | lo | ||||||||||||||
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
r | r | r | r | r | g | g | g | g | g | g | b | b | b | b | b |
Most of the displays are built in so that the first pixel is top-left, only the numeric '+' and 'enter' key displays are rotated 90 degrees left.
My first guess was that scancodes are stored in order of key numbers, but it doesn't add up.
By trial and error, I discovered that position 76*0x40 in scancode.sys is responsible for the key usually labeled 'L': changing that position to e.g. 0x38 changes the resulting character to a '/'. Originally, that position contains scancode 0x0f, which - surprise - maps to 'L' according to the 'Universal Serial Bus HID Usage Tables' version 1.12 chapter 10 on page 53 (get them from http://www.usb.org/developers/hidpage/).
With the help of 'optimus scancode-discover-map all', I tried to create a scancode table. It shows the key number (found by writing an image in the dynamic image file with that number), the raw scancode xev returns, the corresponding linux scancode (found by subtracting 8 - this is just a guess, but seems to work here; showkey -s returns the same values on the console), the corresponding USB Usage Id (as defined in the linux kernel HID driver) and the byte position in scancode.sys where the id can be found.
I tried to swap undefined and unusual USB Usage Ids by free ones, but none of those did show up.
These are the found mappings:
keynum 1 = xev keycode 90 = linux scancode 82 = usb id 0x62 keynum 2 = xev keycode 91 = linux scancode 83 = usb id 0x63 keynum 3 = xev keycode 108 = linux scancode 100 = usb id 0xe6 keynum 4 = xev keycode 89 = linux scancode 81 = usb id 0x5b = pos 0x71 keynum 5 = xev keycode 88 = linux scancode 80 = usb id 0x5a = pos 0x69 keynum 6 = xev keycode 87 = linux scancode 79 = usb id 0x59 = pos 0x61 keynum 8 = xev keycode 85 = linux scancode 77 = usb id 0x5e = pos 0x51 keynum 9 = xev keycode 84 = linux scancode 76 = usb id 0x5d = pos 0x49 keynum 10 = xev keycode 83 = linux scancode 75 = usb id 0x5c = pos 0x41 keynum 11 = xev keycode 79 = linux scancode 71 = usb id 0x5f = pos 0x21 keynum 12 = xev keycode 80 = linux scancode 72 = usb id 0x60 = pos 0x29 keynum 13 = xev keycode 81 = linux scancode 73 = usb id 0x61 = pos 0x31 keynum 14 = xev keycode 86 = linux scancode 78 = usb id 0x57 = pos 0x39 keynum 15 = xev keycode 82 = linux scancode 74 = usb id 0x56 = pos 0x19 keynum 16 = xev keycode 63 = linux scancode 55 = usb id 0x55 = pos 0x11 keynum 17 = xev keycode 112 = linux scancode 104 = usb id 0x4b = pos 0x2a keynum 18 = xev keycode 77 = linux scancode 69 = usb id 0x53 = pos 0x01 keynum 19 = xev keycode 99 = linux scancode 91 = usb id 0x93 = pos 0x4f keynum 20 = xev keycode 97 = linux scancode 89 = usb id 0x87 keynum 21 = xev keycode 106 = linux scancode 98 = usb id 0x54 = pos 0x09 keynum 22 = xev keycode 107 = linux scancode 99 = usb id 0x46 = pos 0x02 keynum 23 = xev keycode 103 = linux scancode 95 = usb id 0x8c keynum 24 = xev keycode 105 = linux scancode 97 = usb id 0xe4 keynum 25 = xev keycode 102 = linux scancode 94 = usb id 0x8b keynum 26 = xev keycode 104 = linux scancode 96 = usb id 0x58 keynum 27 = xev keycode 98 = linux scancode 90 = usb id 0x92 keynum 28 = xev keycode 100 = linux scancode 92 = usb id 0x8a keynum 29 = xev keycode 109 = linux scancode 101 keynum 30 = xev keycode 62 = linux scancode 54 = usb id 0xe5 keynum 31 = no reaction # right windows key keynum 32 = xev keycode 113 = linux scancode 105 = usb id 0x50 = pos 0x52 keynum 33 = xev keycode 61 = linux scancode 53 = usb id 0x38 = pos 0x53 keynum 34 = xev keycode 48 = linux scancode 40 = usb id 0x34 = pos 0x5c keynum 35 = xev keycode 36 = linux scancode 28 = usb id 0x28 = pos 0x64 keynum 36 = xev keycode 51 = linux scancode 43 = usb id 0x32 keynum 37 = xev keycode 35 = linux scancode 27 = usb id 0x30 = pos 0x65 keynum 38 = xev keycode 34 = linux scancode 26 = usb id 0x2f = pos 0x5d keynum 39 = xev keycode 22 = linux scancode 14 = usb id 0x2a = pos 0x6e keynum 40 = xev keycode 21 = linux scancode 13 = usb id 0x2e = pos 0x66 keynum 41 = xev keycode 20 = linux scancode 12 = usb id 0x2d = pos 0x5e keynum 42 = xev keycode 19 = linux scancode 11 = usb id 0x27 = pos 0x56 keynum 43 = xev keycode 32 = linux scancode 24 = usb id 0x12 = pos 0x4d # 'O' keynum 44 = xev keycode 33 = linux scancode 25 = usb id 0x13 = pos 0x55 keynum 45 = xev keycode 47 = linux scancode 39 = usb id 0x33 = pos 0x54 keynum 46 = xev keycode 46 = linux scancode 38 = usb id 0x0f = pos 0x4c # 'L' keynum 47 = xev keycode 60 = linux scancode 52 = usb id 0x37 = pos 0x4b keynum 48 = xev keycode 59 = linux scancode 51 = usb id 0x36 = pos 0x43 keynum 49 = xev keycode 58 = linux scancode 50 = usb id 0x10 = pos 0x3b keynum 50 = xev keycode 57 = linux scancode 49 = usb id 0x11 = pos 0x33 keynum 51 = xev keycode 44 = linux scancode 36 = usb id 0x0d = pos 0x3c keynum 52 = xev keycode 45 = linux scancode 37 = usb id 0x0e = pos 0x44 keynum 53 = xev keycode 31 = linux scancode 23 = usb id 0x0c = pos 0x45 keynum 54 = xev keycode 30 = linux scancode 22 = usb id 0x18 = pos 0x3d keynum 55 = xev keycode 18 = linux scancode 10 = usb id 0x26 = pos 0x4e keynum 56 = xev keycode 17 = linux scancode 9 = usb id 0x25 = pos 0x46 keynum 57 = xev keycode 16 = linux scancode 8 = usb id 0x24 = pos 0x3e keynum 58 = xev keycode 15 = linux scancode 7 = usb id 0x23 = pos 0x36 keynum 59 = xev keycode 28 = linux scancode 20 = usb id 0x17 = pos 0x2d keynum 60 = xev keycode 29 = linux scancode 21 = usb id 0x1c = pos 0x35 keynum 61 = xev keycode 43 = linux scancode 35 = usb id 0x0b = pos 0x34 keynum 62 = xev keycode 42 = linux scancode 34 = usb id 0x0a = pos 0x2c keynum 63 = xev keycode 55 = linux scancode 47 = usb id 0x19 = pos 0x23 keynum 64 = xev keycode 56 = linux scancode 48 = usb id 0x05 = pos 0x2b keynum 65 = xev keycode 54 = linux scancode 46 = usb id 0x06 = pos 0x1b keynum 66 = xev keycode 53 = linux scancode 45 = usb id 0x1b = pos 0x13 keynum 67 = xev keycode 41 = linux scancode 33 = usb id 0x09 = pos 0x24 keynum 68 = xev keycode 40 = linux scancode 32 = usb id 0x07 = pos 0x1c keynum 69 = xev keycode 26 = linux scancode 18 = usb id 0x08 = pos 0x1d keynum 70 = xev keycode 27 = linux scancode 19 = usb id 0x15 = pos 0x25 keynum 71 = xev keycode 14 = linux scancode 6 = usb id 0x22 = pos 0x2e keynum 72 = xev keycode 13 = linux scancode 5 = usb id 0x21 = pos 0x26 keynum 73 = xev keycode 12 = linux scancode 4 = usb id 0x20 = pos 0x1e keynum 74 = xev keycode 11 = linux scancode 3 = usb id 0x1f = pos 0x16 keynum 75 = xev keycode 25 = linux scancode 17 = usb id 0x1a = pos 0x15 keynum 76 = xev keycode 39 = linux scancode 31 = usb id 0x16 = pos 0x14 keynum 77 = xev keycode 52 = linux scancode 44 = usb id 0x1d = pos 0x0b keynum 78 = xev keycode 65 = linux scancode 57 = usb id 0x2c = pos 0x6f keynum 79 = xev keycode 64 = linux scancode 56 = usb id 0xe2 keynum 80 = xev keycode 115 = linux scancode 107 = usb id 0x4d = pos 0x3a keynum 81 = xev keycode 37 = linux scancode 29 = usb id 0xe0 keynum 82 = xev keycode 50 = linux scancode 42 = usb id 0xe1 keynum 83 = xev keycode 38 = linux scancode 30 = usb id 0x04 = pos 0x0c keynum 84 = xev keycode 66 = linux scancode 58 = usb id 0x39 = pos 0x04 keynum 85 = xev keycode 23 = linux scancode 15 = usb id 0x2b = pos 0x05 keynum 86 = xev keycode 24 = linux scancode 16 = usb id 0x14 = pos 0x0d keynum 87 = xev keycode 10 = linux scancode 2 = usb id 0x1e = pos 0x0e keynum 88 = xev keycode 49 = linux scancode 41 = usb id 0x35 = pos 0x06 keynum 89 = xev keycode 208 = linux scancode 200 keynum 90 = no reaction " one of the left function keys keynum 91 = xev keycode 133 = linux scancode 125 = usb id 0xe3 keynum 92 = xev keycode 129 = linux scancode 121 = usb id 0x85 keynum 93 = xev keycode 134 = linux scancode 126 = usb id 0xe7 keynum 94 = xev keycode 160 = linux scancode 152 = usb id 0xf9 keynum 95 = xev keycode 174 = linux scancode 166 = usb id 0xe9 keynum 96 = xev keycode 184 = linux scancode 176 = usb id 0xf7 keynum 97 = xev keycode 162 = linux scancode 154 keynum 98 = xev keycode 211 = linux scancode 203 keynum 99 = xev keycode 9 = linux scancode 1 = usb id 0x29 = pos 0x08 keynum 100 = xev keycode 67 = linux scancode 59 = usb id 0x3a = pos 0x10 keynum 101 = xev keycode 68 = linux scancode 60 = usb id 0x3b = pos 0x18 keynum 102 = xev keycode 69 = linux scancode 61 = usb id 0x3c = pos 0x20 keynum 103 = xev keycode 70 = linux scancode 62 = usb id 0x3d = pos 0x28 keynum 104 = xev keycode 71 = linux scancode 63 = usb id 0x3e = pos 0x30 keynum 105 = xev keycode 72 = linux scancode 64 = usb id 0x3f = pos 0x38 keynum 106 = xev keycode 73 = linux scancode 65 = usb id 0x40 = pos 0x40 keynum 107 = xev keycode 74 = linux scancode 66 = usb id 0x41 = pos 0x48 keynum 108 = xev keycode 75 = linux scancode 67 = usb id 0x42 = pos 0x50 keynum 109 = xev keycode 76 = linux scancode 68 = usb id 0x43 = pos 0x58 keynum 110 = xev keycode 95 = linux scancode 87 = usb id 0x44 = pos 0x60 keynum 111 = xev keycode 96 = linux scancode 88 = usb id 0x45 = pos 0x68 keynum 112 = xev keycode 111 = linux scancode 103 = usb id 0x52 = pos 0x4a keynum 113 = xev keycode 78 = linux scancode 70 = usb id 0x47 = pos 0x0a keynum 114 = xev keycode 110 = linux scancode 102 = usb id 0x4a = pos 0x22
These are the codes I tried to swap (keynum means here the byte position in scancode.sys):
replacing scancode 9d with 68 at keynum 7 replacing scancode 9b with 69 at keynum 15 replacing scancode a3 with 6a at keynum 39 replacing scancode 9c with 6b at keynum 47 replacing scancode 9a with 6c at keynum 55 replacing scancode a0 with 6d at keynum 71 replacing scancode 93 with 6e at keynum 79 replacing scancode ff with 6f at keynum 89 replacing scancode ff with 70 at keynum 99 replacing scancode ff with 71 at keynum 106 replacing scancode ff with 76 at keynum 107 replacing scancode ff with 78 at keynum 108 replacing scancode ff with 79 at keynum 112 replacing scancode ff with 7a at keynum 114
The mapping 'USB Usage ID' => 'linux scancode' (output of showkey -s) is defined in '/usr/src/linux/drivers/hid/usbhid/usbkbd.c'.
.../xorg-server-1.4.1~git20080131/hw/kdrive/linux/ contains some stuff for xorg keyboard handling. From here, I have the -8 offset.
Reassigning scancodes of ordinary keys seems to work, but I had no success with reassigning the left function keys.
The configurator 0.0.8 allows to remap keys to other verbs, so after looking at the changes, swapping 'Caps Lock' and 'Ctrl' is as simple as 'optimus scancode-set 4 72 87 39'. Still no luck on the keynum to scancode.sys-position mapping.
As the interaction of the configurator and keyboard is not so easily guessed, I fired up usbmon (now in binary mode with the usbmon userspace tool) and took this trace while toggleing the 'adjust automatically' option:
01 f7d11700 75.846110 S Bo:2:004:1 - 31 = 55534243 88c87981 00020000 00000a2a 0000000d 84000001 00000000 000000 02 f7d11700 75.846150 C Bo:2:004:1 0 31 > 03 f7d11180 75.848094 S Bo:2:004:1 - 512 = 62303533 41007777 77777777 77777777 77777777 77777777 77777777 77777777 04 f7d11180 75.848149 C Bo:2:004:1 0 512 > 05 f7d11180 75.850084 S Bi:2:004:1 - 512 < 06 f7d11180 75.850900 C Bi:2:004:1 0 13 = 55534253 88c87981 00000000 00 07 f644c500 77.600350 S Bo:2:004:1 - 31 = 55534243 480e7e81 00020000 00000a2a 0000000d 84000001 00000000 000000 08 f644c500 77.600461 C Bo:2:004:1 0 31 > 09 f727cf00 77.602110 S Bo:2:004:1 - 512 = 62303533 00007777 77777777 77777777 77777777 77777777 77777777 77777777 10 f727cf00 77.602206 C Bo:2:004:1 0 512 > 11 f727cf00 77.604093 S Bi:2:004:1 - 512 < 12 f727cf00 77.604831 C Bi:2:004:1 0 13 = 55534253 480e7e81 00000000 00 13 f7d11680 94.981107 S Bo:2:004:1 - 31 = 55534243 280c7e81 00020000 00000a2a 0000000d 84000001 00000000 000000 14 f7d11680 94.981162 C Bo:2:004:1 0 31 > 15 f7d11600 94.983284 S Bo:2:004:1 - 512 = 62303533 41007777 77777777 77777777 77777777 77777777 77777777 77777777 16 f7d11600 94.983409 C Bo:2:004:1 0 512 > 17 f7d11700 94.985093 S Bi:2:004:1 - 512 < 18 f7d11700 94.986159 C Bi:2:004:1 0 13 = 55534253 280c7e81 00000000 00 19 f7d11c00 95.753292 S Bo:2:004:1 - 31 = 55534243 a8cd8981 00020000 00000a2a 0000000d 84000001 00000000 000000 20 f7d11c00 95.753380 C Bo:2:004:1 0 31 > 21 f7d11c00 95.770114 S Bo:2:004:1 - 512 = 62303533 00007777 77777777 77777777 77777777 77777777 77777777 77777777 22 f7d11c00 95.770255 C Bo:2:004:1 0 512 > 23 f644c400 95.786370 S Bi:2:004:1 - 512 < 24 f644c400 95.786505 C Bi:2:004:1 0 13 = 55534253 a8cd8981 00000000 00
Mhm. 'USB Mass Storage BOT' http://www.usb.org/developers/devclass_docs/usbmassbulk_10.pdf (linked from Wikipedia's 'USB mass storage device class' http://en.wikipedia.org/wiki/USB_mass_storage_device_class) gives a clue: 55534243 is the Command Block Wrapper Signature (p. 13) backwards and the length matches, too.
BOT specifies little endian endcoding, but usbmon outputs big endian, so we have to be careful with the values.
0-3 4-7 8-11 12 13 14 15-30 55534243 88c87981 00020000 00 00 0a 2a0000000d8400000100000000000000 ^^^^^^^^ ^^^^^^^^ ^^^^^^^^ ^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ! ! ! ! ! ! ! ! ! ! ! ! ! CBWCB = ! ! ! ! ! bCBWCBLength ! ! ! ! bCBWLUN/Reserved ! ! ! bmCBWFlags/Reserved, Bit 7 (Direction) = 0 = Data-Out from host to the device ! ! dCBWDataTransferLength = 200h ! dCBWTag dCBWSignature = 43425355h (little endian)
According to the SCSI-3 Block Command Set (http://www.t10.org/ftp/t10/drafts/sbc/sbc-r08c.pdf) p. 57, 2Ah is the WRITE(10) command:
0 1 2-5 6 7-8 9 2a 00 00000d84 00 0001 00 000000000000 ^^ ^^ ^^^^^^^^ ^^ ^^^^ ^^ ! ! ! ! ! ! ! ! ! ! ! Control ! ! ! ! Transfer length = 1 block ! ! ! Reserved ! ! Logical Block Address ! Flags Operation Code = 2Ah
So, all writes are going to the same block, the replies from the device are Command Status Wrapper informations.
For the filesystem I saw, here are the first sector numbers for the files:
Name | Sector # |
---|---|
layout.sys | 3462 |
order.sys | 3460 |
present.sys | 3458 |
scancode.sys | 3464 |
upgrade.bin | 3482 |
version.sys | 3456 |
So it's really 'order.sys'! Looking at the written data:
First things first: how to replace the windows logo with tux?
The changes stay as long as the keyboard has power, after a reboot, they are gone.
The next step is probably to create a clock or something and use the dynamic mode to frequently update it.
Just to show a nice application I have two scripts to show the state of my mail inbox:
For testing purposes I'm providing the 'optimus' tool which modifies the files in the vOptimus directory. It also provides 'png2optimus' and 'optimus2png' (simply do 'ln optimus png2optimus; ln optimus optimus2png')
Requires perl with additional modules: GD, X11::Protocol, Getopt::Long.
WARNING! This stuff comes with NO WARRANTY! It might blow up your keyboard, change voltage settings, burn out the oleds by prohibiting sleep mode, overwrite the firmware, wear the flash, and so on.
Latest version is optimus-20090201.tar.gz (15.70 KiB).
Syntax: optimus [options] [cmd] [arguments] options are: --voptimus=[ path ] path to vOptimus directory --display=[ name ] X display name --red display text on red background --yellow display text on yellow background --green display text on green background --led display a small green LED --bar=[ 1 - 6 ] display a bar with 1 - 6 elements commands are: version reads the version brightness [ 0 - 100 [ auto ] ] set keyboard brightness sleep-timeout [ 0 - 9999 | never ] set sleep timeout layout-set [ all | olednum [olednum] ...] set the layout bit layout-clr [ all | olednum [olednum] ...] clear the layout bit scancode-get [ all | keynum [keynum] ...] reads the scancodes scancode-set [ keynum scancode [...] ] sets the scancodes scancode-discover-map [ all | olednum ...] interactive scancode map disc. disp-olednum display oled number on keyboard disp-keynum display key number on keyboard disp-scancode display scancodes on keyboard disp-png [ olednum filename [...] ] display png image on key disp-text [ olednum text [...] ] display text on key (dynamic) disp-text-normal [ olednum text [...] ] display text on key (normal) disp-text-shift [ olednum text [...] ] display text on key (shifted) olednum is a decimal number between 1 and 114 keynum is a decimal number between 1 and 137 scancode is a string of bytes given in hex, e.g. 0105 where every word aabb means (aa : 01 = normal function, 02 = mark, 04 = break; bb : scancode, see USB HUT) alternatively, a decimal byte value can be prefixed with 'k' to try keycode conversion, e.g. k102, 's' to take the value as decimal scancode, e.g. s102.
Interne links sind grün, externe links, die nicht unter meiner Kontrolle stehen, sind zusätzlich gestrichelt unterstrichen. Mallorn CA Zertifikat.
Copyright 2002-2011 Michael Stürmer ·
<ms@www.mallorn.de> ·
letzte Änderung am 18.05.2011 23:10.