source: xtideuniversalbios/trunk/Serial_Server/library/Process.cpp@ 620

Last change on this file since 620 was 589, checked in by Krister Nordvall, 9 years ago

Changes:

  • BIOS: Fixed a purely cosmetic bug from r542 where, in builds containing MODULE_EBIOS, the boot menu would display an incorrect drive size (0.4 kB with MODULE_STRINGS_COMPRESSED or 0.5 kB without) for old drives with no support for LBA.
  • Fixed a bug from r392 where Vision_DetectAndReturnIDinAXandPortInDXifControllerPresent would return the ID in AL instead of AH (if DANGEROUS_DETECTION had been defined).
  • Fixed a bug from r587 in AdvAtaInit.asm that would prevent detection of QDI Vision controllers.
  • Also changed how the QDI Vision IDs are defined (removed the need for shifting) to avoid confusion. This fixed a potential bug from r587 in AdvAtaInit.asm where some IDs were not being shifted.
  • Fixed a bug in PDC20x30.asm from r587 where GetPdcIDtoAX would not return with the IDE base port in DX so DisablePdcProgrammingMode would fail.
  • Made some changes to ModuleDependency.inc and other files so that MODULE_ADVANCED_ATA now requires USE_386. Consequently it is no longer included in the regular AT-builds, only in the 386_8k-build.
  • Moved the UNROLL_SECTORS_IN_CX_TO_xWORDS macros from IDE_8bit.inc to IdeIO.inc which means it's now possible to build a BIOS without MODULE_8BIT_IDE.
  • XTIDECFG: Added a minimum DOS version check (since it needs DOS version 2+) to allow the program to quit gracefully in the unlikely scenario where someone tries to run it under DOS version 1.
  • Made some changes to Drive.asm to improve drive enumeration. The old method using GET_DOS_DRIVE_PARAMETER_BLOCK_FOR_SPECIFIC_DRIVE worked well in Windows XP but not in Windows 98 SE (in Windows or in DOS mode). The two problems were; 1) The function call would access the drives which on single floppy drive systems would cause Windows to swap between A: and B: (throwing a blue screen asking the user to insert a disk etc). 2) Only floppy drives and FAT16 drives would be available in the list of drives, no FAT32/optical/network drives.
  • Improved code in IdeControllerMenu.asm so that the default port addresses for all IDE interfaces are now restored when (re-)selecting the (same) type of IDE device.
  • Also made it impossible to select a device type unless the required module is included in the loaded BIOS.
  • The version check done when loading a BIOS now uses the FLASH_SIGNATURE definition from Version.inc. Any changes affecting RomVars now only requires updating that definition. This means that changes to RomVars must be implemented in both the BIOS and XTIDECFG before being committed to the repository.
  • Added a compatibility fix for 3Com 3C503 cards to the ROM checksumming code in Buffers.asm (Buffers_GenerateChecksum).
  • SerDrive: Made some minor changes to file names and paths to improve compatibility with case sensitive environments.
  • BIOSDRVS: Made a minor size optimization which as a side effect also makes it compatible with all DOS versions including DOS version 1.
  • Library: Renamed the WAIT_RETRACE_IF_NECESSARY_THEN macro to CALL_WAIT_FOR_RETRACE_IF_NECESSARY_THEN and made a tail-call-optimized version of it (JMP_WAIT_FOR_RETRACE_IF_NECESSARY_THEN).
  • A speed optimization to the eRCL_IM macro for 386 and higher. This change breaks emulation in the sense that the macro will fail when given a memory operand as the first parameter.
  • Other minor optimizations and fixes.
