summaryrefslogtreecommitdiffstats
path: root/private/ntos/fw/mips/sonictst.c
blob: 30741fc32489dbd83430e25e29db78ef7b92aa1e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

    sonictst.c

Abstract:

    This module implements the SONIC ethernet test for the selftest.
    The test consist in transmiting packets in loopback mode.

Author:

    Lluis Abello (lluis) 19-Feb-1991

Environment:


Revision History:

--*/

#include "fwp.h"
#include "sonictst.h"
#include "iodevice.h"
#include "ioaccess.h"
#include "fwstring.h"
#ifdef DUO
#include "duoint.h"
#endif

extern volatile ULONG TimerTicks;
extern UCHAR * TranslationTable;

VOID
MapDma(
    ULONG   VirtualAddress,
    ULONG   LogicalAddress,
    ULONG   Pages
    )
/*++

Routine description:

        This routine performs the I/O address maping by setting the
        translation table.

        Physical and Logical Addresses must be page aligned.

Arguments:

        VirtualAddress  -   Specifies the R4000 VirtualAddress.

                            Logical Address will be mapped to the physical
                            address that this Virtual Address Maps.

        LogicalAdress   -   Address to map

        Pages           -   Number of pages to map

Return value:

        None.
--*/
{
PTRANSLATION_ENTRY TranslationTable;
ULONG   PageFrameNumber,LogicalPage,PhysicalAddress;
ULONG   i;
    PhysicalAddress=VirtualAddress&0x0FFFFFFF;  // Extract Physical Address from KSEG0-1
    TranslationTable= (PTRANSLATION_ENTRY)((READ_REGISTER_ULONG(&DMA_CONTROL->TranslationBase.Long)) | 0xA0000000);

                                                // Initialize pointer to base
                                                // of Table to write the new entries.
                                                // Make physical address virtual
                                                // Non cached because we want it
                                                // to be written to memory
    LogicalPage= LogicalAddress>>12;
    PageFrameNumber=PhysicalAddress&0x03FFF000;
    for (i=0;i < Pages;i++) {
        TranslationTable[LogicalPage+i].PageFrame=PageFrameNumber;
        PageFrameNumber+=0x1000;                // next page starts after 4Kb more
        TranslationTable[LogicalPage+i].Fill=0;
    }
    WRITE_REGISTER_ULONG(&DMA_CONTROL->TranslationLimit.Long,(LogicalPage+i) << 3);
}
VOID
AllocateReceiveDescriptors(
    )
/*++

Routine Description:

    This routine allocates and initializes a chain of 3 Receive Descriptors.
    The Receive Descriptors are linked in a circular queue so that the
    pointers don't need to be changed any more. The last Receive descriptor
    points to the first but it has the EOL flag set so it's the last on the
    queue and therefore sonic will not use the following descriptor (first one)
    until we free it. Once we process a received packet, the EOL flag must be
    rotated to the next descriptor to make a new descriptor available.

    In order to be able to use the link field from both physical (system soft)
    and logical (sonic) address spaces the receive descriptors must have
    the same alignment for both physical and logical addresses.

Arguments:

    None.

Return Value:

    None.

--*/
{
ULONG Link,i;
ULONG LogicalReceiveDscr;
    ReceiveDscrQueue.Base = (PRECEIVE_DESCRIPTOR) PHYS_RECEIVE_DSCR_ADDRESS;
    LogicalReceiveDscr = LOGICAL_RECEIVE_DSCR_ADDRESS;
    MapDma((ULONG)ReceiveDscrQueue.Base,    // R4000 Address
           LogicalReceiveDscr,              // Logical Address
           1                                // 1 page
           );
    //get 16 lower bits of address or offset
    ReceiveDscrQueue.Current=Link=(ULONG)ReceiveDscrQueue.Base & 0xFFFF;
    //
    // Link first descriptor to the second one.
    //
    for (i=0;i<2;i++) {
        Link += sizeof(RECEIVE_DESCRIPTOR);
        (ReceiveDscrQueue.Base[i]).Link.Data=Link;       // Link to next RD
        (ReceiveDscrQueue.Base[i]).InUse.Data=AVAILABLE; // Make it avilable
    }
    //
    // Link last descriptor to the first but mark it as EOL
    //
    (ReceiveDscrQueue.Base[2]).Link.Data=ReceiveDscrQueue.Current | EOL;
    //
    // Make it avilable
    //
    (ReceiveDscrQueue.Base[2]).InUse.Data=AVAILABLE;
    ReceiveDscrQueue.Last=Link;     // Keep track of the last one.
    // we need only the upper bits to access a descriptor as Base | First
    ReceiveDscrQueue.Base = (PRECEIVE_DESCRIPTOR)((ULONG) ReceiveDscrQueue.Base & 0xFFFF0000);
    //
    // Initialize sonic Receive descriptor pointers with the logical address
    // of the descriptors.
    //
    WRITE_REGISTER_USHORT(&SONIC->URDA.Reg,(USHORT) (LogicalReceiveDscr >> 16));
    WRITE_REGISTER_USHORT(&SONIC->CRDA.Reg,ReceiveDscrQueue.Current);
}
VOID
AllocateReceiveResources(
    )
