Developer
— 19 Dec 2006 02:47 — 1179 days ago
I just bought the Logitech Z-10 Speakers and I’m very happy with my purchase, except for the fact that they come without Mac OS X drivers for the little LCD screen.
I’m trying to reverse engineer the USB protocol and write a little tool to control the display. I used a USB protocol sniffer to capture the Windows XP driver’s data transfer to the speaker (running Windows and the sniffer in Parallels VM :-). The bitmap format is bit weird, but it turns out that not much reverse engineering is necessary because the speakers use the same LCD screen format and wire protocol as the Logitech G15 gaming keyboard, for which an open source driver exists in the form of the g15tools package.
That package contains a routine (dumpPixmapIntoLCDFormat()) to do the actual bitmap conversion, but I wanted to fully understand the format and do the reverse transformation. The result of my experiments is the Perl script below.
It dumps the bitmap to the screen as ASCII art:
#!/usr/bin/perl
#
# Decode the bitmap format used for Logitech G15 gaming
# keyboard or Z-10 USB loudspeaker LCD screens.
#
# Written by Marc Liyanage <http://www.entropy.ch>
#
# Written to study the bitmap format in preparation for
# the development of a Mac OS X tool which needs to generate
# these bitmaps.
# Written with help in the form of the source code to the
# dumpPixmapIntoLCDFormat() function in the libg15 source
# code from the g15tools package at
# http://sourceforge.net/projects/g15tools.
#
# The raw USB data was captured on Windows using the great
# free sniff-bin / SniffUSB USB protocol sniffer
# from http://benoit.papillault.free.fr/usbsnoop/
#
#
# The screen is 160x43 pixels. The bitmap format covers
# 8 vertical pixels per byte. A "normal" input
# format which covers the pixels from left to right and top
# to bottom has each input byte spread to 8 consecutive output
# bytes (the matrix is transposed), i.e. the input value
#
# 0xff
#
# (which means 8 enabled pixels in a row) becomes the
# 8 low bits of
#
# 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01
#
# in the data block that is actually sent to the LCD
# screen via USB bulk data transfer. After 160 bytes
# (one full horizontal scan line), the next bit
# position is being used. That means that if input byte 1 and
# input byte 161 are both 0xff, the first 8 bytes of the output
# pattern look like this:
#
# 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03
#
# This Perl script reverses the transformation and
# dumps the bitmap to the screen as ASCII art.
#
use warnings;
use strict;
#my $data = "030000000000000000000000000000000000000000000000000000000000000000f0080808081000804040408000c0000000c000c08040408000f0400080404040f800804040408000c00000c00000c000c080404080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070888888100000f04000804040408000c04040408000c00000c00000c000004040408000f04000804040408000f8804040800007080808080400070808080700070808040f000f0000000f00070800070808080f00070808080700030c0300030c03000f0000000f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040808080700000708000708080807003f0808080700030c0300030c0300060909090f000708000708080804000f0000000f0080e070101070e080000080e070101070e0800000000000000080e070101070e0800000f0f01010101010100000000000000080e070101070e080000080e070101070e0800000000000000000000000000000000000000000000080e070101070e080000080e070101070e0800000000000000080e070101070e080000080e070101070e0800000000000000080e070101070e080000080e070101070e08000001f7fe08080e07f1f00001f7fe08080e07f1f000081810000001f7fe08080e07f1f00002363c38181c37e3c000081810000001f7fe08080e07f1f00001f7fe08080e07f1f000000000000000000000000000000000000000000001f7fe08080e07f1f00001f7fe08080e07f1f000081810000001f7fe08080e07f1f00001f7fe08080e07f1f000081810000001f7fe08080e07f1f00001f7fe08080e07f1f000000000000000000000000000000f8f0f0e0e0c04000000000000000000000000000000000000000000000f8f840e0f0f8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f8f0f0e0e0c04000000000000000000000000000000000000000000000f8f840e0f0f80000000000000000000000000000000000000000000000000000000303010100000000000000000000000000000000000000000000000000030300000103000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030301010000000000000000000000000000000000000000000000000003030000010300000000000000000000000000";
my $data = "03000000000000000000000000000000000000000000000000000000000000000000000000000000000080c0e0f0f0f8fcfcfefefeffffffffff3f1f0600000000000000000000000080c0e0f0f8fc7e7e3e1e0c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080e0f8fcffffffffffffffffffffffffff3f0f0300000000808080c0c0c0c0c0c08080800303010101000000000000000000000000000000000000000000000000000000f0f0f00000000000000000000000000000000000000000000000000000000000000000383838000000e0e0e000000000000000000000000000000000000000000000000000f8f8f800000000000000000000000000000000000000fcffffffffffffffffffffffffffffffff830000f8fcfeffffcfe7fbfb1f0f070707078ffffffffefc0000207070f0f0f0f0f0f0f0f0602000000000000000000000000000ffffff0000000000f8fcfe070303030307fefcf800f8fcfe0f0703030303ffffff0000ffffff000303ffffff030303f8fefe33333333333f3e3cf0fcfe0f03030303030302ffffff0303030303fffef800030300000000000000000f7ffffffffffffffffffffffffffffffffff8e0c10303070f3f7f7f7f6f4f6e6e3f1f1f0f07030100000000000000000000000000000000000000000000000000000000000f0f0f0c0c0c0c0c0d03070e0c0c0c0c0e07030100c1c3878f8e8c8c8c8cffff7f00000f0f0f000000070f0f0c0c0c0103070e0c0c0c0c0c0e000003070f0c0c0c0c0c0c040f0f0f00000000000f0f0f0000000000000000000000000001030f1f3f7ffffffffffffffffffffffffffffe0000000000000000000000000000000000000e3e7cfcf8f0f0e0c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101010101010100000000000000000000000000000000000000000000000000000000000000000000000000fc0000000000f8040404040800fc04040408f00000000000000000000000000000010103030707070707070301000000000000000000000000000000000000000000000000010303070700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000704040404000304040404020007040404020100";
my $X = 160; # width in pixels
my @values = map {hex($_)} $data =~ /(..)/g;
splice(@values, 0, 32); # 32 leading bytes
my @out;
my $bitcount = 0;
while (@values) {
my @line = splice(@values, 0, $X);
foreach my $bit (0 .. 7) {
foreach my $char (@line) {
my $value = ($char & 1 << $bit) >> $bit;
$out[$bitcount / 8] |= $value << $bitcount % 8;
$bitcount++;
}
}
}
while (@out) {
my @line =
map {[' ', '@']->[$_]}
map {reverse split(//)}
map {sprintf("%08b", $_)}
splice(@out, 0, $X / 8);
print @line;
print "\n";
}
Can you tell me what USB sniffer program you used on Windows? I'm trying to reverse engineer a similar windows USB device to operate on OS X.
Thanks for any help you can give.