File size: 10.4 KB
Line 
1//======================================================================
2//
3// Project: XTIDE Universal BIOS, Serial Port Server
4//
5// File: Process.cpp - Processes commands received over the serial port
6//
7
8//
9// XTIDE Universal BIOS and Associated Tools
10// Copyright (C) 2009-2010 by Tomi Tilli, 2011-2013 by XTIDE Universal BIOS Team.
11//
12// This program is free software; you can redistribute it and/or modify
13// it under the terms of the GNU General Public License as published by
14// the Free Software Foundation; either version 2 of the License, or
15// (at your option) any later version.
16//
17// This program is distributed in the hope that it will be useful,
18// but WITHOUT ANY WARRANTY; without even the implied warranty of
19// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20// GNU General Public License for more details.
21// Visit http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22//
23
24#include "Library.h"
25#include <memory.h>
26#include <string.h>
27#include <stdio.h>
28
29union _buff {
30 struct {
31 unsigned char command;
32 unsigned char driveAndHead;
33 unsigned char count;
34 unsigned char sector;
35 unsigned short cylinder;
36 } chs;
37 struct {
38 unsigned char command;
39 unsigned char bits24;
40 unsigned char count;
41 unsigned char bits00;
42 unsigned char bits08;
43 unsigned char bits16;
44 } lba;
45 struct {
46 unsigned char command;
47 unsigned char driveAndHead;
48 unsigned char count;
49 unsigned char scan;
50 unsigned char port;
51 unsigned char baud;
52 } inquire;
53 struct {
54 unsigned char command;
55 unsigned char driveAndHead;
56 unsigned char count;
57 unsigned char scan;
58 unsigned short PackedPortAndBaud;
59 } inquirePacked;
60 unsigned char b[514];
61 unsigned short w[257];
62} buff;
63
64#define SERIAL_COMMAND_HEADER 0xa0
65
66#define SERIAL_COMMAND_WRITE 1
67#define SERIAL_COMMAND_READWRITE 2
68#define SERIAL_COMMAND_RWMASK 3
69#define SERIAL_COMMAND_INQUIRE 0
70
71#define SERIAL_COMMAND_MASK 0xe3
72#define SERIAL_COMMAND_HEADERMASK 0xe0
73
74#define ATA_COMMAND_LBA 0x40
75#define ATA_COMMAND_HEADMASK 0xf
76
77#define ATA_DriveAndHead_Drive 0x10
78
79void logBuff( char *message, unsigned long buffoffset, unsigned long readto, int verboseLevel )
80{
81 char logBuff[ 514*9 + 10 ];
82 int logCount;
83
84 if( verboseLevel == 5 || (verboseLevel >= 3 && buffoffset == readto) )
85 {
86 if( verboseLevel == 3 && buffoffset > 11 )
87 logCount = 11;
88 else
89 logCount = buffoffset;
90
91 for( int t = 0; t < logCount; t++ )
92 sprintf( &logBuff[t*9], "[%3d:%02x] ", t, buff.b[t] );
93 if( logCount != buffoffset )
94 sprintf( &logBuff[logCount*9], "... " );
95
96 log( 3, "%s%s", message, logBuff );
97 }
98}
99
100void processRequests( SerialAccess *serial, Image *image0, Image *image1, int timeoutEnabled, int verboseLevel )
101{
102 unsigned char workCommand;
103 int workOffset, workCount;
104
105 unsigned long mylba;
106 unsigned long readto;
107 unsigned long buffoffset;
108 unsigned long lasttick;
109 unsigned short crc;
110 unsigned long GetTime_Timeout_Local;
111 unsigned long len;
112 Image *img;
113 unsigned long cyl, sect, head;
114 unsigned long perfTimer;
115 unsigned char lastScan;
116
117 GetTime_Timeout_Local = GetTime_Timeout();
118
119 buffoffset = 0;
120 readto = 0;
121 workCount = workOffset = workCommand = 0;
122 lastScan = 0;
123
124 //
125 // Floppy disks must come after any hard disks
126 //
127 if( (image0 && image0->floppy) && (image1 && !image1->floppy) )
128 {
129 img = image0;
130 image0 = image1;
131 image1 = img;
132 }
133
134 lasttick = GetTime();
135
136 while( (len = serial->readCharacters( &buff.b[buffoffset], (readto ? readto-buffoffset : 1) )) )
137 {
138 buffoffset += len;
139
140 //
141 // For debugging, look at the incoming packet
142 //
143 if( verboseLevel >= 3 )
144 logBuff( " Received: ", buffoffset, readto, verboseLevel );
145
146 if( timeoutEnabled && readto && GetTime() > lasttick + GetTime_Timeout_Local )
147 {
148 log( 1, "Timeout waiting on data from client, aborting previous command" );
149
150 workCount = workOffset = workCommand = 0;
151 readto = 0;
152
153 if( len <= 8 && (buff.b[buffoffset-len] & SERIAL_COMMAND_HEADERMASK) == SERIAL_COMMAND_HEADER )
154 {
155 // assume that we are at the front of a new command
156 //
157 memcpy( &buff.b[0], &buff.b[buffoffset-len], len );
158 buffoffset = len;
159 readto = 8;
160 // fall through to normal processing
161 }
162 else if( len == 1 )
163 {
164 // one new character, treat it like any other new character received, discarding the buffer
165 //
166 buff.b[0] = buff.b[buffoffset-1];
167 buffoffset = 1;
168 // fall through to normal processing
169 }
170 else
171 {
172 // discard even the newly received data and start listening anew
173 //
174 buffoffset = 0;
175 continue;
176 }
177 }
178
179 lasttick = GetTime();
180
181 //
182 // No work currently to do, look at each character as they come in...
183 //
184 if( !readto )
185 {
186 if( (buff.b[0] & SERIAL_COMMAND_HEADERMASK) == SERIAL_COMMAND_HEADER )
187 {
188 //
189 // Found our command header byte to start a commnad sequence, read the next 7 and evaluate
190 //
191 readto = 8;
192 continue;
193 }
194 else
195 {
196 //
197 // Spurious characters, discard
198 //
199 if( verboseLevel >= 2 )
200 {
201 if( buff.b[0] >= 0x20 && buff.b[0] <= 0x7e )
202 log( 2, "Spurious: [%d:%c]", buff.b[0], buff.b[0] );
203 else
204 log( 2, "Spurious: [%d]", buff.b[0] );
205 }
206 buffoffset = 0;
207 continue;
208 }
209 }
210
211 //
212 // Partial packet received, keep reading...
213 //
214 if( readto && buffoffset < readto )
215 continue;
216
217 //
218 // Read 512 bytes from serial port, only one command reads that many characters: Write Sector
219 //
220 if( buffoffset == readto && readto == 514 )
221 {
222 buffoffset = readto = 0;
223 if( (crc = checksum( &buff.w[0], 256 )) != buff.w[256] )
224 {
225 log( 0, "Bad Write Sector Checksum" );
226 continue;
227 }
228
229 if( img->readOnly )
230 {
231 log( 1, "Attempt to write to read-only image" );
232 continue;
233 }
234
235 img->seekSector( mylba + workOffset );
236 img->writeSector( &buff.w[0] );
237
238 //
239 // Echo back the CRC
240 //
241 if( !serial->writeCharacters( &buff.w[256], 2 ) )
242 break;
243
244 workOffset++;
245 workCount--;
246
247 if( workCount )
248 readto = 1; // looking for continuation ACK
249 }
250
251 //
252 // 8 byte command received, or a continuation of the previous command
253 //
254 else if( (buffoffset == readto && readto == 8) ||
255 (buffoffset == readto && readto == 1 && workCount) )
256 {
257 buffoffset = readto = 0;
258 if( workCount )
259 {
260 if( verboseLevel > 1 )
261 log( 2, " Continuation: Offset=%u, Checksum=%04x", workOffset-1, buff.w[256] );
262
263 //
264 // Continuation...
265 //
266 if( buff.b[0] != (workCount-0) )
267 {
268 log( 0, "Continue Fault: Received=%d, Expected=%d", buff.b[0], workCount );
269 workCount = 0;
270 continue;
271 }
272 }
273 else
274 {
275 //
276 // New Command...
277 //
278 if( (crc = checksum( &buff.w[0], 3 )) != buff.w[3] )
279 {
280 log( 0, "Bad Command Checksum: %02x %02x %02x %02x %02x %02x %02x %02x, Checksum=%04x",
281 buff.b[0], buff.b[1], buff.b[2], buff.b[3], buff.b[4], buff.b[5], buff.b[6], buff.b[7], crc);
282 continue;
283 }
284
285 img = (buff.inquire.driveAndHead & ATA_DriveAndHead_Drive) ? image1 : image0;
286
287 workCommand = buff.chs.command & SERIAL_COMMAND_RWMASK;
288
289 if( (workCommand != SERIAL_COMMAND_INQUIRE) && (buff.chs.driveAndHead & ATA_COMMAND_LBA) )
290 {
291 mylba = ((((unsigned long) buff.lba.bits24) & ATA_COMMAND_HEADMASK) << 24)
292 | (((unsigned long) buff.lba.bits16) << 16)
293 | (((unsigned long) buff.lba.bits08) << 8)
294 | ((unsigned long) buff.lba.bits00);
295 }
296 else
297 {
298 cyl = buff.chs.cylinder;
299 sect = buff.chs.sector;
300 head = (buff.chs.driveAndHead & ATA_COMMAND_HEADMASK);
301 mylba = img ? (((cyl*img->head + head)*img->sect) + sect-1) : 0;
302 }
303
304 workOffset = 0;
305 workCount = buff.chs.count;
306
307 if( verboseLevel > 0 )
308 {
309 char *comStr = (workCommand & SERIAL_COMMAND_WRITE ? "Write" : "Read");
310
311 if( workCommand == SERIAL_COMMAND_INQUIRE )
312 log( 1, "Inquire %d: Client Port=0x%x, Client Baud=%s", img == image0 ? 0 : 1,
313 ((unsigned short) buff.inquire.port) << 2,
314 baudRateMatchDivisor( buff.inquire.baud )->display );
315 else if( buff.chs.driveAndHead & ATA_COMMAND_LBA )
316 log( 1, "%s %d: LBA=%u, Count=%u", comStr, img == image0 ? 0 : 1,
317 mylba, workCount );
318 else
319 log( 1, "%s %d: Cylinder=%u, Sector=%u, Head=%u, Count=%u, LBA=%u", comStr, img == image0 ? 0 : 1,
320 cyl, sect, head, workCount, mylba );
321 }
322
323 if( !img )
324 {
325 log( 1, " No slave drive provided" );
326 workCount = 0;
327 continue;
328 }
329
330 if( (workCommand & SERIAL_COMMAND_WRITE) && img->readOnly )
331 {
332 log( 1, " Write attempt to Read Only disk" );
333 workCount = 0;
334 continue;
335 }
336
337 if( verboseLevel > 0 && workCount > 100 )
338 perfTimer = GetTime();
339 }
340
341 if( workCount && (workCommand == (SERIAL_COMMAND_WRITE | SERIAL_COMMAND_READWRITE)) )
342 {
343 //
344 // Write command... Setup to receive a sector
345 //
346 readto = 514;
347 }
348 else
349 {
350 //
351 // Inquire command...
352 //
353 if( workCommand == SERIAL_COMMAND_INQUIRE )
354 {
355 unsigned char localScan;
356
357 if( serial->speedEmulation &&
358 buff.inquire.baud != serial->baudRate->divisor )
359 {
360 log( 1, " Ignoring Inquire with wrong baud rate" );
361 workCount = 0;
362 continue;
363 }
364
365 localScan = buff.inquire.scan; // need to do this before the call to
366 // img->respondInquire, as it will clear the buff
367 img->respondInquire( &buff.w[0], buff.inquirePacked.PackedPortAndBaud,
368 serial->baudRate,
369 ((unsigned short) buff.inquire.port) << 2,
370 (img == image1 && lastScan) || buff.inquire.scan );
371 lastScan = localScan;
372 }
373 //
374 // Read command...
375 //
376 else
377 {
378 img->seekSector( mylba + workOffset );
379 img->readSector( &buff.w[0] );
380 lastScan = 0;
381 }
382
383 buff.w[256] = checksum( &buff.w[0], 256 );
384
385 if( !serial->writeCharacters( &buff.w[0], 514 ) )
386 break;
387
388 if( verboseLevel >= 3 )
389 logBuff( " Sending: ", 514, 514, verboseLevel );
390
391 workCount--;
392 workOffset++;
393
394 if( workCount )
395 readto = 1; // looking for continuation ACK
396 }
397 }
398
399 if( workCount == 0 && workOffset > 100 )
400 log( 1, " Performance: %.2lf bytes per second", (512.0 * workOffset) / (GetTime() - perfTimer) * 1000.0 );
401 }
402}
403
404
Note: See TracBrowser for help on using the repository browser.