Added a new debugging channel io to allow read/writes on a port to be

logged to a file.
This commit is contained in:
Jonathan Buzzard 1998-10-25 09:55:29 +00:00 committed by Alexandre Julliard
parent b745eaa466
commit bdc48bd1e4
4 changed files with 387 additions and 93 deletions

View file

@ -0,0 +1,223 @@
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 oftern gouped together in five
sucessive 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 higest 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

View file

@ -68,90 +68,91 @@
#define dbch_int19 60 #define dbch_int19 60
#define dbch_int21 61 #define dbch_int21 61
#define dbch_int31 62 #define dbch_int31 62
#define dbch_ipaddress 63 #define dbch_io 63
#define dbch_key 64 #define dbch_ipaddress 64
#define dbch_keyboard 65 #define dbch_key 65
#define dbch_ldt 66 #define dbch_keyboard 66
#define dbch_listbox 67 #define dbch_ldt 67
#define dbch_listview 68 #define dbch_listbox 68
#define dbch_local 69 #define dbch_listview 69
#define dbch_mci 70 #define dbch_local 70
#define dbch_mcianim 71 #define dbch_mci 71
#define dbch_mciwave 72 #define dbch_mcianim 72
#define dbch_mdi 73 #define dbch_mciwave 73
#define dbch_menu 74 #define dbch_mdi 74
#define dbch_message 75 #define dbch_menu 75
#define dbch_metafile 76 #define dbch_message 76
#define dbch_midi 77 #define dbch_metafile 77
#define dbch_mmaux 78 #define dbch_midi 78
#define dbch_mmio 79 #define dbch_mmaux 79
#define dbch_mmsys 80 #define dbch_mmio 80
#define dbch_mmtime 81 #define dbch_mmsys 81
#define dbch_module 82 #define dbch_mmtime 82
#define dbch_mpr 83 #define dbch_module 83
#define dbch_msacm 84 #define dbch_mpr 84
#define dbch_msg 85 #define dbch_msacm 85
#define dbch_nativefont 86 #define dbch_msg 86
#define dbch_nonclient 87 #define dbch_nativefont 87
#define dbch_ntdll 88 #define dbch_nonclient 88
#define dbch_ole 89 #define dbch_ntdll 89
#define dbch_pager 90 #define dbch_ole 90
#define dbch_palette 91 #define dbch_pager 91
#define dbch_pidl 92 #define dbch_palette 92
#define dbch_print 93 #define dbch_pidl 93
#define dbch_process 94 #define dbch_print 94
#define dbch_profile 95 #define dbch_process 95
#define dbch_progress 96 #define dbch_profile 96
#define dbch_prop 97 #define dbch_progress 97
#define dbch_psapi 98 #define dbch_prop 98
#define dbch_psdrv 99 #define dbch_psapi 99
#define dbch_rebar 100 #define dbch_psdrv 100
#define dbch_reg 101 #define dbch_rebar 101
#define dbch_region 102 #define dbch_reg 102
#define dbch_relay 103 #define dbch_region 103
#define dbch_resource 104 #define dbch_relay 104
#define dbch_s 105 #define dbch_resource 105
#define dbch_scroll 106 #define dbch_s 106
#define dbch_security 107 #define dbch_scroll 107
#define dbch_segment 108 #define dbch_security 108
#define dbch_selector 109 #define dbch_segment 109
#define dbch_sem 110 #define dbch_selector 110
#define dbch_sendmsg 111 #define dbch_sem 111
#define dbch_shell 112 #define dbch_sendmsg 112
#define dbch_shm 113 #define dbch_shell 113
#define dbch_snoop 114 #define dbch_shm 114
#define dbch_sound 115 #define dbch_snoop 115
#define dbch_static 116 #define dbch_sound 116
#define dbch_statusbar 117 #define dbch_static 117
#define dbch_stress 118 #define dbch_statusbar 118
#define dbch_string 119 #define dbch_stress 119
#define dbch_syscolor 120 #define dbch_string 120
#define dbch_system 121 #define dbch_syscolor 121
#define dbch_tab 122 #define dbch_system 122
#define dbch_task 123 #define dbch_tab 123
#define dbch_text 124 #define dbch_task 124
#define dbch_thread 125 #define dbch_text 125
#define dbch_thunk 126 #define dbch_thread 126
#define dbch_timer 127 #define dbch_thunk 127
#define dbch_toolbar 128 #define dbch_timer 128
#define dbch_toolhelp 129 #define dbch_toolbar 129
#define dbch_tooltips 130 #define dbch_toolhelp 130
#define dbch_trackbar 131 #define dbch_tooltips 131
#define dbch_treeview 132 #define dbch_trackbar 132
#define dbch_tweak 133 #define dbch_treeview 133
#define dbch_uitools 134 #define dbch_tweak 134
#define dbch_updown 135 #define dbch_uitools 135
#define dbch_ver 136 #define dbch_updown 136
#define dbch_virtual 137 #define dbch_ver 137
#define dbch_vxd 138 #define dbch_virtual 138
#define dbch_win 139 #define dbch_vxd 139
#define dbch_win16drv 140 #define dbch_win 140
#define dbch_win32 141 #define dbch_win16drv 141
#define dbch_wing 142 #define dbch_win32 142
#define dbch_winsock 143 #define dbch_wing 143
#define dbch_wnet 144 #define dbch_winsock 144
#define dbch_x11 145 #define dbch_wnet 145
#define dbch_x11drv 146 #define dbch_x11 146
#define dbch_x11drv 147
/* Definitions for classes identifiers */ /* Definitions for classes identifiers */
#define dbcl_fixme 0 #define dbcl_fixme 0
#define dbcl_err 1 #define dbcl_err 1

