| cat > /dev/null <<EOF | 
 | The above line is necessary, leave it alone!! | 
 | -------------------------------------------------------------------- | 
 |  | 
 | DOING A HARDWARE TRACE IN WINE | 
 | ------------------------------ | 
 |  | 
 | The primary reason to do this is to reverse engineer a hardware device | 
 | for which you don't have documentation, but can get to work under Wine. | 
 |  | 
 | This lot is aimed at parallel port devices, and in particular parallel port | 
 | scanners which are now so cheap they are virtually being given away. The | 
 | problem is that few manufactures will release any programming information which | 
 | prevents drivers being written for Sane, and the traditional technique of using | 
 | DOSemu to produce the traces does not work as the scanners invariably only have | 
 | drivers for Windows. | 
 |  | 
 | Please note that I have not been able to get my scanner working properly (a | 
 | UMAX Astra 600P), but a couple of people have reported success with at least | 
 | the Artec AS6e scanner. I am not in the process of developing any driver nor do | 
 | I intend to, so don't bug me about it. My time is now spent writting programs | 
 | to set things like battery save options under Linux on Toshiba laptops, ans as | 
 | such I don't have any spare time for writting a driver for a parallel port | 
 | scanner etc. | 
 |  | 
 | Presuming that you have compiled and installed wine the first thing to do is is | 
 | to enable direct hardware access to your parallel port. To do this edit | 
 | wine.conf (usually in /usr/local/etc) and in the ports section add the | 
 | following two lines | 
 |  | 
 | read=0x378,0x379,0x37a,0x37c,0x77a | 
 | write=0x378,x379,0x37a,0x37c,0x77a | 
 |  | 
 | This adds the necessary access required for SPP/PS2/EPP/ECP parallel port on | 
 | LPT1. You will need to adjust these number accordingly if your parallel port is | 
 | on LPT2 or LPT0. | 
 |  | 
 | When starting wine use the following command line, where XXXX is the program | 
 | you need to run in order to access your scanner, and YYYY is the file your | 
 | trace will be stored in: | 
 |  | 
 |     wine -debugmsg +io XXXX 2> >(sed 's/^[^:]*:io:[^ ]* //' > YYYY) | 
 |  | 
 | You will need large amounts of hard disk space (read hundreds of megabytes if | 
 | you do a full page scan), and for reasonable performance a really fast | 
 | processor and lots of RAM. | 
 |  | 
 | You might well find the log compression program that David Campbell | 
 | <campbell@torque.net> wrote helpfull in reducing the size of the log files. | 
 | This can be obtained by the following command: | 
 |  | 
 |     sh ioport-trace-hints | 
 |  | 
 | This should extract shrink.c (which is located at the end of this file. Compile | 
 | the log compression program by: | 
 |  | 
 |     cc shrink.c -o shrink | 
 |  | 
 | Use the shrink program to reduce the physical size of the raw log as follows: | 
 |  | 
 |     cat log | shrink > log2 | 
 |  | 
 | The trace has the basic form of | 
 |  | 
 |     XXXX > YY @ ZZZZ:ZZZZ | 
 |  | 
 | where XXXX is the port in hexidecimal being accessed, YY is the data written | 
 | (or read) from the port, and ZZZZ:ZZZZ is the address in memory of the | 
 | instruction that accessed the port. The direction of the arrow indicates | 
 | whether the data was written or read from the port. | 
 |  | 
 |     > data was written to the port | 
 |     < data was read from the port | 
 |  | 
 |  | 
 | My basic tip for interperating these logs is to pay close attention to the | 
 | addresses of the IO instructions. There grouping and sometimes proximity should | 
 | reveal the presence of subroutines in the driver. By studying the different | 
 | versions you should be able to work them out. For example consider the | 
 | following section of trace from my UMAX Astra 600P | 
 |  | 
 |     0x378 > 55 @ 0297:01ec | 
 |     0x37a > 05 @ 0297:01f5 | 
 |     0x379 < 8f @ 0297:01fa | 
 |     0x37a > 04 @ 0297:0211 | 
 |     0x378 > aa @ 0297:01ec | 
 |     0x37a > 05 @ 0297:01f5 | 
 |     0x379 < 8f @ 0297:01fa | 
 |     0x37a > 04 @ 0297:0211 | 
 |     0x378 > 00 @ 0297:01ec | 
 |     0x37a > 05 @ 0297:01f5 | 
 |     0x379 < 8f @ 0297:01fa | 
 |     0x37a > 04 @ 0297:0211 | 
 |     0x378 > 00 @ 0297:01ec | 
 |     0x37a > 05 @ 0297:01f5 | 
 |     0x379 < 8f @ 0297:01fa | 
 |     0x37a > 04 @ 0297:0211 | 
 |     0x378 > 00 @ 0297:01ec | 
 |     0x37a > 05 @ 0297:01f5 | 
 |     0x379 < 8f @ 0297:01fa | 
 |     0x37a > 04 @ 0297:0211 | 
 |     0x378 > 00 @ 0297:01ec | 
 |     0x37a > 05 @ 0297:01f5 | 
 |     0x379 < 8f @ 0297:01fa | 
 |     0x37a > 04 @ 0297:0211 | 
 |  | 
 | As you can see their is a repeating structure starting at address 0297:01ec | 
 | that consists of four io access on the parallel port. Looking at it the first | 
 | io access writes a changing byte to the data port the second always writes the | 
 | byte 0x05 to the control port, then a value which always seems to 0x8f is read | 
 | from the status port at which point a byte 0x04 is written to the control port. | 
 | By studying this and other sections of the trace we can write a C routine that | 
 | emulates this, shown below with some macros to make reading/writing on the | 
 | parallel port easier to read. | 
 |  | 
 |  | 
 | #define r_dtr(x)        inb(x) | 
 | #define r_str(x)        inb(x+1) | 
 | #define r_ctr(x)        inb(x+2) | 
 | #define w_dtr(x,y)      outb(y, x) | 
 | #define w_str(x,y)      outb(y, x+1) | 
 | #define w_ctr(x,y)      outb(y, x+2) | 
 |  | 
 | /* | 
 |  * Seems to be sending a command byte to the scanner | 
 |  * | 
 |  */ | 
 | int udpp_put(int udpp_base, unsigned char command) | 
 | { | 
 |         int loop,value; | 
 |  | 
 |         w_dtr(udpp_base, command); | 
 |         w_ctr(udpp_base, 0x05); | 
 |  | 
 |         for (loop=0;loop<10;loop++) | 
 |                 if (((value=r_str(udpp_base)) & 0x80)!=0x00) { | 
 |                         w_ctr(udpp_base, 0x04); | 
 |                         return value & 0xf8; | 
 |                         } | 
 |  | 
 |         return (value & 0xf8) | 0x01; | 
 | } | 
 |  | 
 |  | 
 | For the UMAX Astra 600P only seven such routines exist (well 14 really, seven | 
 | for SPP and seven for EPP). Whether you choose to disassemble the driver at | 
 | this point to verify the routines is your own choice. If you do, the address | 
 | from the trace should help in locating them in the disassembly. | 
 |  | 
 | You will probably then find it useful to write a script/perl/C program to | 
 | analyse the logfile and decode them futher as this can reveal higher level | 
 | grouping of the low level routines. For example from the logs from my UMAX | 
 | Astra 600P when decoded futher reveal (this is a small snippet) | 
 |  | 
 |  | 
 | start: | 
 | put: 55 8f | 
 | put: aa 8f | 
 | put: 00 8f | 
 | put: 00 8f | 
 | put: 00 8f | 
 | put: c2 8f | 
 | wait: ff | 
 | get: af,87 | 
 | wait: ff | 
 | get: af,87 | 
 | end: cc | 
 | start: | 
 | put: 55 8f | 
 | put: aa 8f | 
 | put: 00 8f | 
 | put: 03 8f | 
 | put: 05 8f | 
 | put: 84 8f | 
 | wait: ff | 
 |  | 
 | From this it is easy to see that put routine is often grouped together in five | 
 | successive calls sending information to the scanner. Once these are understood | 
 | it should be possible to process the logs further to show the higher level | 
 | routines in an easy to see format. Once the highest level format that you | 
 | can derive from this process is understood, you then need to produce a | 
 | series of scans varying only one parameter between them, so you can | 
 | discover how to set the various parameters for the scanner. | 
 |  | 
 |  | 
 | Jonathan Buzzard | 
 | <jab@hex.prestel.co.uk> | 
 |  | 
 |  | 
 | -------------------------------------------------------------------- | 
 | The following is the shrink.c program. | 
 | EOF | 
 | cat > shrink.c <<EOF | 
 | #include <stdio.h> | 
 | #include <string.h> | 
 |  | 
 | void  | 
 | main (void) | 
 | { | 
 |   char buff[256], lastline[256]; | 
 |   int count; | 
 |  | 
 |   count = 0; | 
 |   lastline[0] = 0; | 
 |  | 
 |   while (!feof (stdin)) | 
 |     { | 
 |       fgets (buff, sizeof (buff), stdin); | 
 |       if (strcmp (buff, lastline) == 0) | 
 | 	{ | 
 | 	  count++; | 
 | 	} | 
 |       else | 
 | 	{ | 
 | 	  if (count > 1) | 
 | 	    fprintf (stdout, "# Last line repeated %i times #\n", count); | 
 | 	  fprintf (stdout, "%s", buff); | 
 | 	  strcpy (lastline, buff); | 
 | 	    count = 1; | 
 | 	} | 
 |     } | 
 | } | 
 | EOF |