/*++

Routine Description:

    This routine allocates and initializes the Receive Resource area.
    Two resources are allocated.
    The pointer of each entry points to the Receive Buffers which are
    also allocated by this routine. And the size of each buffer is also
    set in the Recieve Resource word count entry.

    Receive Buffers allocated are RBA_SIZE bytes long.

    It also allocate room for the CAM descriptors. The CAM descriptor pointer
    is pointed by CamDescriptor and resides inside the URRA segment.

    The mapping Logical <-> Physical spaces is also done.

Arguments:

    None.

Return Value:

    None.

--*/
{
ULONG i;
ULONG ReceivePhysBuffer;    // Temporay pointer to a receive buffer area.
ULONG ReceiveLogBuffer;     // Temporary pointer to a logical address.
//
//Allocate memory for the receive descriptors + CAM Addresses + CAM enable.
//
    ReceivePhysRsrc = (PRECEIVE_RESOURCE) RECEIVE_PHYS_RSRC_ADDRESS;
    ReceiveLogRsrc = (PRECEIVE_RESOURCE) RECEIVE_LOG_RSRC_ADDRESS;
    MapDma((ULONG)ReceivePhysRsrc,
           (ULONG)ReceiveLogRsrc,
           1
           );
//
// Allocate Receive buffers in physical and logical spaces and map them.
//
    ReceivePhysBuffer= (ULONG) RECEIVE_PHYS_BUFFER_ADDRESS;
    ReceiveLogBuffer=  (ULONG) RECEIVE_LOG_BUFFER_ADDRESS;
    MapDma(ReceivePhysBuffer,ReceiveLogBuffer,2);
//
// The Receive Buffers are contiguos in memory. Sonic will write
// the logical address of the packets  received. To translate this
// logical address to a physical one what we do is to keep the
// offset between these physical and logical addresses and then
// we just need to add this offset to the logical address to
// convert it to physical.
//
    ReceiveBufferTranslationOffset =ReceivePhysBuffer-ReceiveLogBuffer;
//
// for each receive resource, Write the logical
// address of the receive buffer in the physical Receive resource.
//
    for (i=0; i < 3; i++) {
        ReceivePhysRsrc[i].BufferPtr0.Data=ReceiveLogBuffer & 0xFFFF; //16 lower bits
        ReceivePhysRsrc[i].BufferPtr1.Data=ReceiveLogBuffer >> 16;    //16 upper bits
        ReceivePhysRsrc[i].WordCount0.Data=((RBA_SIZE >>1) & 0xFFFF); //16 lower bits
        ReceivePhysRsrc[i].WordCount1.Data=(RBA_SIZE >> 17);          //16 upper bits
        ReceiveLogBuffer += RBA_SIZE;
    }
//
// Initialize the CamDescriptor to point to the end of the RRA
//
    PhysCamDescriptor=(PCAM_DESCRIPTOR)(&ReceivePhysRsrc[3]);
    LogCamDescriptor=(PCAM_DESCRIPTOR)(&ReceiveLogRsrc[3]);
//
// Initialize sonic Resource Area pointers with the logical address mapped
// to the physical Area.
//
    WRITE_REGISTER_USHORT(&SONIC->URRA.Reg,(USHORT) ((ULONG)ReceiveLogRsrc >> 16));
    WRITE_REGISTER_USHORT(&SONIC->RSA.Reg,(ULONG)ReceiveLogRsrc & 0xFFFF);
    WRITE_REGISTER_USHORT(&SONIC->REA.Reg,(ULONG)(&ReceiveLogRsrc[3]) & 0xFFFF);
    WRITE_REGISTER_USHORT(&SONIC->RRP.Reg,(ULONG)ReceiveLogRsrc & 0xFFFF);
    WRITE_REGISTER_USHORT(&SONIC->RWP.Reg,(ULONG)(&ReceiveLogRsrc[2]) & 0xFFFF);

//
// Set the lower boundary of the RBA to the maximum packet size.
//
    WRITE_REGISTER_USHORT(&SONIC->EOBC.Reg,MAX_PACKET_SIZE >> 1);
//
// Set The receive control register.
//
    WRITE_REGISTER_USHORT(&SONIC->ReceiveControl.Reg,RCR_ENDEC | RCR_RNT);
}
VOID
SetCamDescriptor(
    )