View file

@ -4,7 +4,7 @@
#include "debugtools.h" #include "debugtools.h"
#endif #endif
#define DEBUG_CHANNEL_COUNT 147 #define DEBUG_CHANNEL_COUNT 148
#ifdef DEBUG_RUNTIME #ifdef DEBUG_RUNTIME
short debug_msg_enabled[][DEBUG_CLASS_COUNT] = { short debug_msg_enabled[][DEBUG_CLASS_COUNT] = {
{1, 1, 0, 0}, {1, 1, 0, 0},
@ -154,6 +154,7 @@ short debug_msg_enabled[][DEBUG_CLASS_COUNT] = {
{1, 1, 0, 0}, {1, 1, 0, 0},
{1, 1, 0, 0}, {1, 1, 0, 0},
{1, 1, 0, 0}, {1, 1, 0, 0},
{1, 1, 0, 0},
}; };
const char* debug_ch_name[] = { const char* debug_ch_name[] = {
"1", "1",
@ -219,6 +220,7 @@ const char* debug_ch_name[] = {
"int19", "int19",
"int21", "int21",
"int31", "int31",
"io",
"ipaddress", "ipaddress",
"key", "key",
"keyboard", "keyboard",

View file

@ -489,21 +489,41 @@ BOOL32 INSTR_EmulateInstruction( SIGCONTEXT *context )
{ {
case 0x6c: case 0x6c:
*((BYTE *)data) = IO_inport( DX_sig(context), 1); *((BYTE *)data) = IO_inport( DX_sig(context), 1);
TRACE(io, "0x%x < %02x @ %04x:%04x\n", DX_sig(context),
*((BYTE *)data), CS_sig(context), IP_sig(context));
break; break;
case 0x6d: case 0x6d:
if (long_op) if (long_op)
{
*((DWORD *)data) = IO_inport( DX_sig(context), 4); *((DWORD *)data) = IO_inport( DX_sig(context), 4);
TRACE(io, "0x%x < %08lx @ %04x:%04x\n", DX_sig(context),
*((DWORD *)data), CS_sig(context), IP_sig(context));
}
else else
{
*((WORD *)data) = IO_inport( DX_sig(context), 2); *((WORD *)data) = IO_inport( DX_sig(context), 2);
TRACE(io, "0x%x < %04x @ %04x:%04x\n", DX_sig(context),
*((WORD *)data), CS_sig(context), IP_sig(context));
}
break; break;
case 0x6e: case 0x6e:
IO_outport( DX_sig(context), 1, *((BYTE *)data)); IO_outport( DX_sig(context), 1, *((BYTE *)data));
TRACE(io, "0x%x > %02x @ %04x:%04x\n", DX_sig(context),
*((BYTE *)data), CS_sig(context), IP_sig(context));
break; break;
case 0x6f: case 0x6f:
if (long_op) if (long_op)
{
IO_outport( DX_sig(context), 4, *((DWORD *)data)); IO_outport( DX_sig(context), 4, *((DWORD *)data));
TRACE(io, "0x%x > %08lx @ %04x:%04x\n", DX_sig(context),
*((DWORD *)data), CS_sig(context), IP_sig(context));
}
else else
{
IO_outport( DX_sig(context), 2, *((WORD *)data)); IO_outport( DX_sig(context), 2, *((WORD *)data));
TRACE(io, "0x%x > %04x @ %04x:%04x\n", DX_sig(context),
*((WORD *)data), CS_sig(context), IP_sig(context));
}
break; break;
} }
} }
@ -609,45 +629,93 @@ BOOL32 INSTR_EmulateInstruction( SIGCONTEXT *context )
case 0xe4: /* inb al,XX */ case 0xe4: /* inb al,XX */
AL_sig(context) = IO_inport( instr[1], 1 ); AL_sig(context) = IO_inport( instr[1], 1 );
TRACE(io, "0x%x < %02x @ %04x:%04x\n", instr[1],
AL_sig(context), CS_sig(context), IP_sig(context));
EIP_sig(context) += prefixlen + 2; EIP_sig(context) += prefixlen + 2;
return TRUE; return TRUE;
case 0xe5: /* in (e)ax,XX */ case 0xe5: /* in (e)ax,XX */
if (long_op) EAX_sig(context) = IO_inport( instr[1], 4 ); if (long_op)
else AX_sig(context) = IO_inport( instr[1], 2 ); {
EAX_sig(context) = IO_inport( instr[1], 4 );
TRACE(io, "0x%x < %08lx @ %04x:%04x\n", instr[1],
EAX_sig(context), CS_sig(context), IP_sig(context));
}
else
{
AX_sig(context) = IO_inport( instr[1], 2 );
TRACE(io, "0x%x < %04x @ %04x:%04x\n", instr[1],
AX_sig(context), CS_sig(context), IP_sig(context));
}
EIP_sig(context) += prefixlen + 2; EIP_sig(context) += prefixlen + 2;
return TRUE; return TRUE;
case 0xe6: /* outb XX,al */ case 0xe6: /* outb XX,al */
IO_outport( instr[1], 1, AL_sig(context) ); IO_outport( instr[1], 1, AL_sig(context) );
TRACE(io, "0x%x > %02x @ %04x:%04x\n", instr[1],
AL_sig(context), CS_sig(context), IP_sig(context));
EIP_sig(context) += prefixlen + 2; EIP_sig(context) += prefixlen + 2;
return TRUE; return TRUE;
case 0xe7: /* out XX,(e)ax */ case 0xe7: /* out XX,(e)ax */
if (long_op) IO_outport( instr[1], 4, EAX_sig(context) ); if (long_op)
else IO_outport( instr[1], 2, AX_sig(context) ); {
IO_outport( instr[1], 4, EAX_sig(context) );
TRACE(io, "0x%x > %08lx @ %04x:%04x\n", instr[1],
EAX_sig(context), CS_sig(context), IP_sig(context));
}
else
{
IO_outport( instr[1], 2, AX_sig(context) );
TRACE(io, "0x%x > %04x @ %04x:%04x\n", instr[1],
AX_sig(context), CS_sig(context), IP_sig(context));
}
EIP_sig(context) += prefixlen + 2; EIP_sig(context) += prefixlen + 2;
return TRUE; return TRUE;
case 0xec: /* inb al,dx */ case 0xec: /* inb al,dx */
AL_sig(context) = IO_inport( DX_sig(context), 1 ); AL_sig(context) = IO_inport( DX_sig(context), 1 );
TRACE(io, "0x%x < %02x @ %04x:%04x\n", DX_sig(context),
AL_sig(context), CS_sig(context), IP_sig(context));
EIP_sig(context) += prefixlen + 1; EIP_sig(context) += prefixlen + 1;
return TRUE; return TRUE;
case 0xed: /* in (e)ax,dx */ case 0xed: /* in (e)ax,dx */
if (long_op) EAX_sig(context) = IO_inport( DX_sig(context), 4 ); if (long_op)
else AX_sig(context) = IO_inport( DX_sig(context), 2 ); {
EAX_sig(context) = IO_inport( DX_sig(context), 4 );
TRACE(io, "0x%x < %08lx @ %04x:%04x\n", DX_sig(context),
EAX_sig(context), CS_sig(context), IP_sig(context));
}
else
{
AX_sig(context) = IO_inport( DX_sig(context), 2 );
TRACE(io, "0x%x < %04x @ %04x:%04x\n", DX_sig(context),
AX_sig(context), CS_sig(context), IP_sig(context));
}
EIP_sig(context) += prefixlen + 1; EIP_sig(context) += prefixlen + 1;
return TRUE; return TRUE;
case 0xee: /* outb dx,al */ case 0xee: /* outb dx,al */
IO_outport( DX_sig(context), 1, AL_sig(context) ); IO_outport( DX_sig(context), 1, AL_sig(context) );
TRACE(io, "0x%x > %02x @ %04x:%04x\n", DX_sig(context),
AL_sig(context), CS_sig(context), IP_sig(context));
EIP_sig(context) += prefixlen + 1; EIP_sig(context) += prefixlen + 1;
return TRUE; return TRUE;
case 0xef: /* out dx,(e)ax */ case 0xef: /* out dx,(e)ax */
if (long_op) IO_outport( DX_sig(context), 4, EAX_sig(context) ); if (long_op)
else IO_outport( DX_sig(context), 2, AX_sig(context) ); {
IO_outport( DX_sig(context), 4, EAX_sig(context) );
TRACE(io, "0x%x > %08lx @ %04x:%04x\n", DX_sig(context),
EAX_sig(context), CS_sig(context), IP_sig(context));
}
else
{
IO_outport( DX_sig(context), 2, AX_sig(context) );
TRACE(io, "0x%x > %04x @ %04x:%04x\n", DX_sig(context),
AX_sig(context), CS_sig(context), IP_sig(context));
}
EIP_sig(context) += prefixlen + 1; EIP_sig(context) += prefixlen + 1;
return TRUE; return TRUE;