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

Last change on this file since 589 was 589, checked in by krille_n_, 8 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.