/*++

Routine Description:

    This routine Initializes the CAM descriptor area allocated by
    "AllocateReceiveResources" and being pointed by PhysCamDescriptor.

    The Address loaded in the CAM is the one fetched from the NVRAM.

    It leaves everything ready to issue the Load CAM command.

Arguments:

    None.

Return Value:

    None.

--*/
{
ULONG i;
//
// Initialize CAM descriptor area.
//
    PhysCamDescriptor[0].EntryPointer.Data=0;
    PhysCamDescriptor[0].Port0.Data=(StationAddress[1] << 8) |
                                    (StationAddress[0]);
    PhysCamDescriptor[0].Port1.Data=(StationAddress[3] << 8) |
                                    (StationAddress[2]);
    PhysCamDescriptor[0].Port2.Data=(StationAddress[5] << 8) |
                                    (StationAddress[4]);
// Set CAM Enable.
    PhysCamDescriptor[1].EntryPointer.Data=1;   // enable entry zero.

    WRITE_REGISTER_USHORT(&SONIC->CamDscrCount.Reg,1);  // only one entry.
// Lower 16 bits offset from URRA
    WRITE_REGISTER_USHORT(&SONIC->CamDscrPtr.Reg,(ULONG)LogCamDescriptor & 0xFFFF);
}
VOID
AllocateTransmitDescriptors(
    )
