Resurrecting a dinosaur: the iBIZ KeySync keyboard for Pocket PC

Several years ago I came across a number of obsolete Pocket PC handhelds and accessories at a surplus sale. While the PocketPCs themselves weren’t worth keeping due to the dead batteries and slow processors, I held onto a compact portable keyboard with a 9-pin serial connector made by a company called iBIZ. I’m finally getting around to hooking the keyboard up and seeing if I can get it to work. In this post I’ll document the process of connecting to the iBIZ KeySync keyboard and decoding its protocol.

Identifying the hardware

The KeySync has a power switch on the left and runs on three triple-A batteries. Based on some search results from 2000 and 2001 we can be reasonably sure the keyboard uses RS-232, but I was unable to find any documentation on the protocol or baud rate.

We’ll use a standard USB-to-serial adapter to connect the keyboard to a desktop running Linux to figure it out for ourselves. The first step is figuring out what serial pinout it needs (straight-through or null modem), and what baud rate. When in doubt about an older serial device, 9600 is a good first choice to try (115200 or 57600 are likely speeds for newer RS-232 devices). We’ll set up the serial port for raw I/O, 9600 baud, 8 data bits, no parity, and 1 stop bit (9600/8N1):

Serial port initialization
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# Some of these settings may be unnecessary
stty -F /dev/ttyUSB0 \
  9600 cs8 -parenb -cstopb \
  raw cread clocal -echo -crtscts -echok -echoctl -echoke -iexten -hupcl \
  intr undef \
  quit undef \
  erase undef \
  kill undef \
  eof undef \
  start undef \
  stop undef \
  susp undef \
  rprnt undef \
  werase undef \
  lnext undef \
  flush undef

# Print current settings
stty -F /dev/ttyUSB0 -a
# speed 9600 baud; rows 0; columns 0; line = 0;
# intr = <undef>; quit = <undef>; erase = <undef>; kill = <undef>; eof = <undef>;
# eol = <undef>; eol2 = <undef>; swtch = <undef>; start = <undef>; stop = <undef>;
# susp = <undef>; rprnt = <undef>; werase = <undef>; lnext = <undef>;
# flush = <undef>; min = 1; time = 0;
# -parenb -parodd cs8 -hupcl -cstopb cread clocal -crtscts
# -ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff
# -iuclc -ixany -imaxbel -iutf8
# -opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
# -isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt
# -echoctl -echoke

Now we pick either a straight-through adapter or a null modem adapter (which swaps the TX/RX and handshaking lines), and try to read data from the port.

Decoding the protocol

Chances are the protocol will use some bitwise encoding for the keys and other information, so we want to print data we receive in hexadecimal. The od tool is useful for this.

Reading data one byte at a time
1
od -tx1 -w1 /dev/ttyUSB0

The straight-through adapter gave no results, so I switched to the null modem adapter.

Success! We have bytes!

a s d f g h j k l ;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
0000000 80
0000001 00
0000002 81
0000003 01
0000004 82
0000005 02
0000006 83
0000007 03
0000010 85
0000011 05
0000012 84
0000013 04
0000014 a6
0000015 26
0000016 a8
0000017 28
0000020 a5
0000021 25
0000022 a9
0000023 29

Each key produced one byte on press (make), then one byte on release (break). No amount of holding a key produced any repeats. It looks like the 0x80 bit indicates a press when set, and a release when not set. Then A is 0x00, S is 0x01, etc.

There are also six function keys, F1 through F6, which produce a make-make-break-break sequence. The first key is Alt, followed by the corresponding number key (e.g. Alt+1 for F1):

F1
1
2
3
4
0000000 b7
0000001 92
0000002 12
0000003 37

This doesn’t match either the XT or AT scancodes. There might be some other keyboard protocol that matches this, but it will be faster to write decoding code from scratch than to find a matching protocol. We just have to press every key and make a note of its key code, then arrange them in a nice table:

Key to character table
1
2
3
4
5
6
7
8
9
static const char key_chars[] = {
  // 0    1    2    3    4    5    6    7    8    9    a    b    c    d    e    f
  'a', 's', 'd', 'f', 'h', 'g', 'z', 'x', 'c', 'v',   0, 'b', 'q', 'w', 'e', 'r', // 0x00-0x0f
  'y', 't', '1', '2', '3', '4', '6', '5', '=', '9', '7', '-', '8', '0', ']', 'o', // 0x10-0x1f
  'u', '[', 'i', 'p', '\n','l', 'j', '\'','k', ';', '\\',',', '/', 'n', 'm', '.', // 0x20-0x2f
  '\t',' ', '`', '\b', // 0x30-0x33
  /* Shift, ctrl, etc. */ // 0x34-0x3f
  /* arrows */ // 0x7b-0x7e
};

A few lines of C code later, plus another table for character values when shift is pressed, and we can type on the console:

I couldn’t find documentation on the Fn key. I did discover that Fn+0-9 will type predefined strings related to the early-2000s Internet (and the Taiwanese origin of the keyboard):

Fn + numbers
1
2
3
4
5
6
7
8
9
10
fn1 www
fn2 ftp
fn3 news
fn4 net
fn5 com
fn6 edu
fn7 org
fn8 gov
fn9 html
fn10 tw

Code

The code for a simple C program to read keypresses is on GitHub: https://github.com/mike-bourgeous/ibiz_keysync

A next step for an adventurous developer might be writing a driver for Linux’s input layer.