Added paragraph about different dll versions and structure sizes.
[wine] / documentation / ioport-trace-hints
1 cat > /dev/null <<EOF
2 The above line is necessary, leave it alone!!
3 --------------------------------------------------------------------
4
5 DOING A HARDWARE TRACE IN WINE
6 ------------------------------
7
8 The primary reason to do this is to reverse engineer a hardware device
9 for which you don't have documentation, but can get to work under Wine.
10
11 This lot is aimed at parallel port devices, and in particular parallel port
12 scanners which are now so cheap they are virtually being given away. The
13 problem is that few manufactures will release any programming information which
14 prevents drivers being written for Sane, and the traditional technique of using
15 DOSemu to produce the traces does not work as the scanners invariably only have
16 drivers for Windows.
17
18 Please note that I have not been able to get my scanner working properly (a
19 UMAX Astra 600P), but a couple of people have reported success with at least
20 the Artec AS6e scanner. I am not in the process of developing any driver nor do
21 I intend to, so don't bug me about it. My time is now spent writting programs
22 to set things like battery save options under Linux on Toshiba laptops, ans as
23 such I don't have any spare time for writting a driver for a parallel port
24 scanner etc.
25
26 Presuming that you have compiled and installed wine the first thing to do is is
27 to enable direct hardware access to your parallel port. To do this edit
28 wine.conf (usually in /usr/local/etc) and in the ports section add the
29 following two lines
30
31 read=0x378,0x379,0x37a,0x37c,0x77a
32 write=0x378,x379,0x37a,0x37c,0x77a
33
34 This adds the necessary access required for SPP/PS2/EPP/ECP parallel port on
35 LPT1. You will need to adjust these number accordingly if your parallel port is
36 on LPT2 or LPT0.
37
38 When starting wine use the following command line, where XXXX is the program
39 you need to run in order to access your scanner, and YYYY is the file your
40 trace will be stored in:
41
42     wine -debugmsg +io XXXX 2> >(sed 's/^[^:]*:io:[^ ]* //' > YYYY)
43
44 You will need large amounts of hard disk space (read hundreds of megabytes if
45 you do a full page scan), and for reasonable performance a really fast
46 processor and lots of RAM.
47
48 You might well find the log compression program that David Campbell
49 <campbell@torque.net> wrote helpfull in reducing the size of the log files.
50 This can be obtained by the following command:
51
52     sh ioport-trace-hints
53
54 This should extract shrink.c (which is located at the end of this file. Compile
55 the log compression program by:
56
57     cc shrink.c -o shrink
58
59 Use the shrink program to reduce the physical size of the raw log as follows:
60
61     cat log | shrink > log2
62
63 The trace has the basic form of
64
65     XXXX > YY @ ZZZZ:ZZZZ
66
67 where XXXX is the port in hexidecimal being accessed, YY is the data written
68 (or read) from the port, and ZZZZ:ZZZZ is the address in memory of the
69 instruction that accessed the port. The direction of the arrow indicates
70 whether the data was written or read from the port.
71
72     > data was written to the port
73     < data was read from the port
74
75
76 My basic tip for interperating these logs is to pay close attention to the
77 addresses of the IO instructions. There grouping and sometimes proximity should
78 reveal the presence of subroutines in the driver. By studying the different
79 versions you should be able to work them out. For example consider the
80 following section of trace from my UMAX Astra 600P
81
82     0x378 > 55 @ 0297:01ec
83     0x37a > 05 @ 0297:01f5
84     0x379 < 8f @ 0297:01fa
85     0x37a > 04 @ 0297:0211
86     0x378 > aa @ 0297:01ec
87     0x37a > 05 @ 0297:01f5
88     0x379 < 8f @ 0297:01fa
89     0x37a > 04 @ 0297:0211
90     0x378 > 00 @ 0297:01ec
91     0x37a > 05 @ 0297:01f5
92     0x379 < 8f @ 0297:01fa
93     0x37a > 04 @ 0297:0211
94     0x378 > 00 @ 0297:01ec
95     0x37a > 05 @ 0297:01f5
96     0x379 < 8f @ 0297:01fa
97     0x37a > 04 @ 0297:0211
98     0x378 > 00 @ 0297:01ec
99     0x37a > 05 @ 0297:01f5
100     0x379 < 8f @ 0297:01fa
101     0x37a > 04 @ 0297:0211
102     0x378 > 00 @ 0297:01ec
103     0x37a > 05 @ 0297:01f5
104     0x379 < 8f @ 0297:01fa
105     0x37a > 04 @ 0297:0211
106
107 As you can see their is a repeating structure starting at address 0297:01ec
108 that consists of four io access on the parallel port. Looking at it the first
109 io access writes a changing byte to the data port the second always writes the
110 byte 0x05 to the control port, then a value which always seems to 0x8f is read
111 from the status port at which point a byte 0x04 is written to the control port.
112 By studying this and other sections of the trace we can write a C routine that
113 emulates this, shown below with some macros to make reading/writing on the
114 parallel port easier to read.
115
116
117 #define r_dtr(x)        inb(x)
118 #define r_str(x)        inb(x+1)
119 #define r_ctr(x)        inb(x+2)
120 #define w_dtr(x,y)      outb(y, x)
121 #define w_str(x,y)      outb(y, x+1)
122 #define w_ctr(x,y)      outb(y, x+2)
123
124 /*
125  * Seems to be sending a command byte to the scanner
126  *
127  */
128 int udpp_put(int udpp_base, unsigned char command)
129 {
130         int loop,value;
131
132         w_dtr(udpp_base, command);
133         w_ctr(udpp_base, 0x05);
134
135         for (loop=0;loop<10;loop++)
136                 if (((value=r_str(udpp_base)) & 0x80)!=0x00) {
137                         w_ctr(udpp_base, 0x04);
138                         return value & 0xf8;
139                         }
140
141         return (value & 0xf8) | 0x01;
142 }
143
144
145 For the UMAX Astra 600P only seven such routines exist (well 14 really, seven
146 for SPP and seven for EPP). Whether you choose to disassemble the driver at
147 this point to verify the routines is your own choice. If you do, the address
148 from the trace should help in locating them in the disassembly.
149
150 You will probably then find it useful to write a script/perl/C program to
151 analyse the logfile and decode them futher as this can reveal higher level
152 grouping of the low level routines. For example from the logs from my UMAX
153 Astra 600P when decoded futher reveal (this is a small snippet)
154
155
156 start:
157 put: 55 8f
158 put: aa 8f
159 put: 00 8f
160 put: 00 8f
161 put: 00 8f
162 put: c2 8f
163 wait: ff
164 get: af,87
165 wait: ff
166 get: af,87
167 end: cc
168 start:
169 put: 55 8f
170 put: aa 8f
171 put: 00 8f
172 put: 03 8f
173 put: 05 8f
174 put: 84 8f
175 wait: ff
176
177 From this it is easy to see that put routine is oftern gouped together in five
178 sucessive calls sending information to the scanner. Once these are understood
179 it should be possible to process the logs further to show the higher level
180 routines in an easy to see format. Once the higest level format that you
181 can derive from this process is understood, you then need to produce a
182 series of scans varying only one parameter between them, so you can
183 discover how to set the various parameters for the scanner.
184
185
186 Jonathan Buzzard
187 <jab@hex.prestel.co.uk>
188
189
190 --------------------------------------------------------------------
191 The following is the shrink.c program.
192 EOF
193 cat > shrink.c <<EOF
194 #include <stdio.h>
195 #include <string.h>
196
197 void 
198 main (void)
199 {
200   char buff[256], lastline[256];
201   int count;
202
203   count = 0;
204   lastline[0] = 0;
205
206   while (!feof (stdin))
207     {
208       fgets (buff, sizeof (buff), stdin);
209       if (strcmp (buff, lastline) == 0)
210         {
211           count++;
212         }
213       else
214         {
215           if (count > 1)
216             fprintf (stdout, "# Last line repeated %i times #\n", count);
217           fprintf (stdout, "%s", buff);
218           strcpy (lastline, buff);
219             count = 1;
220         }
221     }
222 }
223 EOF