/*++

Routine Description:

    This routine allocates and initializes a pool of Transmit Descriptors.
    The Transmit Descriptors are set to be used one for each packet as they
    have only room for one fragment.

    The Descriptor fragment pointers are initialized to point to the
    Transmit Buffer Area which is also allocated.
    There is enough room allocated for each buffer for the bigest
    ethernet packet.

    To keep the physical addresses of the TBA we use the upper 16 bits
    of the SONIC_ENTRY this is the field called 'Fill'.
    This way we don't ahve any restriction in these pointers but we
    have to deal with both, physical and logical.


Arguments:

    None.

Return Value:

    None.

--*/
{
ULONG i;
ULONG PhysTbaPtr,LogTbaPtr;           // Transmit Buffer Area pointers
//
// Allocate memory for transmit descriptors and map it
//
    PhysTransmitDscr    = (PTRANSMIT_DESCRIPTOR) PHYS_TRANSMIT_DSCR_ADDRESS;
    LogicalTransmitDscr = (PTRANSMIT_DESCRIPTOR) LOGICAL_TRANSMIT_DSCR_ADDRESS;
    MapDma((ULONG) PhysTransmitDscr,
           (ULONG) LogicalTransmitDscr,
           1
           );
//
// Allocate memory for Transmit Buffer are and map it.
//
    PhysTbaPtr = PHYS_TBA_ADDRESS;
    LogTbaPtr =  LOG_TBA_ADDRESS;
    MapDma(PhysTbaPtr,LogTbaPtr,1);

// Initialize fragment count to 1 (packets won't be scatered)

    PhysTransmitDscr->FragCount.Data = 1;
//
// Initialize Logical pointers to TBA
//
    PhysTransmitDscr->FragPtr0.Data = LogTbaPtr & 0xFFFF;// lower 16 bits
    PhysTransmitDscr->FragPtr1.Data = LogTbaPtr >> 16;   // upper 16 bits
//
// Initialize Physical pointer to TBA
//
    PhysTransmitDscr->FragPtr0.Fill = PhysTbaPtr & 0xFFFF;// lower 16 bits
    PhysTransmitDscr->FragPtr1.Fill = PhysTbaPtr >> 16;   // upper 16 bits
//
// Unlink the packets, we will transmit one at a time.
//
    PhysTransmitDscr->Link.Data      =  EOL;
    PhysTransmitDscr->Config.Data = TCR_POWC;

//
// Initialize UTDA register. This can be done here because the base address
// of the Transmit Descriptor Area will not change.
// CTDA must be set after a new packet is ready to be sent.
//
    WRITE_REGISTER_USHORT(&SONIC->UTDA.Reg,(USHORT)((ULONG)LogicalTransmitDscr>>16));
}
VOID
ComposeMessage(
    ULONG   Size,
    UCHAR   FirstValue
    )
/*++

Routine Description:

    This routine composes a message of the specified size.
    It places the message in the buffer especified by the Transmit
    Descriptor, and initializes the descriptor.

Arguments:

    Size   -     Size of the message in bytes, must be <= MAX_PACKET_SIZE
    FirstValue - value of first Data byte in the packet.

Return Value:

    None.

--*/
{
register ULONG i,j=0;
PUCHAR MsgPtr;              // Temporary pointer to the message area.
//
// Load pointer to packet
//
    MsgPtr= (PUCHAR) ((PhysTransmitDscr->FragPtr1.Fill << 16) | (PhysTransmitDscr->FragPtr0.Fill));


    for (i=0; i<6; i++) {
        MsgPtr[j]=StationAddress[i];    // copy Destination address to packet
        MsgPtr[j+6]=StationAddress[i];  // copy Source address to packet
        j++;
    }
    j+=6;
//
// Set size of message
//
    MsgPtr[j++] = Size >> 8;    // upper 8 bits
    MsgPtr[j++] = Size & 0xFF;  // lower 8 bits
//
// Compose message
//
    for (i=0; i < Size; i++) {
        MsgPtr[j++] = FirstValue++;
    }
//
// Initialize other Transmit descriptor fields.
//
    PhysTransmitDscr->PktSize.Data = Size+6+6+2;
    PhysTransmitDscr->FragSize.Data = Size+6+6+2;
//
// Initialize TDA registers with logical address.
//
    WRITE_REGISTER_USHORT(&SONIC->CTDA.Reg,((ULONG) LogicalTransmitDscr) & 0xFFFF);
}
VOID
SonicCheckError(
    )
/*++

Routine Description:

    This routine checks and reports error conditions after an interrupt.

Arguments:

    None.

Return Value:

    None.

--*/
{
ULONG ErrorValue;
    SonicErrors++;
    if (SonicStatus.InterruptID & INT_BR) {     // Bus Retry.
        ErrorValue= READ_REGISTER_ULONG(&DMA_CONTROL->Errortype.Long);
//
// Clear error by writing back the contents of the register.
//
        WRITE_REGISTER_ULONG(&DMA_CONTROL->Errortype.Long,ErrorValue);
//
// Read error registers to clear them
//
        ErrorValue= READ_REGISTER_ULONG(&DMA_CONTROL->MemoryFailedAddress.Long);
        ErrorValue= READ_REGISTER_ULONG(&DMA_CONTROL->RemoteFailedAddress.Long);
#ifndef DUO
        ErrorValue= READ_REGISTER_ULONG(&DMA_CONTROL->ParityDiagnosticLow.Long);
#else
        ErrorValue= READ_REGISTER_ULONG(&DMA_CONTROL->EccDiagnostic);
#endif

    }
}
BOOLEAN
WaitForSonicInterrupt(
    )
