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

Last change on this file since 233 was 233, checked in by gregli@…, 12 years ago

Serial Port: split single byte port and baud into two bytes, taking advantage of the two bytes in DPT_SERIAL, which supports more serial baud rates and in particular fixed a bug where a 4x client machine couldn't talk to a 115.2K server machine. This is a wide change, touching lots of files, but most are shallow changes. DetectPrint.asm took the most significant changes, now it calculates the baud rate to display instead of using characters provided by the Configurator. The Configurator now has a new menu flag, FLG_MENUITEM_CHOICESTRINGS, for specifying that values are not linear and they should be lookedup rather than indexed. Finally, another important bug fixed here is that in some error cases, the serial port code could get into an infinite loop waiting ont the hardware; now it has a timeout.

File size: 9.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#include "library.h"
9#include <memory.h>
10#include <string.h>
11#include <stdio.h>
12
13union _buff {
14    struct {
15        unsigned char command;
16        unsigned char driveAndHead;
17        unsigned char count;
18        unsigned char sector;
19        unsigned short cylinder;
20    } chs;
21    struct {
22        unsigned char command;
23        unsigned char bits24;
24        unsigned char count;
25        unsigned char bits00;
26        unsigned char bits08;
27        unsigned char bits16;
28    } lba;
29    struct {
30        unsigned char command;
31        unsigned char driveAndHead;
32        unsigned char count;
33        unsigned char scan;
34        unsigned char port;
35        unsigned char baud;
36    } inquire;
37    unsigned char b[514];
38    unsigned short w[257];
39} buff;
40
41#define SERIAL_COMMAND_HEADER 0xa0
42
43#define SERIAL_COMMAND_WRITE 1
44#define SERIAL_COMMAND_READWRITE 2
45#define SERIAL_COMMAND_RWMASK 3
46#define SERIAL_COMMAND_INQUIRE 0
47
48#define SERIAL_COMMAND_MASK 0xe3
49#define SERIAL_COMMAND_HEADERMASK 0xe0
50
51#define ATA_COMMAND_LBA 0x40
52#define ATA_COMMAND_HEADMASK 0xf
53
54#define ATA_DriveAndHead_Drive 0x10
55
56void logBuff( char *message, unsigned long buffoffset, unsigned long readto, int verboseLevel )
57{
58    char logBuff[ 514*9 + 10 ];
59    int logCount;
60
61    if( verboseLevel == 5 || (verboseLevel >= 3 && buffoffset == readto) )
62    {
63        if( verboseLevel == 3 && buffoffset > 11 )
64            logCount = 11;
65        else
66            logCount = buffoffset;
67
68        for( int t = 0; t < logCount; t++ )
69            sprintf( &logBuff[t*9], "[%3d:%02x] ", t, buff.b[t] );
70        if( logCount != buffoffset )
71            sprintf( &logBuff[logCount*9], "... " );
72
73        log( 3, "%s%s", message, logBuff );
74    }
75}
76
77void processRequests( SerialAccess *serial, Image *image0, Image *image1, int timeoutEnabled, int verboseLevel )
78{
79    unsigned char workCommand;
80    int workOffset, workCount;
81
82    unsigned long mylba;
83    unsigned long readto;
84    unsigned long buffoffset;
85    unsigned long lasttick;
86    unsigned short crc;
87    unsigned long GetTime_Timeout_Local;
88    unsigned long len;
89    Image *img;
90    unsigned long cyl, sect, head;
91    unsigned long perfTimer;
92    unsigned char lastScan;
93
94    GetTime_Timeout_Local = GetTime_Timeout();
95
96    buffoffset = 0;
97    readto = 0;
98    workCount = workOffset = workCommand = 0;
99    lastScan = 0;
100
101    lasttick = GetTime();
102
103    while( (len = serial->readCharacters( &buff.b[buffoffset], (readto ? readto-buffoffset : 1) )) )
104    {
105        buffoffset += len;
106
107        //
108        // For debugging, look at the incoming packet
109        //
110        if( verboseLevel >= 3 )
111            logBuff( "    Received: ", buffoffset, readto, verboseLevel );
112
113        if( timeoutEnabled && readto && GetTime() > lasttick + GetTime_Timeout_Local )
114        {
115            log( 1, "Timeout waiting on data from client, aborting previous command" );
116
117            workCount = workOffset = workCommand = 0;
118            readto = 0;
119
120            if( len <= 8 && (buff.b[buffoffset-len] & SERIAL_COMMAND_HEADERMASK) == SERIAL_COMMAND_HEADER )
121            {
122                // assume that we are at the front of a new command
123                //
124                memcpy( &buff.b[0], &buff.b[buffoffset-len], len );
125                buffoffset = len;
126                readto = 8;
127                // fall through to normal processing
128            }
129            else if( len == 1 )
130            {
131                // one new character, treat it like any other new character received, discarding the buffer
132                //
133                buff.b[0] = buff.b[buffoffset-1];
134                buffoffset = 1;
135                // fall through to normal processing
136            }
137            else
138            {
139                // discard even the newly received data and start listening anew
140                //
141                buffoffset = 0;
142                continue;
143            }
144        }
145
146        lasttick = GetTime();
147
148        //
149        // No work currently to do, look at each character as they come in...
150        //
151        if( !readto )
152        {
153            if( (buff.b[0] & SERIAL_COMMAND_HEADERMASK) == SERIAL_COMMAND_HEADER )
154            {
155                //
156                // Found our command header byte to start a commnad sequence, read the next 7 and evaluate
157                //
158                readto = 8;
159                continue;
160            }
161            else
162            {
163                //
164                // Spurious characters, discard
165                //
166                if( verboseLevel >= 2 )
167                {
168                    if( buff.b[0] >= 0x20 && buff.b[0] <= 0x7e )
169                        log( 2, "Spurious: [%d:%c]", buff.b[0], buff.b[0] );
170                    else
171                        log( 2, "Spurious: [%d]", buff.b[0] );
172                }
173                buffoffset = 0;
174                continue;
175            }
176        }
177
178        //
179        // Partial packet received, keep reading...
180        //
181        if( readto && buffoffset < readto )
182            continue;
183
184        //
185        // Read 512 bytes from serial port, only one command reads that many characters: Write Sector
186        //
187        if( buffoffset == readto && readto == 514 )
188        {
189            buffoffset = readto = 0;
190            if( (crc = checksum( &buff.w[0], 256 )) != buff.w[256] )
191            {
192                log( 0, "Bad Write Sector Checksum" );
193                continue;
194            }
195
196            if( img->readOnly )
197            {
198                log( 1, "Attempt to write to read-only image" );
199                continue;
200            }
201
202            img->seekSector( mylba + workOffset );
203            img->writeSector( &buff.w[0] );
204
205            //
206            // Echo back the CRC
207            //
208            if( !serial->writeCharacters( &buff.w[256], 2 ) )
209                break;
210
211            workOffset++;
212            workCount--;
213
214            if( workCount )
215                readto = 1;           // looking for continuation ACK
216        }
217
218        //
219        // 8 byte command received, or a continuation of the previous command
220        //
221        else if( (buffoffset == readto && readto == 8) ||
222                 (buffoffset == readto && readto == 1 && workCount) )
223        {
224            buffoffset = readto = 0;
225            if( workCount )
226            {
227                if( verboseLevel > 1 )
228                    log( 2, "    Continuation: Offset=%u, Checksum=%04x", workOffset-1, buff.w[256] );
229
230                //
231                // Continuation...
232                //
233                if( buff.b[0] != (workCount-0) )
234                {
235                    log( 0, "Continue Fault: Received=%d, Expected=%d", buff.b[0], workCount );
236                    workCount = 0;
237                    continue;
238                }
239            }
240            else
241            {
242                //
243                // New Command...
244                //
245                if( (crc = checksum( &buff.w[0], 3 )) != buff.w[3] )
246                {
247                    log( 0, "Bad Command Checksum: %02x %02x %02x %02x %02x %02x %02x %02x, Checksum=%04x",
248                         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);
249                    continue;
250                }
251
252                img = (buff.inquire.driveAndHead & ATA_DriveAndHead_Drive) ? image1 : image0;
253
254                workCommand = buff.chs.command & SERIAL_COMMAND_RWMASK;
255
256                if( (workCommand != SERIAL_COMMAND_INQUIRE) && (buff.chs.driveAndHead & ATA_COMMAND_LBA) )
257                {
258                    mylba = ((((unsigned long) buff.lba.bits24) & ATA_COMMAND_HEADMASK) << 24) 
259                        | (((unsigned long) buff.lba.bits16) << 16) 
260                        | (((unsigned long) buff.lba.bits08) << 8) 
261                        | ((unsigned long) buff.lba.bits00);
262                }
263                else
264                {
265                    cyl = buff.chs.cylinder;
266                    sect = buff.chs.sector;
267                    head = (buff.chs.driveAndHead & ATA_COMMAND_HEADMASK);
268                    mylba = img ? (((cyl*img->head + head)*img->sect) + sect-1) : 0;
269                }
270
271                workOffset = 0;
272                workCount = buff.chs.count;
273
274                if( verboseLevel > 0 )
275                {
276                    char *comStr = (workCommand & SERIAL_COMMAND_WRITE ? "Write" : "Read");
277
278                    if( workCommand == SERIAL_COMMAND_INQUIRE )
279                        log( 1, "Inquire %d: Client Port=0x%x, Client Baud=%s", img == image0 ? 0 : 1,
280                             ((unsigned short) buff.inquire.port) << 2,
281                             baudRateMatchDivisor( buff.inquire.baud )->display );
282                    else if( buff.chs.driveAndHead & ATA_COMMAND_LBA )
283                        log( 1, "%s %d: LBA=%u, Count=%u", comStr, img == image0 ? 0 : 1,
284                             mylba, workCount );
285                    else
286                        log( 1, "%s %d: Cylinder=%u, Sector=%u, Head=%u, Count=%u, LBA=%u", comStr, img == image0 ? 0 : 1,
287                             cyl, sect, head, workCount, mylba );
288                }
289
290                if( !img )
291                {
292                    log( 1, "    No slave drive provided" );
293                    workCount = 0;
294                    continue;
295                }
296
297                if( (workCommand & SERIAL_COMMAND_WRITE) && img->readOnly )
298                {
299                    log( 1, "    Write attempt to Read Only disk" );
300                    workCount = 0;
301                    continue;
302                }
303
304                if( verboseLevel > 0 && workCount > 100 )
305                    perfTimer = GetTime();
306            }
307
308            if( workCount && (workCommand == (SERIAL_COMMAND_WRITE | SERIAL_COMMAND_READWRITE)) )
309            {
310                //
311                // Write command...   Setup to receive a sector
312                //
313                readto = 514;
314            }
315            else 
316            {
317                //
318                // Inquire command...
319                //
320                if( workCommand == SERIAL_COMMAND_INQUIRE )
321                {
322                    unsigned char localScan;
323
324                    if( serial->speedEmulation && 
325                        buff.inquire.baud != serial->baudRate->divisor )
326                    {
327                        log( 1, "    Ignoring Inquire with wrong baud rate" );
328                        workCount = 0;
329                        continue;
330                    }
331
332                    localScan = buff.inquire.scan;         // need to do this before the call to
333                                                           // img->respondInquire, as it will clear the buff
334                    img->respondInquire( &buff.w[0], serial->baudRate, 
335                                         ((unsigned short) buff.inquire.port) << 2, 
336                                         (img == image1 && lastScan) || buff.inquire.scan );
337                    lastScan = localScan;
338                }
339                //
340                // Read command...
341                //
342                else
343                {
344                    img->seekSector( mylba + workOffset );
345                    img->readSector( &buff.w[0] );
346                    lastScan = 0;
347                }
348
349                buff.w[256] = checksum( &buff.w[0], 256 );
350
351                if( !serial->writeCharacters( &buff.w[0], 514 ) )
352                    break;
353
354                if( verboseLevel >= 3 )
355                    logBuff( "    Sending: ", 514, 514, verboseLevel );
356
357                workCount--;
358                workOffset++;
359
360                if( workCount )
361                    readto = 1;           // looking for continuation ACK
362            }
363        }
364
365        if( workCount == 0 && workOffset > 100 )
366            log( 1, "    Performance: %.2lf bytes per second", (512.0 * workOffset) / (GetTime() - perfTimer) * 1000.0 );
367    }
368}
369
370
Note: See TracBrowser for help on using the repository browser.