xref: /reactos/drivers/network/dd/dc21x4/eeprom.c (revision 8f0657a3)
1 /*
2  * PROJECT:     ReactOS DC21x4 Driver
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     EEPROM manipulation and parsing
5  * COPYRIGHT:   Copyright 2023 Dmitry Borisov <di.sean@protonmail.com>
6  */
7 
8 /* INCLUDES *******************************************************************/
9 
10 #include "dc21x4.h"
11 
12 #include <debug.h>
13 
14 /* GLOBALS ********************************************************************/
15 
16 #define SROM_READ(Adapter, Data)  \
17     do { \
18         *Data = DC_READ((Adapter), DcCsr9_SerialInterface); \
19         NdisStallExecution(10); \
20     } while (0)
21 
22 #define SROM_WRITE(Adapter, Value)  \
23     do { \
24         DC_WRITE((Adapter), DcCsr9_SerialInterface, Value); \
25         NdisStallExecution(10); \
26     } while (0)
27 
28 extern DC_PG_DATA DC_SROM_REPAIR_ENTRY SRompRepairData[];
29 
30 LIST_ENTRY SRompAdapterList;
31 
32 _Interlocked_
33 static volatile LONG SRompAdapterLock = 0;
34 
35 /* PRIVATE FUNCTIONS **********************************************************/
36 
37 static
38 CODE_SEG("PAGE")
39 VOID
40 SRomAcquireListMutex(VOID)
41 {
42     PAGED_CODE();
43 
44     while (_InterlockedCompareExchange(&SRompAdapterLock, 1, 0))
45     {
46         NdisMSleep(10);
47     }
48 }
49 
50 static inline
51 CODE_SEG("PAGE")
52 VOID
53 SRomReleaseListMutex(VOID)
54 {
55     PAGED_CODE();
56 
57     _InterlockedDecrement(&SRompAdapterLock);
58 }
59 
60 static
61 CODE_SEG("PAGE")
62 BOOLEAN
63 SRomIsAdapterInList(
64     _In_ PDC21X4_ADAPTER Adapter,
65     _In_ BOOLEAN SearchForMaster,
66     _Out_opt_ PDC_SROM_ENTRY* FoundEntry)
67 {
68     PLIST_ENTRY PrevEntry;
69     PDC_SROM_ENTRY SRomEntry;
70 
71     PAGED_CODE();
72 
73     /* Loop the adapter list backwards */
74     for (PrevEntry = (&SRompAdapterList)->Blink;
75          PrevEntry != &SRompAdapterList;
76          PrevEntry = PrevEntry->Blink)
77     {
78         SRomEntry = CONTAINING_RECORD(PrevEntry, DC_SROM_ENTRY, ListEntry);
79 
80         if ((SRomEntry->ChipType == Adapter->ChipType) &&
81             (SRomEntry->BusNumber == Adapter->BusNumber) &&
82             (!SearchForMaster || (SRomEntry->DeviceNumber == Adapter->DeviceNumber)))
83         {
84             if (FoundEntry)
85                 *FoundEntry = SRomEntry;
86 
87             return TRUE;
88         }
89     }
90 
91     return FALSE;
92 }
93 
94 static
95 CODE_SEG("PAGE")
96 BOOLEAN
97 SRomRegisterMasterAdapter(
98     _In_ PDC21X4_ADAPTER Adapter,
99     _In_ PDC_SROM_ENTRY SRomEntry)
100 {
101     BOOLEAN Success;
102 
103     PAGED_CODE();
104 
105     SRomAcquireListMutex();
106 
107     /* Check if board is already registered */
108     if (SRomIsAdapterInList(Adapter, TRUE, NULL))
109     {
110         Success = FALSE;
111         goto Exit;
112     }
113 
114     Adapter->SRomEntry = SRomEntry;
115 
116     SRomEntry->ChipType = Adapter->ChipType;
117     SRomEntry->BusNumber = Adapter->BusNumber;
118     SRomEntry->DeviceNumber = Adapter->DeviceNumber;
119     SRomEntry->InterruptLevel = Adapter->InterruptLevel;
120     SRomEntry->InterruptVector = Adapter->InterruptVector;
121 
122     /* Register the port */
123     SRomEntry->DeviceBitmap |= 1 << Adapter->DeviceNumber;
124 
125     /*
126      * On some multiport boards only the first port contains an EEPROM.
127      * We put their references to the global adapter list.
128      */
129     InsertTailList(&SRompAdapterList, &SRomEntry->ListEntry);
130     Success = TRUE;
131 
132 Exit:
133     SRomReleaseListMutex();
134 
135     return Success;
136 }
137 
138 static
139 CODE_SEG("PAGE")
140 BOOLEAN
141 SRomFindMasterAdapter(
142     _In_ PDC21X4_ADAPTER Adapter,
143     _Out_ PDC_SROM_ENTRY* FoundEntry)
144 {
145     PDC_SROM_ENTRY SRomEntry;
146     ULONG i;
147     BOOLEAN Found;
148 
149     PAGED_CODE();
150 
151     SRomAcquireListMutex();
152 
153     if (!SRomIsAdapterInList(Adapter, FALSE, &SRomEntry))
154     {
155         Found = FALSE;
156         goto Exit;
157     }
158 
159     Adapter->SRomEntry = SRomEntry;
160 
161     /* Register the port */
162     SRomEntry->DeviceBitmap |= 1 << Adapter->DeviceNumber;
163 
164     /*
165      * Determine the port index that should be used in order to
166      * (possibly) update the base MAC address.
167      */
168     for (i = 0; i < PCI_MAX_DEVICES; ++i)
169     {
170         if (i == Adapter->DeviceNumber)
171             break;
172 
173         if (SRomEntry->DeviceBitmap & (1 << i))
174             ++Adapter->ControllerIndex;
175     }
176 
177     /*
178      * On a multiport board there can be up to 4 ports
179      * connected through a 21050 or 21152 PCI-to-PCI Bridge.
180      * These boards share a single IRQ line between all of the chips.
181      * Some BIOSes incorrectly assign different IRQs to the different ports.
182      */
183     Adapter->InterruptLevel = SRomEntry->InterruptLevel;
184     Adapter->InterruptVector = SRomEntry->InterruptVector;
185 
186     WARN("EEPROM is missing on controller %u, using image from the master at %u:%u\n",
187          Adapter->DeviceNumber,
188          SRomEntry->BusNumber,
189          SRomEntry->DeviceNumber);
190 
191     *FoundEntry = SRomEntry;
192     Found = TRUE;
193 
194 Exit:
195     SRomReleaseListMutex();
196 
197     return Found;
198 }
199 
200 static
201 CODE_SEG("PAGE")
202 BOOLEAN
203 SRomIsEmpty(
204     _In_reads_bytes_(Length) const VOID* Buffer,
205     _In_ ULONG Length)
206 {
207     const UCHAR* Data = Buffer;
208     const UCHAR FirstByte = Data[0];
209     ULONG i;
210 
211     PAGED_CODE();
212 
213     for (i = 1; i < Length; ++i)
214     {
215         if (FirstByte != Data[i])
216             return FALSE;
217     }
218 
219     return TRUE;
220 }
221 
222 static
223 CODE_SEG("PAGE")
224 VOID
225 SRomNWayAdvertise(
226     _In_ PDC21X4_ADAPTER Adapter,
227     _In_ ULONG MediaCode)
228 {
229     PAGED_CODE();
230 
231     switch (MediaCode)
232     {
233         case MEDIA_10T:
234             Adapter->SymAdvertising |= MII_ADV_10T_HD;
235             break;
236         case MEDIA_10T_FD:
237             Adapter->SymAdvertising |= MII_ADV_10T_FD;
238             break;
239         case MEDIA_100TX_HD:
240             Adapter->SymAdvertising |= MII_ADV_100T_HD;
241             break;
242         case MEDIA_100TX_FD:
243             Adapter->SymAdvertising |= MII_ADV_100T_FD;
244             break;
245         case MEDIA_100T4:
246             Adapter->SymAdvertising |= MII_ADV_100T4;
247             break;
248 
249         default:
250             break;
251     }
252 }
253 
254 static
255 CODE_SEG("PAGE")
256 NDIS_STATUS
257 SRomDecodeBlockGpr(
258     _In_ PDC21X4_ADAPTER Adapter,
259     _In_ PUCHAR SRomEnd,
260     _In_ PUCHAR BlockData)
261 {
262     PDC_MEDIA Media;
263     ULONG MediaCode, OpMode;
264     USHORT Command;
265 
266     PAGED_CODE();
267 
268     if (BlockData > (SRomEnd - 4))
269         return NDIS_STATUS_BUFFER_OVERFLOW;
270 
271     MediaCode = SRomGetMediaCode(*BlockData++);
272     if (MediaCode > SROM_MEDIA_MAX)
273     {
274         WARN("Unknown media code %u\n", MediaCode);
275         return NDIS_STATUS_SUCCESS;
276     }
277     Adapter->MediaBitmap |= 1 << MediaCode;
278 
279     Media = &Adapter->Media[MediaCode];
280 
281     Media->GpioData = *BlockData++;
282 
283     Command = DcRetrieveWord(BlockData);
284 
285     OpMode = Media->OpMode;
286     OpMode &= ~SROM_OPMODE_MASK;
287     OpMode |= SRomCommandToOpMode(Command);
288     Media->OpMode = OpMode;
289 
290     if (SRomMediaHasActivityIndicator(Command))
291     {
292         Media->LinkMask = SRomMediaGetSenseMask(Command);
293     }
294     if (SRomMediaActivityIsActiveLow(Command))
295     {
296         Media->Polarity = 0xFFFFFFFF;
297     }
298 
299     INFO("GPR #%u %s, Command %04lx, Data %02lx\n",
300          MediaCode,
301          MediaNumber2Str(Adapter, MediaCode),
302          Command,
303          Media->GpioData);
304 
305     return NDIS_STATUS_SUCCESS;
306 }
307 
308 static
309 CODE_SEG("PAGE")
310 NDIS_STATUS
311 SRomDecodeBlockMii(
312     _In_ PDC21X4_ADAPTER Adapter,
313     _In_ PUCHAR SRomEnd,
314     _In_ PUCHAR BlockData,
315     _In_ BOOLEAN IsOldVersion)
316 {
317     PDC_MII_MEDIA Media;
318     ULONG i, Bytes, Offset;
319     UCHAR PhyNumber, StreamLength, InterruptInfo;
320     USHORT Capabilities, Fdx, Ttm;
321 
322     PAGED_CODE();
323 
324     if (BlockData > (SRomEnd - 2))
325         return NDIS_STATUS_BUFFER_OVERFLOW;
326 
327     PhyNumber = *BlockData++;
328 
329     /*
330      * Even though the SROM specification allows several
331      * PHY devices to be connected to the same chip on a board,
332      * most if not all boards never use more than 1 MII PHY device.
333      */
334     if (Adapter->Features & DC_HAS_MII)
335     {
336         WARN("Unsupported PHY %u\n", PhyNumber);
337         return NDIS_STATUS_SUCCESS;
338     }
339 
340     Media = &Adapter->MiiMedia;
341 
342     /*
343      * PHY selection sequence
344      */
345 
346     StreamLength = *BlockData++;
347     if (StreamLength > SROM_MAX_STREAM_REGS)
348     {
349         WARN("Too much registers %u\n", StreamLength);
350         return NDIS_STATUS_SUCCESS;
351     }
352 
353     Bytes = StreamLength;
354     if (!IsOldVersion)
355     {
356         /* In words */
357         Bytes *= 2;
358     }
359     if ((BlockData + Bytes) > (SRomEnd - 1))
360     {
361         return NDIS_STATUS_BUFFER_OVERFLOW;
362     }
363 
364     Media->SetupStreamLength = StreamLength;
365 
366     /* Check if we already have the GPIO direction data */
367     if (Media->SetupStream[0] != 0)
368     {
369         Offset = 1;
370         ++Media->SetupStreamLength;
371     }
372     else
373     {
374         Offset = 0;
375     }
376 
377     for (i = 0; i < StreamLength; ++i)
378     {
379         if (IsOldVersion)
380         {
381             Media->SetupStream[i + Offset] = *BlockData++;
382         }
383         else
384         {
385             Media->SetupStream[i + Offset] = DcRetrieveWord(BlockData);
386             BlockData += sizeof(USHORT);
387         }
388     }
389 
390     /*
391      * PHY reset sequence
392      */
393 
394     if (BlockData > (SRomEnd - 1))
395     {
396         return NDIS_STATUS_BUFFER_OVERFLOW;
397     }
398 
399     StreamLength = *BlockData++;
400     if (StreamLength > SROM_MAX_STREAM_REGS)
401     {
402         WARN("Too much registers %u\n", StreamLength);
403         return NDIS_STATUS_SUCCESS;
404     }
405 
406     Bytes = StreamLength;
407     if (!IsOldVersion)
408     {
409         /* In words */
410         Bytes *= 2;
411     }
412     if ((BlockData + Bytes) > (SRomEnd - 1))
413     {
414         return NDIS_STATUS_BUFFER_OVERFLOW;
415     }
416 
417     Media->ResetStreamLength = StreamLength;
418 
419     for (i = 0; i < StreamLength; ++i)
420     {
421         if (IsOldVersion)
422         {
423             Media->ResetStream[i] = *BlockData++;
424         }
425         else
426         {
427             Media->ResetStream[i] = DcRetrieveWord(BlockData);
428             BlockData += sizeof(USHORT);
429         }
430     }
431 
432     /*
433      * MII data
434      */
435 
436     Bytes = 4 * sizeof(USHORT);
437     if (!IsOldVersion)
438     {
439         Bytes += 1;
440     }
441     if (BlockData > (SRomEnd - Bytes))
442     {
443         return NDIS_STATUS_BUFFER_OVERFLOW;
444     }
445 
446     Capabilities = DcRetrieveWord(BlockData);
447     BlockData += sizeof(USHORT);
448 
449     Media->Advertising = DcRetrieveWord(BlockData);
450     BlockData += sizeof(USHORT);
451 
452     Fdx = DcRetrieveWord(BlockData);
453     BlockData += sizeof(USHORT);
454 
455     Ttm = DcRetrieveWord(BlockData);
456     BlockData += sizeof(USHORT);
457 
458     InterruptInfo = IsOldVersion ? 0 : *BlockData;
459 
460     Adapter->Features |= DC_HAS_MII;
461 
462     INFO("MII #%u, Caps %04lx, Adv %04lx, Fdx %04lx, Ttm %04lx, Int %02x\n",
463          PhyNumber,
464          Capabilities,
465          Media->Advertising,
466          Fdx,
467          Ttm,
468          InterruptInfo);
469 
470     return NDIS_STATUS_SUCCESS;
471 }
472 
473 static
474 CODE_SEG("PAGE")
475 NDIS_STATUS
476 SRomDecodeBlockSia(
477     _In_ PDC21X4_ADAPTER Adapter,
478     _In_ PUCHAR SRomEnd,
479     _In_ PUCHAR BlockData)
480 {
481     PDC_MEDIA Media;
482     UCHAR BlockStart;
483     ULONG MediaCode;
484     BOOLEAN HasExtendedData;
485 
486     PAGED_CODE();
487 
488     if (BlockData > (SRomEnd - 1))
489         return NDIS_STATUS_BUFFER_OVERFLOW;
490 
491     BlockStart = *BlockData++;
492 
493     HasExtendedData = SRomBlockHasExtendedData(BlockStart);
494     if (BlockData > (SRomEnd - (HasExtendedData ? 10 : 4)))
495         return NDIS_STATUS_BUFFER_OVERFLOW;
496 
497     MediaCode = SRomGetMediaCode(BlockStart);
498     if (MediaCode > SROM_MEDIA_MAX && MediaCode != SROM_MEDIA_HMR)
499     {
500         WARN("Unknown media code %u\n", MediaCode);
501         return NDIS_STATUS_SUCCESS;
502     }
503 
504     /* TODO: There were a few 21143-based boards with HMR media */
505     if ((MediaCode == SROM_MEDIA_HMR) && (Adapter->ChipType != DC21145))
506     {
507         ERR("FIXME: 21143 HMR is not supported yet\n");
508         return NDIS_STATUS_SUCCESS;
509     }
510 
511     /* Map the code to our internal value */
512     if (MediaCode == SROM_MEDIA_HMR)
513     {
514         MediaCode = MEDIA_HMR;
515     }
516 
517     Adapter->MediaBitmap |= 1 << MediaCode;
518 
519     Media = &Adapter->Media[MediaCode];
520 
521     if (HasExtendedData)
522     {
523         Media->Csr13 = DcRetrieveWord(BlockData);
524         BlockData += sizeof(USHORT);
525 
526         Media->Csr14 = DcRetrieveWord(BlockData);
527         BlockData += sizeof(USHORT);
528 
529         Media->Csr15 = DcRetrieveWord(BlockData);
530         BlockData += sizeof(USHORT);
531     }
532 
533     Media->GpioCtrl = DcRetrieveWord(BlockData);
534     BlockData += sizeof(USHORT);
535 
536     Media->GpioData = DcRetrieveWord(BlockData);
537     BlockData += sizeof(USHORT);
538 
539     SRomNWayAdvertise(Adapter, MediaCode);
540 
541     INFO("SIA #%u %s, %sCSR13 %04lx CSR14 %04lx CSR15 %04lx, "
542          "Ctrl %04lx, Data %04lx\n",
543          MediaCode,
544          MediaNumber2Str(Adapter, MediaCode),
545          HasExtendedData ? "EXT " : "",
546          Media->Csr13,
547          Media->Csr14,
548          Media->Csr15,
549          Media->GpioCtrl,
550          Media->GpioData);
551 
552     return NDIS_STATUS_SUCCESS;
553 }
554 
555 static
556 CODE_SEG("PAGE")
557 NDIS_STATUS
558 SRomDecodeBlockSym(
559     _In_ PDC21X4_ADAPTER Adapter,
560     _In_ PUCHAR SRomEnd,
561     _In_ PUCHAR BlockData)
562 {
563     PDC_MEDIA Media;
564     ULONG MediaCode, OpMode;
565     USHORT Command;
566 
567     PAGED_CODE();
568 
569     if (BlockData > (SRomEnd - 7))
570         return NDIS_STATUS_BUFFER_OVERFLOW;
571 
572     MediaCode = SRomGetMediaCode(*BlockData++);
573     if (MediaCode > SROM_MEDIA_MAX)
574     {
575         WARN("Unknown media code %u\n", MediaCode);
576         return NDIS_STATUS_SUCCESS;
577     }
578     Adapter->MediaBitmap |= 1 << MediaCode;
579 
580     Media = &Adapter->Media[MediaCode];
581 
582     Media->GpioCtrl = DcRetrieveWord(BlockData);
583     BlockData += sizeof(USHORT);
584 
585     Media->GpioData = DcRetrieveWord(BlockData);
586     BlockData += sizeof(USHORT);
587 
588     Command = DcRetrieveWord(BlockData);
589     BlockData += sizeof(USHORT);
590 
591     OpMode = Media->OpMode;
592     OpMode &= ~SROM_OPMODE_MASK;
593     OpMode |= SRomCommandToOpMode(Command);
594     Media->OpMode = OpMode;
595 
596     SRomNWayAdvertise(Adapter, MediaCode);
597 
598     INFO("SYM #%u %s, Command %04lx, Ctrl %04lx, Data %04lx\n",
599          MediaCode,
600          MediaNumber2Str(Adapter, MediaCode),
601          Command,
602          Media->GpioCtrl,
603          Media->GpioData);
604 
605     return NDIS_STATUS_SUCCESS;
606 }
607 
608 static
609 CODE_SEG("PAGE")
610 NDIS_STATUS
611 SRomDecodeBlockReset(
612     _In_ PDC21X4_ADAPTER Adapter,
613     _In_ PUCHAR SRomEnd,
614     _In_ PUCHAR BlockData)
615 {
616     UCHAR i, StreamLength;
617 
618     PAGED_CODE();
619 
620     if (BlockData > (SRomEnd - 1))
621         return NDIS_STATUS_BUFFER_OVERFLOW;
622 
623     StreamLength = *BlockData++;
624     if (StreamLength > SROM_MAX_STREAM_REGS)
625     {
626         WARN("Too much registers %u\n", StreamLength);
627         return NDIS_STATUS_SUCCESS;
628     }
629 
630     if ((BlockData + StreamLength * 2) > (SRomEnd - 1))
631         return NDIS_STATUS_BUFFER_OVERFLOW;
632 
633     Adapter->ResetStreamLength = StreamLength;
634 
635     for (i = 0; i < StreamLength; ++i)
636     {
637         Adapter->ResetStream[i] = DcRetrieveWord(BlockData);
638         BlockData += sizeof(USHORT);
639     }
640 
641     INFO("RESET, length %u\n", StreamLength);
642 
643     return NDIS_STATUS_SUCCESS;
644 }
645 
646 static
647 CODE_SEG("PAGE")
648 NDIS_STATUS
649 SRomDecodeBlockHmr(
650     _In_ PDC21X4_ADAPTER Adapter,
651     _In_ PUCHAR SRomEnd,
652     _In_ PUCHAR BlockData,
653     _In_ UCHAR BlockLength)
654 {
655     ULONG Offset, ExtraData, i;
656 
657     PAGED_CODE();
658 
659     if (BlockData > (SRomEnd - (2 + 6)))
660         return NDIS_STATUS_BUFFER_OVERFLOW;
661 
662     Adapter->AnalogControl = DcRetrieveWord(BlockData) << 16;
663     BlockData += sizeof(USHORT);
664 
665     Adapter->HpnaRegister[HPNA_CONTROL_LOW] = *BlockData++;
666     Adapter->HpnaRegister[HPNA_CONTROL_HIGH] = *BlockData++;
667     Adapter->HpnaRegister[HPNA_NOISE] = *BlockData++;
668     Adapter->HpnaRegister[HPNA_NOISE_FLOOR] = *BlockData++;
669     Adapter->HpnaRegister[HPNA_NOISE_CEILING] = *BlockData++;
670     Adapter->HpnaRegister[HPNA_NOISE_ATTACK] = *BlockData++;
671     Adapter->HpnaInitBitmap |= ((1 << HPNA_CONTROL_LOW) |
672                                 (1 << HPNA_CONTROL_HIGH) |
673                                 (1 << HPNA_NOISE) |
674                                 (1 << HPNA_NOISE_FLOOR) |
675                                 (1 << HPNA_NOISE_CEILING) |
676                                 (1 << HPNA_NOISE_ATTACK));
677 
678     Offset = 2 /* Length and type fields */ + 2 /* Analog ctrl */ + 6; /* Regs */
679     ExtraData = (BlockLength - Offset);
680 
681     if ((BlockData + ExtraData) > (SRomEnd - 1))
682         return NDIS_STATUS_BUFFER_OVERFLOW;
683 
684     for (i = 0; i < ExtraData / sizeof(USHORT); ++i)
685     {
686         UCHAR RegAddress = SRomHmrRegAddress(*BlockData++);
687         UCHAR RegValue = *BlockData++;
688 
689         Adapter->HpnaRegister[RegAddress] = RegValue;
690         Adapter->HpnaInitBitmap |= 1 << RegAddress;
691     }
692 
693 #if DBG
694     INFO_VERB("Analog Ctrl %04lx\n", Adapter->AnalogControl);
695 
696     for (i = 0; i < RTL_NUMBER_OF(Adapter->HpnaRegister); ++i)
697     {
698         if (Adapter->HpnaInitBitmap & (1 << i))
699         {
700             INFO_VERB("HR Reg %02x = %02x\n", i, Adapter->HpnaRegister[i]);
701         }
702     }
703 
704     if (ExtraData % sizeof(USHORT))
705     {
706         INFO_VERB("HR Data = %02x\n", *BlockData);
707     }
708 #endif
709 
710     return NDIS_STATUS_SUCCESS;
711 }
712 
713 static
714 CODE_SEG("PAGE")
715 NDIS_STATUS
716 SRomParseExtendedBlock(
717     _In_ PDC21X4_ADAPTER Adapter,
718     _In_ PUCHAR SRomEnd,
719     _In_ PUCHAR BlockData,
720     _Out_ PULONG BlockSize)
721 {
722     NDIS_STATUS Status;
723     ULONG Length, Type;
724 
725     PAGED_CODE();
726 
727     if (BlockData > (SRomEnd - 2))
728         return NDIS_STATUS_BUFFER_OVERFLOW;
729 
730     Length = SRomGetExtendedBlockLength(*BlockData++);
731     Type = *BlockData++;
732 
733     *BlockSize = Length;
734 
735     switch (Type)
736     {
737         case SROM_BLOCK_TYPE_GPR:
738             Status = SRomDecodeBlockGpr(Adapter, SRomEnd, BlockData);
739             break;
740         case SROM_BLOCK_TYPE_MII_1:
741         case SROM_BLOCK_TYPE_MII_2:
742             Status = SRomDecodeBlockMii(Adapter,
743                                         SRomEnd,
744                                         BlockData,
745                                         (Type == SROM_BLOCK_TYPE_MII_1));
746             break;
747         case SROM_BLOCK_TYPE_SIA:
748             Status = SRomDecodeBlockSia(Adapter, SRomEnd, BlockData);
749             break;
750         case SROM_BLOCK_TYPE_SYM:
751             Status = SRomDecodeBlockSym(Adapter, SRomEnd, BlockData);
752             break;
753         case SROM_BLOCK_TYPE_RESET:
754             Status = SRomDecodeBlockReset(Adapter, SRomEnd, BlockData);
755             break;
756         case SROM_BLOCK_TYPE_HOMERUN:
757             Status = SRomDecodeBlockHmr(Adapter, SRomEnd, BlockData, Length);
758             break;
759 
760         /* Skip over the unused or unknown blocks */
761         default:
762             WARN("Unknown block type %u, length %u\n", Type, Length);
763         case SROM_BLOCK_TYPE_PHY_SHUTDOWN:
764             Status = NDIS_STATUS_SUCCESS;
765             break;
766     }
767 
768     return Status;
769 }
770 
771 static
772 CODE_SEG("PAGE")
773 NDIS_STATUS
774 SRomParse21041Block(
775     _In_ PDC21X4_ADAPTER Adapter,
776     _In_ PUCHAR SRomEnd,
777     _In_ PUCHAR BlockData,
778     _Out_ PULONG BlockSize)
779 {
780     PDC_MEDIA Media;
781     UCHAR BlockStart;
782     ULONG MediaCode;
783     BOOLEAN HasExtendedData;
784 
785     PAGED_CODE();
786 
787     if (BlockData > (SRomEnd - 1))
788         return NDIS_STATUS_BUFFER_OVERFLOW;
789 
790     BlockStart = *BlockData++;
791 
792     HasExtendedData = SRomBlockHasExtendedData(BlockStart);
793     if (BlockData > (SRomEnd - (HasExtendedData ? 7 : 1)))
794         return NDIS_STATUS_BUFFER_OVERFLOW;
795 
796     *BlockSize = HasExtendedData ? 7 : 1;
797 
798     MediaCode = SRomGetMediaCode(BlockStart);
799     if (MediaCode > SROM_MEDIA_MAX)
800     {
801         WARN("Unknown media code %u\n", MediaCode);
802         return NDIS_STATUS_SUCCESS;
803     }
804     Adapter->MediaBitmap |= 1 << MediaCode;
805 
806     Media = &Adapter->Media[MediaCode];
807 
808     if (HasExtendedData)
809     {
810         Media->Csr13 = DcRetrieveWord(BlockData);
811         BlockData += sizeof(USHORT);
812 
813         Media->Csr14 = DcRetrieveWord(BlockData);
814         BlockData += sizeof(USHORT);
815 
816         Media->Csr15 = DcRetrieveWord(BlockData);
817         BlockData += sizeof(USHORT);
818     }
819 
820     INFO("SIA #%u %s, %sCSR13 %04lx CSR14 %04lx CSR15 %04lx\n",
821          MediaCode,
822          MediaNumber2Str(Adapter, MediaCode),
823          HasExtendedData ? "EXT " : "",
824          Media->Csr13,
825          Media->Csr14,
826          Media->Csr15);
827 
828     return NDIS_STATUS_SUCCESS;
829 }
830 
831 static
832 CODE_SEG("PAGE")
833 BOOLEAN
834 SRomChecksumValid(
835     _In_ PUCHAR SRom)
836 {
837     USHORT Checksum;
838 
839     PAGED_CODE();
840 
841     Checksum = ~DcEthernetCrc(SRom, SROM_CHECKSUM_V1);
842     if (Checksum == DcRetrieveWord(&SRom[SROM_CHECKSUM_V1]))
843         return TRUE;
844 
845     Checksum = ~DcEthernetCrc(SRom, SROM_CHECKSUM_V2);
846     if (Checksum == DcRetrieveWord(&SRom[SROM_CHECKSUM_V2]))
847         return TRUE;
848 
849     return FALSE;
850 }
851 
852 static
853 CODE_SEG("PAGE")
854 BOOLEAN
855 AddressRomChecksumValid(
856     _In_reads_bytes_(EAR_SIZE) PVOID AddressRom)
857 {
858     const UCHAR* Octet = AddressRom;
859     ULONG64 TestPatterm;
860     ULONG Checksum, i;
861 
862     PAGED_CODE();
863 
864     NdisMoveMemory(&TestPatterm, &Octet[24], 8);
865     if (TestPatterm != EAR_TEST_PATTERN)
866         return FALSE;
867 
868     for (i = 0; i < 8; ++i)
869     {
870         if (Octet[i] != Octet[15 - i])
871             return FALSE;
872     }
873 
874     Checksum = (Octet[0] << 10) + (Octet[2] << 9) + (Octet[4] << 8) +
875                (Octet[1] << 2) + (Octet[3] << 1) + Octet[5];
876     Checksum %= 0xFFFF;
877 
878     return ((USHORT)Checksum == ((Octet[6] << 8) | Octet[7]));
879 }
880 
881 static
882 CODE_SEG("PAGE")
883 BOOLEAN
884 SRomReadMacAddress(
885     _In_ PDC21X4_ADAPTER Adapter,
886     _In_ PUCHAR SRom,
887     _Out_opt_ PULONG AddressOffset)
888 {
889     ULONG MacOffset;
890 
891     PAGED_CODE();
892 
893     /* Check if we have a board with an old EAR format */
894     if (NdisEqualMemory(SRom, &SRom[16], 8))
895     {
896         /* Validate the EAR checksum */
897         if (!AddressRomChecksumValid(SRom))
898         {
899             ERR("EAR has an invalid checksum\n");
900             return FALSE;
901         }
902 
903         MacOffset = 0;
904         goto ReadMac;
905     }
906 
907     /* Check for a new SROM format */
908     if (Adapter->ChipType != DC21040)
909     {
910         /* Validate the SROM checksum */
911         if (SRomChecksumValid(SRom))
912         {
913             MacOffset = SROM_MAC_ADDRESS;
914             goto ReadMac;
915         }
916     }
917 
918     /* Sanity check */
919     if (*(PULONG)SRom == 0xFFFFFFFF || *(PULONG)SRom == 0)
920         return FALSE;
921 
922     WARN("Legacy/unknown board found\n");
923     MacOffset = 0;
924 
925 ReadMac:
926     if (AddressOffset)
927         *AddressOffset = MacOffset;
928 
929     NdisMoveMemory(Adapter->PermanentMacAddress,
930                    &SRom[MacOffset],
931                    ETH_LENGTH_OF_ADDRESS);
932 
933     return TRUE;
934 }
935 
936 static
937 CODE_SEG("PAGE")
938 NDIS_STATUS
939 SRomParseHeader(
940     _In_ PDC21X4_ADAPTER Adapter,
941     _In_ PUCHAR SRom,
942     _Out_ PUCHAR* InfoLeaf,
943     _Out_ PUCHAR* SRomEnd)
944 {
945     ULONG i, MacOffset, LeafOffset;
946 
947     PAGED_CODE();
948 
949     if (!SRomReadMacAddress(Adapter, SRom, &MacOffset))
950     {
951         ERR("Unable to read the MAC address\n");
952         return NDIS_STATUS_FAILURE;
953     }
954 
955     /* Assign our own fake info leaf */
956     if (MacOffset != SROM_MAC_ADDRESS)
957     {
958         for (i = 0; SRompRepairData[i].InfoLeaf; ++i)
959         {
960             /* Check for a MAC match */
961             if (NdisEqualMemory(SRompRepairData[i].InfoLeaf, &Adapter->PermanentMacAddress, 3))
962             {
963                 /* This check is used to distinguish Accton EN1207 from Maxtech */
964                 if ((Adapter->PermanentMacAddress[0] == 0xE8) && (SRom[0x1A] == 0x55))
965                     ++i;
966 
967                 break;
968             }
969         }
970         if (!SRompRepairData[i].InfoLeaf)
971         {
972             ERR("Non-standard SROM format, OUI %02x:%02x:%02x\n",
973                 Adapter->PermanentMacAddress[0],
974                 Adapter->PermanentMacAddress[1],
975                 Adapter->PermanentMacAddress[2]);
976 
977             return NDIS_STATUS_ADAPTER_NOT_FOUND;
978         }
979 
980         *InfoLeaf = &SRompRepairData[i].InfoLeaf[3];
981         *SRomEnd = *InfoLeaf + SRompRepairData[i].Length;
982 
983         /* Update the base address on multiport boards */
984         Adapter->PermanentMacAddress[5] += Adapter->ControllerIndex;
985 
986 #if DBG
987         WARN("Non-standard SROM format, using '%s' info leaf\n", SRompRepairData[i].Name);
988 #endif
989         return STATUS_SUCCESS;
990     }
991 
992     /* Check if the SROM chip is shared between multiple controllers on a multiport board */
993     if (SRom[SROM_CONTROLLER_COUNT] > 1)
994     {
995         INFO("Multiport board, controller number %u (%u/%u)\n",
996              Adapter->DeviceNumber,
997              Adapter->ControllerIndex + 1,
998              SRom[SROM_CONTROLLER_COUNT]);
999 
1000         for (i = 0; i < SRom[SROM_CONTROLLER_COUNT]; ++i)
1001         {
1002             if (SROM_DEVICE_NUMBER(i) >= EE_SIZE)
1003                 return NDIS_STATUS_BUFFER_OVERFLOW;
1004 
1005             if (SRom[SROM_DEVICE_NUMBER(i)] == Adapter->DeviceNumber)
1006                 break;
1007         }
1008         if (i == SRom[SROM_CONTROLLER_COUNT])
1009         {
1010             ERR("Controller %u was not found in the SROM\n", Adapter->DeviceNumber);
1011             return NDIS_STATUS_ADAPTER_NOT_FOUND;
1012         }
1013 
1014         if (SROM_LEAF_OFFSET(i) >= EE_SIZE)
1015             return NDIS_STATUS_BUFFER_OVERFLOW;
1016 
1017         /* Update the base address */
1018         Adapter->PermanentMacAddress[5] += i;
1019     }
1020     else
1021     {
1022         i = 0;
1023     }
1024 
1025     /* Controller info block offset */
1026     LeafOffset = DcRetrieveWord(SRom + SROM_LEAF_OFFSET(i));
1027     if (LeafOffset > (EE_SIZE - sizeof(DC_SROM_COMPACT_BLOCK)))
1028         return NDIS_STATUS_BUFFER_OVERFLOW;
1029 
1030     /* Controller info leaf */
1031     *InfoLeaf = &SRom[LeafOffset];
1032 
1033     *SRomEnd = SRom + EE_SIZE;
1034 
1035     return STATUS_SUCCESS;
1036 }
1037 
1038 static
1039 CODE_SEG("PAGE")
1040 NDIS_STATUS
1041 SRomParse(
1042     _In_ PDC21X4_ADAPTER Adapter,
1043     _In_ PUCHAR SRom)
1044 {
1045     ULONG Index, BlockCount, BlockSize, DefaultMedia;
1046     NDIS_STATUS Status;
1047     USHORT GpioCtrl;
1048     PUCHAR Data, SRomEnd;
1049 
1050     PAGED_CODE();
1051 
1052     INFO("SROM Version %u, Controller count %u\n",
1053          SRom[SROM_VERSION],
1054          SRom[SROM_CONTROLLER_COUNT]);
1055 
1056     Status = SRomParseHeader(Adapter, SRom, &Data, &SRomEnd);
1057     if (Status != NDIS_STATUS_SUCCESS)
1058         return Status;
1059 
1060     DefaultMedia = DcRetrieveWord(Data);
1061     Data += sizeof(USHORT);
1062 
1063     INFO("Default Media:  %04lx\n", DefaultMedia);
1064 
1065     /* Direction of the GPIO pins */
1066     if (Adapter->ChipType == DC21140)
1067     {
1068         GpioCtrl = *Data++;
1069 
1070         INFO("GPIO Direction: %04lx\n", GpioCtrl);
1071 
1072         GpioCtrl |= DC_GPIO_CONTROL;
1073 
1074         for (Index = 0; Index < MEDIA_LIST_MAX; ++Index)
1075         {
1076             Adapter->Media[Index].GpioCtrl = GpioCtrl;
1077         }
1078 
1079         /* Control word for block type 1 */
1080         Adapter->MiiMedia.SetupStream[0] = GpioCtrl;
1081     }
1082 
1083     BlockCount = *Data++;
1084 
1085     INFO("Block Count:    %u\n", BlockCount);
1086 
1087     if (BlockCount == 0 || BlockCount == 0xFF)
1088     {
1089         WARN("No media information found\n");
1090         return NDIS_STATUS_SUCCESS;
1091     }
1092 
1093     /* Analyze and decode blocks */
1094     for (Index = 0; Index < BlockCount; ++Index)
1095     {
1096         if (Adapter->ChipType == DC21041)
1097         {
1098             Status = SRomParse21041Block(Adapter, SRomEnd, Data, &BlockSize);
1099         }
1100         else
1101         {
1102             if (Data > (SRomEnd - 1))
1103                 return NDIS_STATUS_BUFFER_OVERFLOW;
1104 
1105             if (SRomIsBlockExtended(*Data))
1106             {
1107                 Status = SRomParseExtendedBlock(Adapter, SRomEnd, Data, &BlockSize);
1108             }
1109             else
1110             {
1111                 Status = SRomDecodeBlockGpr(Adapter, SRomEnd, Data);
1112                 BlockSize = 4;
1113             }
1114         }
1115         if (Status != NDIS_STATUS_SUCCESS)
1116             return Status;
1117 
1118         Data += BlockSize;
1119     }
1120 
1121     if ((Adapter->MediaBitmap == 0) && !(Adapter->Features & DC_HAS_MII))
1122     {
1123         WARN("No media information found\n");
1124     }
1125 
1126     return NDIS_STATUS_SUCCESS;
1127 }
1128 
1129 static
1130 CODE_SEG("PAGE")
1131 VOID
1132 SRomShiftOut(
1133     _In_ PDC21X4_ADAPTER Adapter,
1134     _In_ ULONG Sequence,
1135     _In_ ULONG BitCount)
1136 {
1137     LONG i;
1138 
1139     PAGED_CODE();
1140 
1141     for (i = BitCount - 1; i >= 0; --i)
1142     {
1143         ULONG DataIn = ((Sequence >> i) & 1) << DC_SERIAL_EE_DI_SHIFT;
1144 
1145         SROM_WRITE(Adapter, DataIn | DC_SERIAL_EE_RD | DC_SERIAL_EE_SR | DC_SERIAL_EE_CS);
1146         SROM_WRITE(Adapter, DataIn | DC_SERIAL_EE_RD | DC_SERIAL_EE_SR | DC_SERIAL_EE_CS |
1147                             DC_SERIAL_EE_SK);
1148         SROM_WRITE(Adapter, DataIn | DC_SERIAL_EE_RD | DC_SERIAL_EE_SR | DC_SERIAL_EE_CS);
1149     }
1150 }
1151 
1152 static
1153 CODE_SEG("PAGE")
1154 USHORT
1155 SRomShiftIn(
1156     _In_ PDC21X4_ADAPTER Adapter)
1157 {
1158     ULONG i, Csr;
1159     USHORT SerialData;
1160 
1161     PAGED_CODE();
1162 
1163     /* Shift the data out of the EEPROM */
1164     SerialData = 0;
1165     for (i = 0; i < RTL_BITS_OF(USHORT); ++i)
1166     {
1167         SROM_WRITE(Adapter, DC_SERIAL_EE_RD | DC_SERIAL_EE_SR | DC_SERIAL_EE_CS | DC_SERIAL_EE_SK);
1168 
1169         SROM_READ(Adapter, &Csr);
1170         SerialData = (SerialData << 1) | ((Csr >> DC_SERIAL_EE_DO_SHIFT) & 1);
1171 
1172         SROM_WRITE(Adapter, DC_SERIAL_EE_RD | DC_SERIAL_EE_SR | DC_SERIAL_EE_CS);
1173     }
1174 
1175     /* End the read cycle */
1176     SROM_WRITE(Adapter, DC_SERIAL_EE_RD | DC_SERIAL_EE_SR);
1177 
1178     return SerialData;
1179 }
1180 
1181 static
1182 CODE_SEG("PAGE")
1183 ULONG
1184 SRomDetectAddressBusWidth(
1185     _In_ PDC21X4_ADAPTER Adapter)
1186 {
1187     ULONG Csr, BusWidth;
1188 
1189     PAGED_CODE();
1190 
1191     /* Assume the SROM is a 1kB ROM, send the read command and zero address (6 bits) */
1192     SRomShiftOut(Adapter, EEPROM_CMD_READ << 6, EEPROM_CMD_LENGTH + 6);
1193 
1194     /* Check the preceding dummy zero bit */
1195     Csr = DC_READ(Adapter, DcCsr9_SerialInterface);
1196     if (Csr & DC_SERIAL_EE_DO)
1197     {
1198         /* 4kB EEPROM */
1199         BusWidth = 8;
1200 
1201         /* Send the remaining part of the address */
1202         SRomShiftOut(Adapter, 0, 8 - 6);
1203 
1204         /* The preceding dummy bit must be zero */
1205         Csr = DC_READ(Adapter, DcCsr9_SerialInterface);
1206         if (Csr & DC_SERIAL_EE_DO)
1207             return 0;
1208     }
1209     else
1210     {
1211         /* 1kB EEPROM */
1212         BusWidth = 6;
1213     }
1214 
1215     /* Complete the read cycle */
1216     (VOID)SRomShiftIn(Adapter);
1217 
1218     return BusWidth;
1219 }
1220 
1221 static
1222 CODE_SEG("PAGE")
1223 BOOLEAN
1224 SRomReadSRom(
1225     _In_ PDC21X4_ADAPTER Adapter,
1226     _Out_writes_all_(EE_SIZE) PVOID SRom)
1227 {
1228     PUSHORT SRomWord = SRom;
1229     BOOLEAN Success = TRUE;
1230     ULONG BusWidth, Address;
1231 
1232     PAGED_CODE();
1233 
1234     /* Select the device */
1235     SROM_WRITE(Adapter, DC_SERIAL_EE_RD | DC_SERIAL_EE_SR);
1236     SROM_WRITE(Adapter, DC_SERIAL_EE_RD | DC_SERIAL_EE_SR | DC_SERIAL_EE_CS);
1237 
1238     BusWidth = SRomDetectAddressBusWidth(Adapter);
1239     if (BusWidth == 0)
1240     {
1241         Success = FALSE;
1242         goto Done;
1243     }
1244     INFO("SROM Bus width: %u\n", BusWidth);
1245 
1246     /* Read the SROM contents once */
1247     for (Address = 0; Address < (EE_SIZE / sizeof(USHORT)); ++Address)
1248     {
1249         /* Send the command and address */
1250         SRomShiftOut(Adapter,
1251                      (EEPROM_CMD_READ << BusWidth) | Address,
1252                      EEPROM_CMD_LENGTH + BusWidth);
1253 
1254         /* Read the data */
1255         SRomWord[Address] = SRomShiftIn(Adapter);
1256     }
1257 
1258 Done:
1259     /* End chip select */
1260     DC_WRITE(Adapter, DcCsr9_SerialInterface, 0);
1261 
1262     return Success;
1263 }
1264 
1265 #if DBG
1266 static
1267 CODE_SEG("PAGE")
1268 VOID
1269 SRomDumpContents(
1270     _In_reads_bytes_(Length) const VOID* Buffer,
1271     _In_ ULONG Length)
1272 {
1273     ULONG Offset, Count, i;
1274     const UCHAR* Data = Buffer;
1275 
1276     PAGED_CODE();
1277 
1278     DbgPrint("SROM data:\n");
1279 
1280     Offset = 0;
1281     while (Offset < Length)
1282     {
1283         DbgPrint("%04x:\t", Offset);
1284 
1285         Count = min(Length - Offset, 16);
1286         for (i = 0; i < Count; ++i, ++Offset)
1287         {
1288             DbgPrint("0x%02x, ", Data[Offset], (i == 7) ? '-' : ' ');
1289         }
1290 
1291         DbgPrint("\n");
1292     }
1293 }
1294 #endif // DBG
1295 
1296 static
1297 CODE_SEG("PAGE")
1298 NDIS_STATUS
1299 SRomRead(
1300     _In_ PDC21X4_ADAPTER Adapter)
1301 {
1302     PDC_SROM_ENTRY SRomEntry;
1303     NDIS_STATUS Status;
1304     BOOLEAN ReleaseImage;
1305 
1306     PAGED_CODE();
1307 
1308     Status = NdisAllocateMemoryWithTag((PVOID*)&SRomEntry,
1309                                        FIELD_OFFSET(DC_SROM_ENTRY, SRomImage[EE_SIZE]),
1310                                        DC21X4_TAG);
1311     if (Status != NDIS_STATUS_SUCCESS)
1312         return NDIS_STATUS_RESOURCES;
1313     NdisZeroMemory(SRomEntry, FIELD_OFFSET(DC_SROM_ENTRY, SRomImage));
1314 
1315     ReleaseImage = FALSE;
1316 
1317     if (SRomReadSRom(Adapter, SRomEntry->SRomImage))
1318     {
1319         if (!SRomRegisterMasterAdapter(Adapter, SRomEntry))
1320             ReleaseImage = TRUE;
1321     }
1322     else
1323     {
1324         NdisFreeMemory(SRomEntry, 0, 0);
1325 
1326         if (!SRomFindMasterAdapter(Adapter, &SRomEntry))
1327         {
1328             ERR("Failed to retrieve the SROM contents\n");
1329             return NDIS_STATUS_FAILURE;
1330         }
1331     }
1332 
1333     Status = SRomParse(Adapter, SRomEntry->SRomImage);
1334     if (Status != NDIS_STATUS_SUCCESS)
1335     {
1336         ERR("Failed to parse SROM\n");
1337     }
1338 
1339 #if DBG
1340     if (Status != NDIS_STATUS_SUCCESS)
1341         SRomDumpContents(SRomEntry->SRomImage, EE_SIZE);
1342 #endif
1343 
1344     if (ReleaseImage)
1345         NdisFreeMemory(SRomEntry, 0, 0);
1346 
1347     return Status;
1348 }
1349 
1350 static
1351 CODE_SEG("PAGE")
1352 BOOLEAN
1353 AddressRomReadData(
1354     _In_ PDC21X4_ADAPTER Adapter,
1355     _Out_writes_all_(EAR_SIZE) PUCHAR AddressRom)
1356 {
1357     ULONG Data, i, j;
1358 
1359     PAGED_CODE();
1360 
1361     /* Reset the ROM pointer */
1362     DC_WRITE(Adapter, DcCsr9_SerialInterface, 0);
1363 
1364     for (i = 0; i < EAR_SIZE; ++i)
1365     {
1366         for (j = 10000; j > 0; --j)
1367         {
1368             NdisStallExecution(1);
1369             Data = DC_READ(Adapter, DcCsr9_SerialInterface);
1370 
1371             if (!(Data & DC_SERIAL_EAR_DN))
1372                 break;
1373         }
1374         AddressRom[i] = Data & DC_SERIAL_EAR_DT;
1375     }
1376 
1377     if (SRomIsEmpty(AddressRom, EAR_SIZE))
1378         return FALSE;
1379 
1380     return TRUE;
1381 }
1382 
1383 static
1384 CODE_SEG("PAGE")
1385 NDIS_STATUS
1386 AddressRomRead(
1387     _In_ PDC21X4_ADAPTER Adapter)
1388 {
1389     PDC_SROM_ENTRY SRomEntry;
1390     NDIS_STATUS Status;
1391     BOOLEAN ReleaseImage;
1392 
1393     PAGED_CODE();
1394 
1395     Status = NdisAllocateMemoryWithTag((PVOID*)&SRomEntry,
1396                                        FIELD_OFFSET(DC_SROM_ENTRY, SRomImage[EAR_SIZE]),
1397                                        DC21X4_TAG);
1398     if (Status != NDIS_STATUS_SUCCESS)
1399         return NDIS_STATUS_RESOURCES;
1400     NdisZeroMemory(SRomEntry, FIELD_OFFSET(DC_SROM_ENTRY, SRomImage));
1401 
1402     ReleaseImage = FALSE;
1403 
1404     if (AddressRomReadData(Adapter, SRomEntry->SRomImage))
1405     {
1406         if (!SRomRegisterMasterAdapter(Adapter, SRomEntry))
1407             ReleaseImage = TRUE;
1408     }
1409     else
1410     {
1411         NdisFreeMemory(SRomEntry, 0, 0);
1412 
1413         if (!SRomFindMasterAdapter(Adapter, &SRomEntry))
1414         {
1415             ERR("Failed to retrieve the EAR contents\n");
1416             return NDIS_STATUS_FAILURE;
1417         }
1418     }
1419 
1420     if (!SRomReadMacAddress(Adapter, SRomEntry->SRomImage, NULL))
1421     {
1422         ERR("Unable to read the MAC address\n");
1423         Status = NDIS_STATUS_FAILURE;
1424     }
1425 
1426     /* Update the base address on multiport boards */
1427     Adapter->PermanentMacAddress[5] += Adapter->ControllerIndex;
1428 
1429 #if DBG
1430     if (Status != NDIS_STATUS_SUCCESS)
1431         SRomDumpContents(SRomEntry->SRomImage, EAR_SIZE);
1432 #endif
1433 
1434     if (ReleaseImage)
1435         NdisFreeMemory(SRomEntry, 0, 0);
1436 
1437     return Status;
1438 }
1439 
1440 /* PUBLIC FUNCTIONS ***********************************************************/
1441 
1442 CODE_SEG("PAGE")
1443 VOID
1444 DcFreeEeprom(
1445     _In_ PDC21X4_ADAPTER Adapter)
1446 {
1447     PDC_SROM_ENTRY SRomEntry;
1448 
1449     PAGED_CODE();
1450 
1451     SRomEntry = Adapter->SRomEntry;
1452     if (!SRomEntry)
1453         return;
1454 
1455     SRomAcquireListMutex();
1456 
1457     /* Unregister the port */
1458     SRomEntry->DeviceBitmap &= ~(1 << Adapter->DeviceNumber);
1459 
1460     /*
1461      * Free the SROM as soon as the last registered port has removed.
1462      * We can't free it in an unload handler
1463      * as the bus numbers can be changed by a resource rebalance.
1464      */
1465     if (SRomEntry->DeviceBitmap == 0)
1466     {
1467         INFO("Freeing SROM %p at %u:%u\n",
1468              SRomEntry,
1469              SRomEntry->BusNumber,
1470              SRomEntry->DeviceNumber);
1471 
1472         RemoveEntryList(&SRomEntry->ListEntry);
1473 
1474         NdisFreeMemory(SRomEntry, 0, 0);
1475     }
1476 
1477     SRomReleaseListMutex();
1478 }
1479 
1480 CODE_SEG("PAGE")
1481 NDIS_STATUS
1482 DcReadEeprom(
1483     _In_ PDC21X4_ADAPTER Adapter)
1484 {
1485     NDIS_STATUS Status;
1486 
1487     PAGED_CODE();
1488 
1489     if (Adapter->ChipType == DC21040)
1490     {
1491         /* Ethernet Address ROM */
1492         Status = AddressRomRead(Adapter);
1493     }
1494     else
1495     {
1496         /* MicroWire Compatible Serial EEPROM */
1497         Status = SRomRead(Adapter);
1498     }
1499 
1500     if (Status != NDIS_STATUS_SUCCESS)
1501         return Status;
1502 
1503     INFO("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
1504          Adapter->PermanentMacAddress[0],
1505          Adapter->PermanentMacAddress[1],
1506          Adapter->PermanentMacAddress[2],
1507          Adapter->PermanentMacAddress[3],
1508          Adapter->PermanentMacAddress[4],
1509          Adapter->PermanentMacAddress[5]);
1510 
1511     if (ETH_IS_BROADCAST(Adapter->PermanentMacAddress) ||
1512         ETH_IS_EMPTY(Adapter->PermanentMacAddress) ||
1513         ETH_IS_MULTICAST(Adapter->PermanentMacAddress))
1514     {
1515         ERR("Invalid permanent MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
1516             Adapter->PermanentMacAddress[0],
1517             Adapter->PermanentMacAddress[1],
1518             Adapter->PermanentMacAddress[2],
1519             Adapter->PermanentMacAddress[3],
1520             Adapter->PermanentMacAddress[4],
1521             Adapter->PermanentMacAddress[5]);
1522 
1523         NdisWriteErrorLogEntry(Adapter->AdapterHandle, NDIS_ERROR_CODE_NETWORK_ADDRESS, 0);
1524 
1525         return NDIS_STATUS_INVALID_ADDRESS;
1526     }
1527 
1528     return NDIS_STATUS_SUCCESS;
1529 }
1530