/*++

Routine Description:

    This routine waits for a sonic interrupt by polling the Semaphore
    It sets the TimerTicks variable to 20 and if it becomes zero
    (at least 20 millisecond has passed) it time-out.

Arguments:

    None

Return Value:

    FALSE if the interrupt ocurred.
    TRUE  otherwise.

--*/
{
ULONG i;
    TimerTicks=20;
    while (TimerTicks) {                // check for timeout
        if (SonicIntSemaphore==0) {     // if interrupt has ocurred
            return FALSE;               // return to process
        }
    }
    return TRUE;                        // return if timeout.
}
VOID
InitSonic(
    )
/*++

Routine Description:

    This routine initializes the SONIC chip.

Arguments:

    None

Return Value:

    None

--*/
{

#ifdef DUO
//
// Enable sonic interrupts in altera
//
    USHORT InterruptMask;

    InterruptMask =  READ_REGISTER_USHORT(&((PINTERRUPT_REGISTERS)INTERRUPT_VIRTUAL_BASE)->Enable);
    WRITE_REGISTER_USHORT(&((PINTERRUPT_REGISTERS)INTERRUPT_VIRTUAL_BASE)->Enable,InterruptMask | (1 << 3));

#endif



//
//  Software Reset
//
    WRITE_REGISTER_USHORT(&SONIC->Command.Reg,CR_RST);
// Set Hardware dependent configuration.
    WRITE_REGISTER_USHORT(&SONIC->DataConfiguration.Reg,DATA_CONFIGURATION);
// Clear Reset
    WRITE_REGISTER_USHORT(&SONIC->Command.Reg,0);

//
// Initialize all these messy tables of descriptors...
//
    AllocateReceiveDescriptors();
    AllocateReceiveResources();
    SetCamDescriptor();
    AllocateTransmitDescriptors();
//
// Set Interrupt Mask
//
    WRITE_REGISTER_USHORT(&SONIC->InterruptMask.Reg,( INT_BR |
                                INT_LCD |
                                INT_PKTRX |
                                INT_TXDN |
                                INT_TXER |
                                INT_RDE |
                                INT_RBE |
                                INT_RBAE |
                                INT_RFO));
//
//Issue Load CAM Command and wait for this interrupt.
//
    SonicStatus.ExpectedInt = INT_LCD;      // Expect an INT_LCD interrupt.
    SonicIntSemaphore=1;
    WRITE_REGISTER_USHORT(&SONIC->Command.Reg,CR_LCAM);
    if (WaitForSonicInterrupt()) {
        FwPrint("Timeout waiting for sonic int\r\n");
        SonicErrors++;
        return;
    }
    if (SonicStatus.Status==DONE) {
        SonicStatus.ExpectedInt=0;          // clear expected interrupts.
    } else {
        FwPrint("Sonic status not DONE\r\n");
        SonicErrors++;
    }
//
// Issue the RRA Read Command
//
    WRITE_REGISTER_USHORT(&SONIC->Command.Reg,CR_RRA);
    while (READ_REGISTER_USHORT(&SONIC->Command.Reg) & CR_RRA) {   // Wait until the command is processed.
    }
//
// Enable Reception.
//
    WRITE_REGISTER_USHORT(&SONIC->Command.Reg,CR_RXEN);
    while (READ_REGISTER_USHORT(&SONIC->Command.Reg) & CR_RXDIS) { // wait until reception is enabled.
    }
}
VOID
SonicInterrupt(
    )
