xref: /reactos/drivers/network/dd/dc21x4/eeprom.c (revision 3fb5957d)
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     NdisMoveMemory(&TestPatterm, &Octet[24], 8);
863     if (TestPatterm != EAR_TEST_PATTERN)
864         return FALSE;
865 
866     for (i = 0; i < 8; ++i)
867     {
868         if (Octet[i] != Octet[15 - i])
869             return FALSE;
870     }
871 
872     Checksum = (Octet[0] << 10) + (Octet[2] << 9) + (Octet[4] << 8) +
873                (Octet[1] << 2) + (Octet[3] << 1) + Octet[5];
874     Checksum %= 0xFFFF;
875 
876     return ((USHORT)Checksum == ((Octet[6] << 8) | Octet[7]));
877 }
878 
879 static
880 CODE_SEG("PAGE")
881 BOOLEAN
882 SRomReadMacAddress(
883     _In_ PDC21X4_ADAPTER Adapter,
884     _In_ PUCHAR SRom,
885     _Out_opt_ PULONG AddressOffset)
886 {
887     ULONG MacOffset;
888 
889     /* Check if we have a board with an old EAR format */
890     if (NdisEqualMemory(SRom, &SRom[16], 8))
891     {
892         /* Validate the EAR checksum */
893         if (!AddressRomChecksumValid(SRom))
894         {
895             ERR("EAR has an invalid checksum\n");
896             return FALSE;
897         }
898 
899         MacOffset = 0;
900         goto ReadMac;
901     }
902 
903     /* Check for a new SROM format */
904     if (Adapter->ChipType != DC21040)
905     {
906         /* Validate the SROM checksum */
907         if (SRomChecksumValid(SRom))
908         {
909             MacOffset = SROM_MAC_ADDRESS;
910             goto ReadMac;
911         }
912     }
913 
914     /* Sanity check */
915     if (*(PULONG)SRom == 0xFFFFFFF || *(PULONG)SRom == 0)
916         return FALSE;
917 
918     WARN("Legacy/unknown board found\n");
919     MacOffset = 0;
920 
921 ReadMac:
922     if (AddressOffset)
923         *AddressOffset = MacOffset;
924 
925     NdisMoveMemory(Adapter->PermanentMacAddress,
926                    &SRom[MacOffset],
927                    ETH_LENGTH_OF_ADDRESS);
928 
929     return TRUE;
930 }
931 
932 static
933 CODE_SEG("PAGE")
934 NDIS_STATUS
935 SRomParseHeader(
936     _In_ PDC21X4_ADAPTER Adapter,
937     _In_ PUCHAR SRom,
938     _Out_ PUCHAR* InfoLeaf,
939     _Out_ PUCHAR* SRomEnd)
940 {
941     ULONG i, MacOffset, LeafOffset;
942 
943     PAGED_CODE();
944 
945     if (!SRomReadMacAddress(Adapter, SRom, &MacOffset))
946     {
947         ERR("Unable to read the MAC address\n");
948         return NDIS_STATUS_FAILURE;
949     }
950 
951     /* Assign our own fake info leaf */
952     if (MacOffset != SROM_MAC_ADDRESS)
953     {
954         for (i = 0; SRompRepairData[i].InfoLeaf; ++i)
955         {
956             /* Check for a MAC match */
957             if (NdisEqualMemory(SRompRepairData[i].InfoLeaf, &Adapter->PermanentMacAddress, 3))
958             {
959                 /* This check is used to distinguish Accton EN1207 from Maxtech */
960                 if ((Adapter->PermanentMacAddress[0] == 0xE8) && (SRom[0x1A] == 0x55))
961                     ++i;
962 
963                 break;
964             }
965         }
966         if (!SRompRepairData[i].InfoLeaf)
967         {
968             ERR("Non-standard SROM format, OUI %02x:%02x:%02x\n",
969                 Adapter->PermanentMacAddress[0],
970                 Adapter->PermanentMacAddress[1],
971                 Adapter->PermanentMacAddress[2]);
972 
973             return NDIS_STATUS_ADAPTER_NOT_FOUND;
974         }
975 
976         *InfoLeaf = &SRompRepairData[i].InfoLeaf[3];
977         *SRomEnd = *InfoLeaf + SRompRepairData[i].Length;
978 
979         /* Update the base address on multiport boards */
980         Adapter->PermanentMacAddress[5] += Adapter->ControllerIndex;
981 
982 #if DBG
983         WARN("Non-standard SROM format, using '%s' info leaf\n", SRompRepairData[i].Name);
984 #endif
985         return STATUS_SUCCESS;
986     }
987 
988     /* Check if the SROM chip is shared between multiple controllers on a multiport board */
989     if (SRom[SROM_CONTROLLER_COUNT] > 1)
990     {
991         INFO("Multiport board, controller number %u (%u/%u)\n",
992              Adapter->DeviceNumber,
993              Adapter->ControllerIndex + 1,
994              SRom[SROM_CONTROLLER_COUNT]);
995 
996         for (i = 0; i < SRom[SROM_CONTROLLER_COUNT]; ++i)
997         {
998             if (SROM_DEVICE_NUMBER(i) >= EE_SIZE)
999                 return NDIS_STATUS_BUFFER_OVERFLOW;
1000 
1001             if (SRom[SROM_DEVICE_NUMBER(i)] == Adapter->DeviceNumber)
1002                 break;
1003         }
1004         if (i == SRom[SROM_CONTROLLER_COUNT])
1005         {
1006             ERR("Controller %u was not found in the SROM\n", Adapter->DeviceNumber);
1007             return NDIS_STATUS_ADAPTER_NOT_FOUND;
1008         }
1009 
1010         if (SROM_LEAF_OFFSET(i) >= EE_SIZE)
1011             return NDIS_STATUS_BUFFER_OVERFLOW;
1012 
1013         /* Update the base address */
1014         Adapter->PermanentMacAddress[5] += i;
1015     }
1016     else
1017     {
1018         i = 0;
1019     }
1020 
1021     /* Controller info block offset */
1022     LeafOffset = DcRetrieveWord(SRom + SROM_LEAF_OFFSET(i));
1023     if (LeafOffset > (EE_SIZE - sizeof(DC_SROM_COMPACT_BLOCK)))
1024         return NDIS_STATUS_BUFFER_OVERFLOW;
1025 
1026     /* Controller info leaf */
1027     *InfoLeaf = &SRom[LeafOffset];
1028 
1029     *SRomEnd = SRom + EE_SIZE;
1030 
1031     return STATUS_SUCCESS;
1032 }
1033 
1034 static
1035 CODE_SEG("PAGE")
1036 NDIS_STATUS
1037 SRomParse(
1038     _In_ PDC21X4_ADAPTER Adapter,
1039     _In_ PUCHAR SRom)
1040 {
1041     ULONG Index, BlockCount, BlockSize, DefaultMedia;
1042     NDIS_STATUS Status;
1043     USHORT GpioCtrl;
1044     PUCHAR Data, SRomEnd;
1045 
1046     PAGED_CODE();
1047 
1048     INFO("SROM Version %u, Controller count %u\n",
1049          SRom[SROM_VERSION],
1050          SRom[SROM_CONTROLLER_COUNT]);
1051 
1052     Status = SRomParseHeader(Adapter, SRom, &Data, &SRomEnd);
1053     if (Status != NDIS_STATUS_SUCCESS)
1054         return Status;
1055 
1056     DefaultMedia = DcRetrieveWord(Data);
1057     Data += sizeof(USHORT);
1058 
1059     INFO("Default Media:  %04lx\n", DefaultMedia);
1060 
1061     /* Direction of the GPIO pins */
1062     if (Adapter->ChipType == DC21140)
1063     {
1064         GpioCtrl = *Data++;
1065 
1066         INFO("GPIO Direction: %04lx\n", GpioCtrl);
1067 
1068         GpioCtrl |= DC_GPIO_CONTROL;
1069 
1070         for (Index = 0; Index < MEDIA_LIST_MAX; ++Index)
1071         {
1072             Adapter->Media[Index].GpioCtrl = GpioCtrl;
1073         }
1074 
1075         /* Control word for block type 1 */
1076         Adapter->MiiMedia.SetupStream[0] = GpioCtrl;
1077     }
1078 
1079     BlockCount = *Data++;
1080 
1081     INFO("Block Count:    %u\n", BlockCount);
1082 
1083     if (BlockCount == 0 || BlockCount == 0xFF)
1084     {
1085         WARN("No media information found\n");
1086         return NDIS_STATUS_SUCCESS;
1087     }
1088 
1089     /* Analyze and decode blocks */
1090     for (Index = 0; Index < BlockCount; ++Index)
1091     {
1092         if (Adapter->ChipType == DC21041)
1093         {
1094             Status = SRomParse21041Block(Adapter, SRomEnd, Data, &BlockSize);
1095         }
1096         else
1097         {
1098             if (Data > (SRomEnd - 1))
1099                 return NDIS_STATUS_BUFFER_OVERFLOW;
1100 
1101             if (SRomIsBlockExtended(*Data))
1102             {
1103                 Status = SRomParseExtendedBlock(Adapter, SRomEnd, Data, &BlockSize);
1104             }
1105             else
1106             {
1107                 Status = SRomDecodeBlockGpr(Adapter, SRomEnd, Data);
1108                 BlockSize = 4;
1109             }
1110         }
1111         if (Status != NDIS_STATUS_SUCCESS)
1112             return Status;
1113 
1114         Data += BlockSize;
1115     }
1116 
1117     if ((Adapter->MediaBitmap == 0) && !(Adapter->Features & DC_HAS_MII))
1118     {
1119         WARN("No media information found\n");
1120     }
1121 
1122     return NDIS_STATUS_SUCCESS;
1123 }
1124 
1125 static
1126 CODE_SEG("PAGE")
1127 VOID
1128 SRomShiftOut(
1129     _In_ PDC21X4_ADAPTER Adapter,
1130     _In_ ULONG Sequence,
1131     _In_ ULONG BitCount)
1132 {
1133     LONG i;
1134 
1135     PAGED_CODE();
1136 
1137     for (i = BitCount - 1; i >= 0; --i)
1138     {
1139         ULONG DataIn = ((Sequence >> i) & 1) << DC_SERIAL_EE_DI_SHIFT;
1140 
1141         SROM_WRITE(Adapter, DataIn | DC_SERIAL_EE_RD | DC_SERIAL_EE_SR | DC_SERIAL_EE_CS);
1142         SROM_WRITE(Adapter, DataIn | DC_SERIAL_EE_RD | DC_SERIAL_EE_SR | DC_SERIAL_EE_CS |
1143                             DC_SERIAL_EE_SK);
1144         SROM_WRITE(Adapter, DataIn | DC_SERIAL_EE_RD | DC_SERIAL_EE_SR | DC_SERIAL_EE_CS);
1145     }
1146 }
1147 
1148 static
1149 CODE_SEG("PAGE")
1150 USHORT
1151 SRomShiftIn(
1152     _In_ PDC21X4_ADAPTER Adapter)
1153 {
1154     ULONG i, Csr;
1155     USHORT SerialData;
1156 
1157     PAGED_CODE();
1158 
1159     /* Shift the data out of the EEPROM */
1160     SerialData = 0;
1161     for (i = 0; i < RTL_BITS_OF(USHORT); ++i)
1162     {
1163         SROM_WRITE(Adapter, DC_SERIAL_EE_RD | DC_SERIAL_EE_SR | DC_SERIAL_EE_CS | DC_SERIAL_EE_SK);
1164 
1165         SROM_READ(Adapter, &Csr);
1166         SerialData = (SerialData << 1) | ((Csr >> DC_SERIAL_EE_DO_SHIFT) & 1);
1167 
1168         SROM_WRITE(Adapter, DC_SERIAL_EE_RD | DC_SERIAL_EE_SR | DC_SERIAL_EE_CS);
1169     }
1170 
1171     /* End the read cycle */
1172     SROM_WRITE(Adapter, DC_SERIAL_EE_RD | DC_SERIAL_EE_SR);
1173 
1174     return SerialData;
1175 }
1176 
1177 static
1178 CODE_SEG("PAGE")
1179 ULONG
1180 SRomDetectAddressBusWidth(
1181     _In_ PDC21X4_ADAPTER Adapter)
1182 {
1183     ULONG Csr, BusWidth;
1184 
1185     PAGED_CODE();
1186 
1187     /* Assume the SROM is a 1kB ROM, send the read command and zero address (6 bits) */
1188     SRomShiftOut(Adapter, EEPROM_CMD_READ << 6, EEPROM_CMD_LENGTH + 6);
1189 
1190     /* Check the preceding dummy zero bit */
1191     Csr = DC_READ(Adapter, DcCsr9_SerialInterface);
1192     if (Csr & DC_SERIAL_EE_DO)
1193     {
1194         /* 4kB EEPROM */
1195         BusWidth = 8;
1196 
1197         /* Send the remaining part of the address */
1198         SRomShiftOut(Adapter, 0, 8 - 6);
1199 
1200         /* The preceding dummy bit must be zero */
1201         Csr = DC_READ(Adapter, DcCsr9_SerialInterface);
1202         if (Csr & DC_SERIAL_EE_DO)
1203             return 0;
1204     }
1205     else
1206     {
1207         /* 1kB EEPROM */
1208         BusWidth = 6;
1209     }
1210 
1211     /* Complete the read cycle */
1212     (VOID)SRomShiftIn(Adapter);
1213 
1214     return BusWidth;
1215 }
1216 
1217 static
1218 CODE_SEG("PAGE")
1219 BOOLEAN
1220 SRomReadSRom(
1221     _In_ PDC21X4_ADAPTER Adapter,
1222     _Out_writes_all_(EE_SIZE) PVOID SRom)
1223 {
1224     PUSHORT SRomWord = SRom;
1225     BOOLEAN Success = TRUE;
1226     ULONG BusWidth, Address;
1227 
1228     PAGED_CODE();
1229 
1230     /* Select the device */
1231     SROM_WRITE(Adapter, DC_SERIAL_EE_RD | DC_SERIAL_EE_SR);
1232     SROM_WRITE(Adapter, DC_SERIAL_EE_RD | DC_SERIAL_EE_SR | DC_SERIAL_EE_CS);
1233 
1234     BusWidth = SRomDetectAddressBusWidth(Adapter);
1235     if (BusWidth == 0)
1236     {
1237         Success = FALSE;
1238         goto Done;
1239     }
1240     INFO("SROM Bus width: %u\n", BusWidth);
1241 
1242     /* Read the SROM contents once */
1243     for (Address = 0; Address < (EE_SIZE / sizeof(USHORT)); ++Address)
1244     {
1245         /* Send the command and address */
1246         SRomShiftOut(Adapter,
1247                      (EEPROM_CMD_READ << BusWidth) | Address,
1248                      EEPROM_CMD_LENGTH + BusWidth);
1249 
1250         /* Read the data */
1251         SRomWord[Address] = SRomShiftIn(Adapter);
1252     }
1253 
1254 Done:
1255     /* End chip select */
1256     DC_WRITE(Adapter, DcCsr9_SerialInterface, 0);
1257 
1258     return Success;
1259 }
1260 
1261 #if DBG
1262 static
1263 CODE_SEG("PAGE")
1264 VOID
1265 SRomDumpContents(
1266     _In_reads_bytes_(Length) const VOID* Buffer,
1267     _In_ ULONG Length)
1268 {
1269     ULONG Offset, Count, i;
1270     const UCHAR* Data = Buffer;
1271 
1272     PAGED_CODE();
1273 
1274     DbgPrint("SROM data:\n");
1275 
1276     Offset = 0;
1277     while (Offset < Length)
1278     {
1279         DbgPrint("%04x:\t", Offset);
1280 
1281         Count = min(Length - Offset, 16);
1282         for (i = 0; i < Count; ++i, ++Offset)
1283         {
1284             DbgPrint("0x%02x, ", Data[Offset], (i == 7) ? '-' : ' ');
1285         }
1286 
1287         DbgPrint("\n");
1288     }
1289 }
1290 #endif // DBG
1291 
1292 static
1293 CODE_SEG("PAGE")
1294 NDIS_STATUS
1295 SRomRead(
1296     _In_ PDC21X4_ADAPTER Adapter)
1297 {
1298     PDC_SROM_ENTRY SRomEntry;
1299     NDIS_STATUS Status;
1300     BOOLEAN ReleaseImage;
1301 
1302     PAGED_CODE();
1303 
1304     Status = NdisAllocateMemoryWithTag((PVOID*)&SRomEntry,
1305                                        FIELD_OFFSET(DC_SROM_ENTRY, SRomImage[EE_SIZE]),
1306                                        DC21X4_TAG);
1307     if (Status != NDIS_STATUS_SUCCESS)
1308         return NDIS_STATUS_RESOURCES;
1309     NdisZeroMemory(SRomEntry, FIELD_OFFSET(DC_SROM_ENTRY, SRomImage));
1310 
1311     ReleaseImage = FALSE;
1312 
1313     if (SRomReadSRom(Adapter, SRomEntry->SRomImage))
1314     {
1315         if (!SRomRegisterMasterAdapter(Adapter, SRomEntry))
1316             ReleaseImage = TRUE;
1317     }
1318     else
1319     {
1320         NdisFreeMemory(SRomEntry, 0, 0);
1321 
1322         if (!SRomFindMasterAdapter(Adapter, &SRomEntry))
1323         {
1324             ERR("Failed to retrieve the SROM contents\n");
1325             return NDIS_STATUS_FAILURE;
1326         }
1327     }
1328 
1329     Status = SRomParse(Adapter, SRomEntry->SRomImage);
1330     if (Status != NDIS_STATUS_SUCCESS)
1331     {
1332         ERR("Failed to parse SROM\n");
1333     }
1334 
1335 #if DBG
1336     if (Status != NDIS_STATUS_SUCCESS)
1337         SRomDumpContents(SRomEntry->SRomImage, EE_SIZE);
1338 #endif
1339 
1340     if (ReleaseImage)
1341         NdisFreeMemory(SRomEntry, 0, 0);
1342 
1343     return Status;
1344 }
1345 
1346 static
1347 CODE_SEG("PAGE")
1348 BOOLEAN
1349 AddressRomReadData(
1350     _In_ PDC21X4_ADAPTER Adapter,
1351     _Out_writes_all_(EAR_SIZE) PUCHAR AddressRom)
1352 {
1353     ULONG Data, i, j;
1354 
1355     PAGED_CODE();
1356 
1357     /* Reset the ROM pointer */
1358     DC_WRITE(Adapter, DcCsr9_SerialInterface, 0);
1359 
1360     for (i = 0; i < EAR_SIZE; ++i)
1361     {
1362         for (j = 10000; j > 0; --j)
1363         {
1364             NdisStallExecution(1);
1365             Data = DC_READ(Adapter, DcCsr9_SerialInterface);
1366 
1367             if (!(Data & DC_SERIAL_EAR_DN))
1368                 break;
1369         }
1370         AddressRom[i] = Data & DC_SERIAL_EAR_DT;
1371     }
1372 
1373     if (SRomIsEmpty(AddressRom, EAR_SIZE))
1374         return FALSE;
1375 
1376     return TRUE;
1377 }
1378 
1379 static
1380 CODE_SEG("PAGE")
1381 NDIS_STATUS
1382 AddressRomRead(
1383     _In_ PDC21X4_ADAPTER Adapter)
1384 {
1385     PDC_SROM_ENTRY SRomEntry;
1386     NDIS_STATUS Status;
1387     BOOLEAN ReleaseImage;
1388 
1389     PAGED_CODE();
1390 
1391     Status = NdisAllocateMemoryWithTag((PVOID*)&SRomEntry,
1392                                        FIELD_OFFSET(DC_SROM_ENTRY, SRomImage[EAR_SIZE]),
1393                                        DC21X4_TAG);
1394     if (Status != NDIS_STATUS_SUCCESS)
1395         return NDIS_STATUS_RESOURCES;
1396     NdisZeroMemory(SRomEntry, FIELD_OFFSET(DC_SROM_ENTRY, SRomImage));
1397 
1398     ReleaseImage = FALSE;
1399 
1400     if (AddressRomReadData(Adapter, SRomEntry->SRomImage))
1401     {
1402         if (!SRomRegisterMasterAdapter(Adapter, SRomEntry))
1403             ReleaseImage = TRUE;
1404     }
1405     else
1406     {
1407         NdisFreeMemory(SRomEntry, 0, 0);
1408 
1409         if (!SRomFindMasterAdapter(Adapter, &SRomEntry))
1410         {
1411             ERR("Failed to retrieve the EAR contents\n");
1412             return NDIS_STATUS_FAILURE;
1413         }
1414     }
1415 
1416     if (!SRomReadMacAddress(Adapter, SRomEntry->SRomImage, NULL))
1417     {
1418         ERR("Unable to read the MAC address\n");
1419         Status = NDIS_STATUS_FAILURE;
1420     }
1421 
1422     /* Update the base address on multiport boards */
1423     Adapter->PermanentMacAddress[5] += Adapter->ControllerIndex;
1424 
1425 #if DBG
1426     if (Status != NDIS_STATUS_SUCCESS)
1427         SRomDumpContents(SRomEntry->SRomImage, EAR_SIZE);
1428 #endif
1429 
1430     if (ReleaseImage)
1431         NdisFreeMemory(SRomEntry, 0, 0);
1432 
1433     return Status;
1434 }
1435 
1436 /* PUBLIC FUNCTIONS ***********************************************************/
1437 
1438 CODE_SEG("PAGE")
1439 VOID
1440 DcFreeEeprom(
1441     _In_ PDC21X4_ADAPTER Adapter)
1442 {
1443     PDC_SROM_ENTRY SRomEntry;
1444 
1445     PAGED_CODE();
1446 
1447     SRomEntry = Adapter->SRomEntry;
1448     if (!SRomEntry)
1449         return;
1450 
1451     SRomAcquireListMutex();
1452 
1453     /* Unregister the port */
1454     SRomEntry->DeviceBitmap &= ~(1 << Adapter->DeviceNumber);
1455 
1456     /*
1457      * Free the SROM as soon as the last registered port has removed.
1458      * We can't free it in an unload handler
1459      * as the bus numbers can be changed by a resource rebalance.
1460      */
1461     if (SRomEntry->DeviceBitmap == 0)
1462     {
1463         INFO("Freeing SROM %p at %u:%u\n",
1464              SRomEntry,
1465              SRomEntry->BusNumber,
1466              SRomEntry->DeviceNumber);
1467 
1468         RemoveEntryList(&SRomEntry->ListEntry);
1469 
1470         NdisFreeMemory(SRomEntry, 0, 0);
1471     }
1472 
1473     SRomReleaseListMutex();
1474 }
1475 
1476 CODE_SEG("PAGE")
1477 NDIS_STATUS
1478 DcReadEeprom(
1479     _In_ PDC21X4_ADAPTER Adapter)
1480 {
1481     NDIS_STATUS Status;
1482 
1483     PAGED_CODE();
1484 
1485     if (Adapter->ChipType == DC21040)
1486     {
1487         /* Ethernet Address ROM */
1488         Status = AddressRomRead(Adapter);
1489     }
1490     else
1491     {
1492         /* MicroWire Compatible Serial EEPROM */
1493         Status = SRomRead(Adapter);
1494     }
1495 
1496     if (Status != NDIS_STATUS_SUCCESS)
1497         return Status;
1498 
1499     INFO("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
1500          Adapter->PermanentMacAddress[0],
1501          Adapter->PermanentMacAddress[1],
1502          Adapter->PermanentMacAddress[2],
1503          Adapter->PermanentMacAddress[3],
1504          Adapter->PermanentMacAddress[4],
1505          Adapter->PermanentMacAddress[5]);
1506 
1507     if (ETH_IS_BROADCAST(Adapter->PermanentMacAddress) ||
1508         ETH_IS_EMPTY(Adapter->PermanentMacAddress) ||
1509         ETH_IS_MULTICAST(Adapter->PermanentMacAddress))
1510     {
1511         ERR("Invalid permanent MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
1512             Adapter->PermanentMacAddress[0],
1513             Adapter->PermanentMacAddress[1],
1514             Adapter->PermanentMacAddress[2],
1515             Adapter->PermanentMacAddress[3],
1516             Adapter->PermanentMacAddress[4],
1517             Adapter->PermanentMacAddress[5]);
1518 
1519         NdisWriteErrorLogEntry(Adapter->AdapterHandle, NDIS_ERROR_CODE_NETWORK_ADDRESS, 0);
1520 
1521         return NDIS_STATUS_INVALID_ADDRESS;
1522     }
1523 
1524     return NDIS_STATUS_SUCCESS;
1525 }
1526