/*++

Routine Description:

    This routine is the SONIC port service interrupt. It will be called from
    the Trap Handler when getting an interrupt from the SONIC.

Arguments:

    None.

Return Value:

    None.

--*/
{
register USHORT InterruptStatus,NotCleared;
//
// Read Interrupt Status register
//
    InterruptStatus= READ_REGISTER_USHORT(&SONIC->InterruptStatus.Reg);
//
// clear interrupt writing it back
//
    WRITE_REGISTER_USHORT(&SONIC->InterruptStatus.Reg,InterruptStatus);
    InterruptStatus= InterruptStatus & (~INT_HBL); // clear HBL bit.
    if (EXPECTED_INT && NO_OTHER_INT) {
        if (InterruptStatus & INT_TXDN) {
            //
            // Packet Transmited
            //
            SonicStatus.ExpectedInt &= ~INT_TXDN; // clear expected bit.
            // check if it was properly sent.
            SonicStatus.TransmitControl=READ_REGISTER_USHORT(&SONIC->TransmitControl.Reg);
            if (SonicStatus.TransmitControl & (TCR_BCM | TCR_EXC | TCR_FU | TCR_EXD)) {
                //Error Transmiting
                SonicStatus.Status=ERROR;
                SonicStatus.InterruptID = InterruptStatus;
                SonicIntSemaphore = 0;      // signal that the interrupt occurred.
                return;
            }
        }
        if (InterruptStatus & INT_PKTRX) {      // packet received
            SonicStatus.ExpectedInt &= ~INT_PKTRX; // clear interrupt
        }
        if (InterruptStatus & INT_LCD) {            // load cam interrupt
            SonicStatus.ExpectedInt &= ~INT_LCD;   // clear interrupt
        }
        if (SonicStatus.ExpectedInt) {              // we still want another interrupt
            return;
        } else {
            SonicStatus.Status=DONE;
            SonicIntSemaphore = 0;          // signal that the interrupt occurred.
            return;
        }
    } else { // we got an interrupt not expected.
        SonicStatus.InterruptID=InterruptStatus;
        SonicStatus.TransmitControl=READ_REGISTER_USHORT(&SONIC->TransmitControl.Reg);
        SonicStatus.Status=ERROR;
        SonicIntSemaphore = 0;                         // signal that the interrupt ocurred.
        return;
    }
}
ULONG
SonicCheckReception(
    )
/*++

Routine Description:

    This routine compares the sent packet with the received one.
    Makes the used Receive Descriptor available, and if the Receive Buffer
    is full it makes the used receive resurce available.

Arguments:

    None.

Return Value:

    ERROR - If errors are found
    DONE  - If no errors

--*/
{
PUCHAR SentMsg,ReceivedMsg;
USHORT ReceiveStatus,TransmitControl;
ULONG PktSize,i;
    if(CURRENT_DESCRIPTOR->InUse.Data) {
        FwPrint("Descriptor in use\r\n");
        SonicErrors++;
        return ERROR;
    }
    ReceiveStatus=CURRENT_DESCRIPTOR->Status.Data;
    if (ReceiveStatus & (RCR_MC | RCR_BC | RCR_COL | RCR_CRCR | RCR_FAER)) {
        FwPrint("Receive status %lx \r\n",ReceiveStatus);
        SonicErrors++;
        return ERROR;
    }
    //
    // Get ptr to packet and add the offset between Logical and Physical
    // to obtain the physical ptr.
    //
    ReceivedMsg=(PUCHAR) (( (CURRENT_DESCRIPTOR->PktPtr1.Data << 16) |
                            (CURRENT_DESCRIPTOR->PktPtr0.Data))
                            + ReceiveBufferTranslationOffset);

    SentMsg=(PUCHAR) (
          (PhysTransmitDscr->FragPtr1.Fill << 16) |
           PhysTransmitDscr->FragPtr0.Fill
          );
    PktSize=CURRENT_DESCRIPTOR->ByteCount.Data;
    PktSize -=4;                                   // don't check FCS field.
    for (i=0;i<PktSize;i++) {
        if (ReceivedMsg[i] != SentMsg[i]) {
            FwPrint("\r\n Data mismatch, expected %02lx received %02lx \r\n",SentMsg[i],ReceivedMsg[i]);
            SonicErrors++;
            return ERROR;
        }
    }
    //
    // If we get here is because the packet was successfully received.
    // Set the descriptor tables ready for the next packet.
    //
    if (ReceiveStatus & RCR_LPKT) {                 // last packet in RBA.
        //
        // Advance the RWP to free the used Rba again.
        //
        if (READ_REGISTER_USHORT(&SONIC->RWP.Reg)+sizeof(RECEIVE_RESOURCE) ==
            READ_REGISTER_USHORT(&SONIC->REA.Reg)) {//if it's the last one
            WRITE_REGISTER_USHORT(&SONIC->RWP.Reg,READ_REGISTER_USHORT(&SONIC->RSA.Reg));
            // the new RWP points at the starting address.
        } else {
            WRITE_REGISTER_USHORT(&SONIC->RWP.Reg,READ_REGISTER_USHORT(&SONIC->RWP.Reg)+sizeof(RECEIVE_RESOURCE));
        }
    }
    //
    // Free Used Receive Descripor
    //
    CURRENT_DESCRIPTOR->InUse.Data=AVAILABLE;  // make used desc available
    ReceiveDscrQueue.Current=CURRENT_DESCRIPTOR->Link.Data; // First is the next
    LAST_DESCRIPTOR->Link.Data &= NOT_EOL;  // Last is not EOL any more.
    ReceiveDscrQueue.Last=LAST_DESCRIPTOR->Link.Data; // Last is next one.
    LAST_DESCRIPTOR->InUse.Data |= EOL;     // New Last is EOL.
    return DONE;
}
VOID
RomXTOA(
    IN ULONG number,
    OUT PSZ string
    )
/*++

Routine Description:

    This routine converts an ULONG to ASCII.
    The conversion is done in HexaDecimal.

Arguments:

    number  -   Supplies the ULONG to convert.
    string  -   PSZ where the result is placed.

Return Value:

    None.

--*/
{
ULONG i;
    for (i=7;i >= 0; i--) {
        string[i]=TranslationTable[number&0xF];
        number = number >> 4;
    }
    string[8]='\0';
}
ULONG
RomSonicLoopBackTest(
    )
/*++

Routine Description:

    This routine implements the SONIC loopback test for the selftest.
    The Ethernet Controller is tested using a Loopback in the MAC.

Arguments:

    None.

Return Value:

    Returns 0 if no errors are found.

--*/
{
//
// Note Packets are set to be 32 byte long so that they fit in the fifo.
// This is done because at this point interrupts are dispatched trough
// the Bootstrap Vector, reads from PROM take so long that the SONIC
// will get Bus retry Errors.
//
ULONG i,MsgLength=MIN_DATA_LENGTH-32;
CHAR  String[64];
    SonicErrors=0;
    InitSonic();
    if (SonicErrors) {
        return SonicErrors;
    }
    for (i=0;i<16;i++) {
        ComposeMessage(MsgLength,(UCHAR)i);
        SonicStatus.ExpectedInt=INT_TXDN | INT_PKTRX;
        // Issue the Transmit command.
        SonicIntSemaphore=1;
        WRITE_REGISTER_USHORT(&SONIC->Command.Reg,CR_TXP);
        if (WaitForSonicInterrupt()) {
            FwPrint("Timeout waiting for sonic int 2\r\n");
            SonicErrors++;
            return SonicErrors;
        }
        if (SonicStatus.Status==DONE) { // a packet has been sent and received.
            if (SonicCheckReception()==ERROR) {
                FwPrint(ST_RECEIVED_MSG);
                return SonicErrors;
            } else {
                FwPrint(".");
            }
        } else {
            SonicCheckError();
            FwPrint("\r\nInt:%x Tx:%x",SonicStatus.InterruptID,SonicStatus.TransmitControl);
            return SonicErrors;
        }
    }
    return SonicErrors;
}