Patch to remove STATIC modifier. This is on longer recommended by EFI Framework codin...
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / UndiRuntimeDxe / E100b.c
1 /** @file\r
2   Provides basic function upon network adapter card.\r
3 \r
4 Copyright (c) 2006, Intel Corporation\r
5 All rights reserved. This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution.  The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9 \r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12 \r
13 **/\r
14 \r
15 #include "Undi32.h"\r
16 \r
17 UINT8 basic_config_cmd[22] = {\r
18                     22,        0x08,\r
19                     0,           0,\r
20                     0, (UINT8)0x80,\r
21                     0x32,        0x03,\r
22                     1,            0,\r
23                     0x2E,           0,\r
24                     0x60,           0,\r
25                     (UINT8)0xf2,        0x48,\r
26                     0,        0x40,\r
27                     (UINT8)0xf2, (UINT8)0x80, // 0x40=Force full-duplex\r
28                     0x3f,       0x05,\r
29 };\r
30 \r
31 //\r
32 // How to wait for the command unit to accept a command.\r
33 // Typically this takes 0 ticks.\r
34 //\r
35 #define wait_for_cmd_done(cmd_ioaddr) \\r
36 {                      \\r
37   INT16 wait_count = 2000;              \\r
38   while ((InByte (AdapterInfo, cmd_ioaddr) != 0) && --wait_count >= 0)  \\r
39     DelayIt (AdapterInfo, 10);  \\r
40   if (wait_count == 0) \\r
41     DelayIt (AdapterInfo, 50);    \\r
42 }\r
43 \r
44 \r
45 /**\r
46   This function calls the MemIo callback to read a byte from the device's\r
47   address space\r
48   Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine)\r
49   which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have\r
50   to make undi3.0 a special case\r
51 \r
52   @param  Port                            Which port to read from.\r
53 \r
54   @retval Results                         The data read from the port.\r
55 \r
56 **/\r
57 // TODO:    AdapterInfo - add argument and description to function comment\r
58 UINT8\r
59 InByte (\r
60   IN NIC_DATA_INSTANCE *AdapterInfo,\r
61   IN UINT32            Port\r
62   )\r
63 {\r
64   UINT8 Results;\r
65 \r
66   (*AdapterInfo->Mem_Io) (\r
67     AdapterInfo->Unique_ID,\r
68     PXE_MEM_READ,\r
69     1,\r
70     (UINT64)Port,\r
71     (UINT64) (UINTN) &Results\r
72     );\r
73   return Results;\r
74 }\r
75 \r
76 \r
77 /**\r
78   This function calls the MemIo callback to read a word from the device's\r
79   address space\r
80   Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine)\r
81   which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have\r
82   to make undi3.0 a special case\r
83 \r
84   @param  Port                            Which port to read from.\r
85 \r
86   @retval Results                         The data read from the port.\r
87 \r
88 **/\r
89 // TODO:    AdapterInfo - add argument and description to function comment\r
90 UINT16\r
91 InWord (\r
92   IN NIC_DATA_INSTANCE *AdapterInfo,\r
93   IN UINT32            Port\r
94   )\r
95 {\r
96   UINT16  Results;\r
97 \r
98   (*AdapterInfo->Mem_Io) (\r
99     AdapterInfo->Unique_ID,\r
100     PXE_MEM_READ,\r
101     2,\r
102     (UINT64)Port,\r
103     (UINT64)(UINTN)&Results\r
104     );\r
105   return Results;\r
106 }\r
107 \r
108 \r
109 /**\r
110   This function calls the MemIo callback to read a dword from the device's\r
111   address space\r
112   Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine)\r
113   which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have\r
114   to make undi3.0 a special case\r
115 \r
116   @param  Port                            Which port to read from.\r
117 \r
118   @retval Results                         The data read from the port.\r
119 \r
120 **/\r
121 // TODO:    AdapterInfo - add argument and description to function comment\r
122 UINT32\r
123 InLong (\r
124   IN NIC_DATA_INSTANCE *AdapterInfo,\r
125   IN UINT32            Port\r
126   )\r
127 {\r
128   UINT32  Results;\r
129 \r
130   (*AdapterInfo->Mem_Io) (\r
131     AdapterInfo->Unique_ID,\r
132     PXE_MEM_READ,\r
133     4,\r
134     (UINT64)Port,\r
135     (UINT64)(UINTN)&Results\r
136     );\r
137   return Results;\r
138 }\r
139 \r
140 \r
141 /**\r
142   This function calls the MemIo callback to write a byte from the device's\r
143   address space\r
144   Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine)\r
145   which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have\r
146   to make undi3.0 a special case\r
147 \r
148   @param  Data                            Data to write to Port.\r
149   @param  Port                            Which port to write to.\r
150 \r
151   @return none\r
152 \r
153 **/\r
154 // TODO:    AdapterInfo - add argument and description to function comment\r
155 VOID\r
156 OutByte (\r
157   IN NIC_DATA_INSTANCE *AdapterInfo,\r
158   IN UINT8             Data,\r
159   IN UINT32            Port\r
160   )\r
161 {\r
162   UINT8 Val;\r
163 \r
164   Val = Data;\r
165   (*AdapterInfo->Mem_Io) (\r
166      AdapterInfo->Unique_ID,\r
167      PXE_MEM_WRITE,\r
168      1,\r
169      (UINT64)Port,\r
170      (UINT64)(UINTN)(UINTN)&Val\r
171      );\r
172   return ;\r
173 }\r
174 \r
175 \r
176 /**\r
177   This function calls the MemIo callback to write a word from the device's\r
178   address space\r
179   Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine)\r
180   which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have\r
181   to make undi3.0 a special case\r
182 \r
183   @param  Data                            Data to write to Port.\r
184   @param  Port                            Which port to write to.\r
185 \r
186   @return none\r
187 \r
188 **/\r
189 // TODO:    AdapterInfo - add argument and description to function comment\r
190 VOID\r
191 OutWord (\r
192   IN NIC_DATA_INSTANCE *AdapterInfo,\r
193   IN UINT16            Data,\r
194   IN UINT32            Port\r
195   )\r
196 {\r
197   UINT16  Val;\r
198 \r
199   Val = Data;\r
200   (*AdapterInfo->Mem_Io) (\r
201      AdapterInfo->Unique_ID,\r
202      PXE_MEM_WRITE,\r
203      2,\r
204      (UINT64)Port,\r
205      (UINT64)(UINTN)&Val\r
206      );\r
207   return ;\r
208 }\r
209 \r
210 \r
211 /**\r
212   This function calls the MemIo callback to write a dword from the device's\r
213   address space\r
214   Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine)\r
215   which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have\r
216   to make undi3.0 a special case\r
217 \r
218   @param  Data                            Data to write to Port.\r
219   @param  Port                            Which port to write to.\r
220 \r
221   @return none\r
222 \r
223 **/\r
224 // TODO:    AdapterInfo - add argument and description to function comment\r
225 VOID\r
226 OutLong (\r
227   IN NIC_DATA_INSTANCE *AdapterInfo,\r
228   IN UINT32            Data,\r
229   IN UINT32            Port\r
230   )\r
231 {\r
232   UINT32  Val;\r
233 \r
234   Val = Data;\r
235   (*AdapterInfo->Mem_Io) (\r
236      AdapterInfo->Unique_ID,\r
237      PXE_MEM_WRITE,\r
238      4,\r
239      (UINT64)Port,\r
240      (UINT64)(UINTN)&Val\r
241      );\r
242   return ;\r
243 }\r
244 \r
245 \r
246 /**\r
247   TODO: Add function description\r
248 \r
249   @param  AdapterInfo                     TODO: add argument description\r
250   @param  MemAddr                         TODO: add argument description\r
251   @param  Size                            TODO: add argument description\r
252   @param  Direction                       TODO: add argument description\r
253   @param  MappedAddr                      TODO: add argument description\r
254 \r
255   @return TODO: add return values\r
256 \r
257 **/\r
258 UINTN\r
259 MapIt (\r
260   IN NIC_DATA_INSTANCE *AdapterInfo,\r
261   IN UINT64            MemAddr,\r
262   IN UINT32            Size,\r
263   IN UINT32            Direction,\r
264   OUT UINT64           MappedAddr\r
265   )\r
266 {\r
267   UINT64  *PhyAddr;\r
268 \r
269   PhyAddr = (UINT64 *) (UINTN) MappedAddr;\r
270   //\r
271   // mapping is different for theold and new NII protocols\r
272   //\r
273   if (AdapterInfo->VersionFlag == 0x30) {\r
274     if (AdapterInfo->Virt2Phys_30 == (VOID *) NULL) {\r
275       *PhyAddr = (UINT64) AdapterInfo->MemoryPtr;\r
276     } else {\r
277       (*AdapterInfo->Virt2Phys_30) (MemAddr, (UINT64) (UINTN) PhyAddr);\r
278     }\r
279 \r
280     if (*PhyAddr > FOUR_GIGABYTE) {\r
281       return PXE_STATCODE_INVALID_PARAMETER;\r
282     }\r
283   } else {\r
284     if (AdapterInfo->Map_Mem == (VOID *) NULL) {\r
285       //\r
286       // this UNDI cannot handle addresses beyond 4 GB without a map routine\r
287       //\r
288       if (MemAddr > FOUR_GIGABYTE) {\r
289         return PXE_STATCODE_INVALID_PARAMETER;\r
290       } else {\r
291         *PhyAddr = MemAddr;\r
292       }\r
293     } else {\r
294       (*AdapterInfo->Map_Mem) (\r
295         AdapterInfo->Unique_ID,\r
296         MemAddr,\r
297         Size,\r
298         Direction,\r
299         MappedAddr\r
300         );\r
301     }\r
302   }\r
303 \r
304   return PXE_STATCODE_SUCCESS;\r
305 }\r
306 \r
307 \r
308 /**\r
309   TODO: Add function description\r
310 \r
311   @param  AdapterInfo                     TODO: add argument description\r
312   @param  MemAddr                         TODO: add argument description\r
313   @param  Size                            TODO: add argument description\r
314   @param  Direction                       TODO: add argument description\r
315   @param  MappedAddr                      TODO: add argument description\r
316 \r
317   @return TODO: add return values\r
318 \r
319 **/\r
320 VOID\r
321 UnMapIt (\r
322   IN NIC_DATA_INSTANCE *AdapterInfo,\r
323   IN UINT64            MemAddr,\r
324   IN UINT32            Size,\r
325   IN UINT32            Direction,\r
326   IN UINT64            MappedAddr\r
327   )\r
328 {\r
329   if (AdapterInfo->VersionFlag > 0x30) {\r
330     //\r
331     // no mapping service\r
332     //\r
333     if (AdapterInfo->UnMap_Mem != (VOID *) NULL) {\r
334       (*AdapterInfo->UnMap_Mem) (\r
335         AdapterInfo->Unique_ID,\r
336         MemAddr,\r
337         Size,\r
338         Direction,\r
339         MappedAddr\r
340         );\r
341 \r
342     }\r
343   }\r
344 \r
345   return ;\r
346 }\r
347 \r
348 \r
349 /**\r
350 \r
351   @param  AdapterInfo                     Pointer to the NIC data structure\r
352                                           information which the UNDI driver is\r
353                                           layering on..\r
354 \r
355 \r
356 **/\r
357 // TODO:    MicroSeconds - add argument and description to function comment\r
358 VOID\r
359 DelayIt (\r
360   IN NIC_DATA_INSTANCE *AdapterInfo,\r
361   UINT16               MicroSeconds\r
362   )\r
363 {\r
364   if (AdapterInfo->VersionFlag == 0x30) {\r
365     (*AdapterInfo->Delay_30) (MicroSeconds);\r
366   } else {\r
367     (*AdapterInfo->Delay) (AdapterInfo->Unique_ID, MicroSeconds);\r
368   }\r
369 }\r
370 \r
371 \r
372 /**\r
373 \r
374   @param  AdapterInfo                     Pointer to the NIC data structure\r
375                                           information which the UNDI driver is\r
376                                           layering on..\r
377 \r
378 \r
379 **/\r
380 // TODO:    flag - add argument and description to function comment\r
381 VOID\r
382 BlockIt (\r
383   IN NIC_DATA_INSTANCE *AdapterInfo,\r
384   UINT32               flag\r
385   )\r
386 {\r
387   if (AdapterInfo->VersionFlag == 0x30) {\r
388     (*AdapterInfo->Block_30) (flag);\r
389   } else {\r
390     (*AdapterInfo->Block) (AdapterInfo->Unique_ID, flag);\r
391   }\r
392 }\r
393 \r
394 \r
395 /**\r
396   TODO: Add function description\r
397 \r
398   @param  AdapterInfo                     TODO: add argument description\r
399 \r
400   @return TODO: add return values\r
401 \r
402 **/\r
403 UINT8\r
404 Load_Base_Regs (\r
405   NIC_DATA_INSTANCE *AdapterInfo\r
406   )\r
407 {\r
408   //\r
409   // we will use the linear (flat) memory model and fill our base registers\r
410   // with 0's so that the entire physical address is our offset\r
411   //\r
412   //\r
413   // we reset the statistics totals here because this is where we are loading stats addr\r
414   //\r
415   AdapterInfo->RxTotals = 0;\r
416   AdapterInfo->TxTotals = 0;\r
417 \r
418   //\r
419   // Load the statistics block address.\r
420   //\r
421   wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);\r
422   OutLong (AdapterInfo, (UINT32) AdapterInfo->stat_phy_addr, AdapterInfo->ioaddr + SCBPointer);\r
423   OutByte (AdapterInfo, CU_STATSADDR, AdapterInfo->ioaddr + SCBCmd);\r
424   AdapterInfo->statistics->done_marker = 0;\r
425 \r
426   wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);\r
427   OutLong (AdapterInfo, 0, AdapterInfo->ioaddr + SCBPointer);\r
428   OutByte (AdapterInfo, RX_ADDR_LOAD, AdapterInfo->ioaddr + SCBCmd);\r
429 \r
430   wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);\r
431   OutLong (AdapterInfo, 0, AdapterInfo->ioaddr + SCBPointer);\r
432   OutByte (AdapterInfo, CU_CMD_BASE, AdapterInfo->ioaddr + SCBCmd);\r
433 \r
434   return 0;\r
435 }\r
436 \r
437 \r
438 /**\r
439   TODO: Add function description\r
440 \r
441   @param  AdapterInfo                     TODO: add argument description\r
442   @param  cmd_ptr                         TODO: add argument description\r
443 \r
444   @return TODO: add return values\r
445 \r
446 **/\r
447 UINT8\r
448 IssueCB (\r
449   NIC_DATA_INSTANCE *AdapterInfo,\r
450   TxCB              *cmd_ptr\r
451   )\r
452 {\r
453   UINT16  status;\r
454 \r
455   wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);\r
456 \r
457   //\r
458   // read the CU status, if it is idle, write the address of cb_ptr\r
459   // in the scbpointer and issue a cu_start,\r
460   // if it is suspended, remove the suspend bit in the previous command\r
461   // block and issue a resume\r
462   //\r
463   // Ensure that the CU Active Status bit is not on from previous CBs.\r
464   //\r
465   status = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBStatus);\r
466 \r
467   //\r
468   // Skip acknowledging the interrupt if it is not already set\r
469   //\r
470 \r
471   //\r
472   // ack only the cna the integer\r
473   //\r
474   if ((status & SCB_STATUS_CNA) != 0) {\r
475     OutWord (AdapterInfo, SCB_STATUS_CNA, AdapterInfo->ioaddr + SCBStatus);\r
476 \r
477   }\r
478 \r
479   if ((status & SCB_STATUS_CU_MASK) == SCB_STATUS_CU_IDLE) {\r
480     //\r
481     // give a cu_start\r
482     //\r
483     OutLong (AdapterInfo, cmd_ptr->PhysTCBAddress, AdapterInfo->ioaddr + SCBPointer);\r
484     OutByte (AdapterInfo, CU_START, AdapterInfo->ioaddr + SCBCmd);\r
485   } else {\r
486     //\r
487     // either active or suspended, give a resume\r
488     //\r
489 \r
490     cmd_ptr->PrevTCBVirtualLinkPtr->cb_header.command &= ~(CmdSuspend | CmdIntr);\r
491     OutByte (AdapterInfo, CU_RESUME, AdapterInfo->ioaddr + SCBCmd);\r
492   }\r
493 \r
494   return 0;\r
495 }\r
496 \r
497 \r
498 /**\r
499   TODO: Add function description\r
500 \r
501   @param  AdapterInfo                     TODO: add argument description\r
502 \r
503   @return TODO: add return values\r
504 \r
505 **/\r
506 UINT8\r
507 Configure (\r
508   NIC_DATA_INSTANCE *AdapterInfo\r
509   )\r
510 {\r
511   //\r
512   // all command blocks are of TxCB format\r
513   //\r
514   TxCB  *cmd_ptr;\r
515   UINT8 *data_ptr;\r
516   volatile INT16 Index;\r
517   UINT8 my_filter;\r
518 \r
519   cmd_ptr   = GetFreeCB (AdapterInfo);\r
520   data_ptr  = (UINT8 *) (&cmd_ptr->PhysTBDArrayAddres);\r
521 \r
522   //\r
523   // start the config data right after the command header\r
524   //\r
525   for (Index = 0; Index < sizeof (basic_config_cmd); Index++) {\r
526     data_ptr[Index] = basic_config_cmd[Index];\r
527   }\r
528 \r
529   my_filter = (UINT8) ((AdapterInfo->Rx_Filter & PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS) ? 1 : 0);\r
530   my_filter = (UINT8) (my_filter | ((AdapterInfo->Rx_Filter & PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST) ? 0 : 2));\r
531 \r
532   data_ptr[15]  = (UINT8) (data_ptr[15] | my_filter);\r
533   data_ptr[19]  = (UINT8) (AdapterInfo->Duplex ? 0xC0 : 0x80);\r
534   data_ptr[21]  = (UINT8) ((AdapterInfo->Rx_Filter & PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST) ? 0x0D : 0x05);\r
535 \r
536   //\r
537   // check if we have to use the AUI port instead\r
538   //\r
539   if ((AdapterInfo->PhyRecord[0] & 0x8000) != 0) {\r
540     data_ptr[15] |= 0x80;\r
541     data_ptr[8] = 0;\r
542   }\r
543 \r
544   BlockIt (AdapterInfo, TRUE);\r
545   cmd_ptr->cb_header.command = CmdSuspend | CmdConfigure;\r
546 \r
547   IssueCB (AdapterInfo, cmd_ptr);\r
548   wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);\r
549 \r
550   BlockIt (AdapterInfo, FALSE);\r
551 \r
552   CommandWaitForCompletion (cmd_ptr, AdapterInfo);\r
553 \r
554   //\r
555   // restore the cb values for tx\r
556   //\r
557   cmd_ptr->PhysTBDArrayAddres = cmd_ptr->PhysArrayAddr;\r
558   cmd_ptr->ByteCount = cmd_ptr->Threshold = cmd_ptr->TBDCount = 0;\r
559   //\r
560   // fields beyond the immediatedata are assumed to be safe\r
561   // add the CB to the free list again\r
562   //\r
563   SetFreeCB (AdapterInfo, cmd_ptr);\r
564   return 0;\r
565 }\r
566 \r
567 \r
568 /**\r
569   TODO: Add function description\r
570 \r
571   @param  AdapterInfo                     TODO: add argument description\r
572 \r
573   @return TODO: add return values\r
574 \r
575 **/\r
576 UINT8\r
577 E100bSetupIAAddr (\r
578   NIC_DATA_INSTANCE *AdapterInfo\r
579   )\r
580 {\r
581   //\r
582   // all command blocks are of TxCB format\r
583   //\r
584   TxCB    *cmd_ptr;\r
585   UINT16  *data_ptr;\r
586   UINT16  *eaddrs;\r
587 \r
588   eaddrs    = (UINT16 *) AdapterInfo->CurrentNodeAddress;\r
589 \r
590   cmd_ptr   = GetFreeCB (AdapterInfo);\r
591   data_ptr  = (UINT16 *) (&cmd_ptr->PhysTBDArrayAddres);\r
592 \r
593   //\r
594   // AVOID a bug (?!) here by marking the command already completed.\r
595   //\r
596   cmd_ptr->cb_header.command  = (CmdSuspend | CmdIASetup);\r
597   cmd_ptr->cb_header.status   = 0;\r
598   data_ptr[0]                 = eaddrs[0];\r
599   data_ptr[1]                 = eaddrs[1];\r
600   data_ptr[2]                 = eaddrs[2];\r
601 \r
602   BlockIt (AdapterInfo, TRUE);\r
603   IssueCB (AdapterInfo, cmd_ptr);\r
604   wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);\r
605   BlockIt (AdapterInfo, FALSE);\r
606 \r
607   CommandWaitForCompletion (cmd_ptr, AdapterInfo);\r
608 \r
609   //\r
610   // restore the cb values for tx\r
611   //\r
612   cmd_ptr->PhysTBDArrayAddres = cmd_ptr->PhysArrayAddr;\r
613   cmd_ptr->ByteCount = cmd_ptr->Threshold = cmd_ptr->TBDCount = 0;\r
614   //\r
615   // fields beyond the immediatedata are assumed to be safe\r
616   // add the CB to the free list again\r
617   //\r
618   SetFreeCB (AdapterInfo, cmd_ptr);\r
619   return 0;\r
620 }\r
621 \r
622 \r
623 /**\r
624   Instructs the NIC to stop receiving packets.\r
625 \r
626   @param  AdapterInfo                     Pointer to the NIC data structure\r
627                                           information which the UNDI driver is\r
628                                           layering on..\r
629 \r
630 \r
631 **/\r
632 VOID\r
633 StopRU (\r
634   IN NIC_DATA_INSTANCE *AdapterInfo\r
635   )\r
636 {\r
637   if (AdapterInfo->Receive_Started) {\r
638 \r
639     //\r
640     // Todo: verify that we must wait for previous command completion.\r
641     //\r
642     wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);\r
643 \r
644     //\r
645     // Disable interrupts, and stop the chip's Rx process.\r
646     //\r
647     OutWord (AdapterInfo, INT_MASK, AdapterInfo->ioaddr + SCBCmd);\r
648     OutWord (AdapterInfo, INT_MASK | RX_ABORT, AdapterInfo->ioaddr + SCBCmd);\r
649 \r
650     AdapterInfo->Receive_Started = FALSE;\r
651   }\r
652 \r
653   return ;\r
654 }\r
655 \r
656 \r
657 /**\r
658   Instructs the NIC to start receiving packets.\r
659 \r
660   @param  AdapterInfo                     Pointer to the NIC data structure\r
661                                           information which the UNDI driver is\r
662                                           layering on..\r
663 \r
664   @retval 0                               Successful\r
665   @retval -1                              Already Started\r
666 \r
667 **/\r
668 INT8\r
669 StartRU (\r
670   NIC_DATA_INSTANCE *AdapterInfo\r
671   )\r
672 {\r
673 \r
674   if (AdapterInfo->Receive_Started) {\r
675     //\r
676     // already started\r
677     //\r
678     return -1;\r
679   }\r
680 \r
681   AdapterInfo->cur_rx_ind = 0;\r
682   AdapterInfo->Int_Status = 0;\r
683 \r
684   wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);\r
685 \r
686   OutLong (AdapterInfo, (UINT32) AdapterInfo->rx_phy_addr, AdapterInfo->ioaddr + SCBPointer);\r
687   OutByte (AdapterInfo, RX_START, AdapterInfo->ioaddr + SCBCmd);\r
688 \r
689   wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);\r
690 \r
691   AdapterInfo->Receive_Started = TRUE;\r
692   return 0;\r
693 }\r
694 \r
695 \r
696 /**\r
697   Configures the chip.  This routine expects the NIC_DATA_INSTANCE structure to be filled in.\r
698 \r
699   @param  AdapterInfo                     Pointer to the NIC data structure\r
700                                           information which the UNDI driver is\r
701                                           layering on..\r
702 \r
703   @retval 0                               Successful\r
704   @retval PXE_STATCODE_NOT_ENOUGH_MEMORY  Insufficient length of locked memory\r
705   @retval other                           Failure initializing chip\r
706 \r
707 **/\r
708 UINTN\r
709 E100bInit (\r
710   IN NIC_DATA_INSTANCE *AdapterInfo\r
711   )\r
712 {\r
713   PCI_CONFIG_HEADER *CfgHdr;\r
714   UINTN             stat;\r
715   UINTN             rx_size;\r
716   UINTN             tx_size;\r
717 \r
718   if (AdapterInfo->MemoryLength < MEMORY_NEEDED) {\r
719     return PXE_STATCODE_NOT_ENOUGH_MEMORY;\r
720   }\r
721 \r
722   stat = MapIt (\r
723           AdapterInfo,\r
724           AdapterInfo->MemoryPtr,\r
725           AdapterInfo->MemoryLength,\r
726           TO_AND_FROM_DEVICE,\r
727           (UINT64)(UINTN) &AdapterInfo->Mapped_MemoryPtr\r
728           );\r
729 \r
730   if (stat != 0) {\r
731     return stat;\r
732   }\r
733 \r
734   CfgHdr = (PCI_CONFIG_HEADER *) &(AdapterInfo->Config[0]);\r
735 \r
736   //\r
737   // fill in the ioaddr, int... from the config space\r
738   //\r
739   AdapterInfo->int_num = CfgHdr->int_line;\r
740 \r
741   //\r
742   // we don't need to validate integer number, what if they don't want to assign one?\r
743   // if (AdapterInfo->int_num == 0 || AdapterInfo->int_num == 0xff)\r
744   // return PXE_STATCODE_DEVICE_FAILURE;\r
745   //\r
746   AdapterInfo->ioaddr       = 0;\r
747   AdapterInfo->VendorID     = CfgHdr->VendorID;\r
748   AdapterInfo->DeviceID     = CfgHdr->DeviceID;\r
749   AdapterInfo->RevID        = CfgHdr->RevID;\r
750   AdapterInfo->SubVendorID  = CfgHdr->SubVendorID;\r
751   AdapterInfo->SubSystemID  = CfgHdr->SubSystemID;\r
752   AdapterInfo->flash_addr   = 0;\r
753 \r
754   //\r
755   // Read the station address EEPROM before doing the reset.\r
756   // Perhaps this should even be done before accepting the device,\r
757   // then we wouldn't have a device name with which to report the error.\r
758   //\r
759   if (E100bReadEepromAndStationAddress (AdapterInfo) != 0) {\r
760     return PXE_STATCODE_DEVICE_FAILURE;\r
761 \r
762   }\r
763   //\r
764   // ## calculate the buffer #s depending on memory given\r
765   // ## calculate the rx and tx ring pointers\r
766   //\r
767 \r
768   AdapterInfo->TxBufCnt       = TX_BUFFER_COUNT;\r
769   AdapterInfo->RxBufCnt       = RX_BUFFER_COUNT;\r
770   rx_size                     = (AdapterInfo->RxBufCnt * sizeof (RxFD));\r
771   tx_size                     = (AdapterInfo->TxBufCnt * sizeof (TxCB));\r
772   AdapterInfo->rx_ring        = (RxFD *) (UINTN) (AdapterInfo->MemoryPtr);\r
773   AdapterInfo->tx_ring        = (TxCB *) (UINTN) (AdapterInfo->MemoryPtr + rx_size);\r
774   AdapterInfo->statistics     = (struct speedo_stats *) (UINTN) (AdapterInfo->MemoryPtr + rx_size + tx_size);\r
775 \r
776   AdapterInfo->rx_phy_addr    = AdapterInfo->Mapped_MemoryPtr;\r
777   AdapterInfo->tx_phy_addr    = AdapterInfo->Mapped_MemoryPtr + rx_size;\r
778   AdapterInfo->stat_phy_addr  = AdapterInfo->tx_phy_addr + tx_size;\r
779 \r
780   //\r
781   // auto detect.\r
782   //\r
783   AdapterInfo->PhyAddress     = 0xFF;\r
784   AdapterInfo->Rx_Filter            = PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST;\r
785   AdapterInfo->Receive_Started      = FALSE;\r
786   AdapterInfo->mcast_list.list_len  = 0;\r
787   return InitializeChip (AdapterInfo);\r
788 }\r
789 \r
790 \r
791 /**\r
792   Sets the interrupt state for the NIC.\r
793 \r
794   @param  AdapterInfo                     Pointer to the NIC data structure\r
795                                           information which the UNDI driver is\r
796                                           layering on..\r
797 \r
798   @retval 0                               Successful\r
799 \r
800 **/\r
801 UINT8\r
802 E100bSetInterruptState (\r
803   IN NIC_DATA_INSTANCE *AdapterInfo\r
804   )\r
805 {\r
806   //\r
807   // don't set receive interrupt if receiver is disabled...\r
808   //\r
809   UINT16  cmd_word;\r
810 \r
811   if ((AdapterInfo->int_mask & PXE_OPFLAGS_INTERRUPT_RECEIVE) != 0) {\r
812     cmd_word = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBCmd);\r
813     cmd_word &= ~INT_MASK;\r
814     OutWord (AdapterInfo, cmd_word, AdapterInfo->ioaddr + SCBCmd);\r
815   } else {\r
816     //\r
817     // disable ints, should not be given for SW Int.\r
818     //\r
819     OutWord (AdapterInfo, INT_MASK, AdapterInfo->ioaddr + SCBCmd);\r
820   }\r
821 \r
822   if ((AdapterInfo->int_mask & PXE_OPFLAGS_INTERRUPT_SOFTWARE) != 0) {\r
823     //\r
824     // reset the bit in our mask, it is only one time!!\r
825     //\r
826     AdapterInfo->int_mask &= ~(PXE_OPFLAGS_INTERRUPT_SOFTWARE);\r
827     cmd_word = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBCmd);\r
828     cmd_word |= DRVR_INT;\r
829     OutWord (AdapterInfo, cmd_word, AdapterInfo->ioaddr + SCBCmd);\r
830   }\r
831 \r
832   return 0;\r
833 }\r
834 //\r
835 // we are not going to disable broadcast for the WOL's sake!\r
836 //\r
837 \r
838 /**\r
839   Instructs the NIC to start receiving packets.\r
840 \r
841   @param  AdapterInfo                     Pointer to the NIC data structure\r
842                                           information which the UNDI driver is\r
843                                           layering on.. new_filter\r
844                                               - cpb                             -\r
845                                           cpbsize                         -\r
846 \r
847   @retval 0                               Successful\r
848   @retval -1                              Already Started\r
849 \r
850 **/\r
851 UINTN\r
852 E100bSetfilter (\r
853   NIC_DATA_INSTANCE *AdapterInfo,\r
854   UINT16            new_filter,\r
855   UINT64            cpb,\r
856   UINT32            cpbsize\r
857   )\r
858 {\r
859   PXE_CPB_RECEIVE_FILTERS *mc_list = (PXE_CPB_RECEIVE_FILTERS *) (UINTN)cpb;\r
860   UINT16                  cfg_flt;\r
861   UINT16                  old_filter;\r
862   UINT16                  Index;\r
863   UINT16                  Index2;\r
864   UINT16                  mc_count;\r
865   TxCB                    *cmd_ptr;\r
866   struct MC_CB_STRUCT     *data_ptr;\r
867   UINT16                  mc_byte_cnt;\r
868 \r
869   old_filter  = AdapterInfo->Rx_Filter;\r
870 \r
871   //\r
872   // only these bits need a change in the configuration\r
873   // actually change in bcast requires configure but we ignore that change\r
874   //\r
875   cfg_flt = PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS |\r
876             PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST;\r
877 \r
878   if ((old_filter & cfg_flt) != (new_filter & cfg_flt)) {\r
879     XmitWaitForCompletion (AdapterInfo);\r
880 \r
881     if (AdapterInfo->Receive_Started) {\r
882       StopRU (AdapterInfo);\r
883     }\r
884 \r
885     AdapterInfo->Rx_Filter = (UINT8) (new_filter | PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST);\r
886     Configure (AdapterInfo);\r
887   }\r
888 \r
889   //\r
890   // check if mcast setting changed\r
891   //\r
892   if ( ((new_filter & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) !=\r
893        (old_filter & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) ) ||\r
894        (mc_list != NULL) ) {\r
895 \r
896 \r
897     if (mc_list != NULL) {\r
898       mc_count = AdapterInfo->mcast_list.list_len = (UINT16) (cpbsize / PXE_MAC_LENGTH);\r
899 \r
900       for (Index = 0; (Index < mc_count && Index < MAX_MCAST_ADDRESS_CNT); Index++) {\r
901         for (Index2 = 0; Index2 < PXE_MAC_LENGTH; Index2++) {\r
902           AdapterInfo->mcast_list.mc_list[Index][Index2] = mc_list->MCastList[Index][Index2];\r
903         }\r
904       }\r
905     }\r
906 \r
907     //\r
908     // are we setting the list or resetting??\r
909     //\r
910     if ((new_filter & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) != 0) {\r
911       //\r
912       // we are setting a new list!\r
913       //\r
914       mc_count = AdapterInfo->mcast_list.list_len;\r
915       //\r
916       // count should be the actual # of bytes in the list\r
917       // so multiply this with 6\r
918       //\r
919       mc_byte_cnt = (UINT16) ((mc_count << 2) + (mc_count << 1));\r
920       AdapterInfo->Rx_Filter |= PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST;\r
921     } else {\r
922       //\r
923       // disabling the list in the NIC.\r
924       //\r
925       mc_byte_cnt = mc_count = 0;\r
926       AdapterInfo->Rx_Filter &= (~PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST);\r
927     }\r
928 \r
929     //\r
930     // before issuing any new command!\r
931     //\r
932     XmitWaitForCompletion (AdapterInfo);\r
933 \r
934     if (AdapterInfo->Receive_Started) {\r
935       StopRU (AdapterInfo);\r
936 \r
937     }\r
938 \r
939     cmd_ptr = GetFreeCB (AdapterInfo);\r
940     if (cmd_ptr == NULL) {\r
941       return PXE_STATCODE_QUEUE_FULL;\r
942     }\r
943     //\r
944     // fill the command structure and issue\r
945     //\r
946     data_ptr = (struct MC_CB_STRUCT *) (&cmd_ptr->PhysTBDArrayAddres);\r
947     //\r
948     // first 2 bytes are the count;\r
949     //\r
950     data_ptr->count = mc_byte_cnt;\r
951     for (Index = 0; Index < mc_count; Index++) {\r
952       for (Index2 = 0; Index2 < PXE_HWADDR_LEN_ETHER; Index2++) {\r
953         data_ptr->m_list[Index][Index2] = AdapterInfo->mcast_list.mc_list[Index][Index2];\r
954       }\r
955     }\r
956 \r
957     cmd_ptr->cb_header.command  = CmdSuspend | CmdMulticastList;\r
958     cmd_ptr->cb_header.status   = 0;\r
959 \r
960     BlockIt (AdapterInfo, TRUE);\r
961     IssueCB (AdapterInfo, cmd_ptr);\r
962     wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);\r
963 \r
964     BlockIt (AdapterInfo, FALSE);\r
965 \r
966     CommandWaitForCompletion (cmd_ptr, AdapterInfo);\r
967 \r
968     cmd_ptr->PhysTBDArrayAddres = cmd_ptr->PhysArrayAddr;\r
969     cmd_ptr->ByteCount = cmd_ptr->Threshold = cmd_ptr->TBDCount = 0;\r
970     //\r
971     // fields beyond the immediatedata are assumed to be safe\r
972     // add the CB to the free list again\r
973     //\r
974     SetFreeCB (AdapterInfo, cmd_ptr);\r
975   }\r
976 \r
977   if (new_filter != 0) {\r
978     //\r
979     // enable unicast and start the RU\r
980     //\r
981     AdapterInfo->Rx_Filter = (UINT8) (AdapterInfo->Rx_Filter | (new_filter | PXE_OPFLAGS_RECEIVE_FILTER_UNICAST));\r
982     StartRU (AdapterInfo);\r
983   } else {\r
984     //\r
985     // may be disabling everything!\r
986     //\r
987     if (AdapterInfo->Receive_Started) {\r
988       StopRU (AdapterInfo);\r
989     }\r
990 \r
991     AdapterInfo->Rx_Filter |= (~PXE_OPFLAGS_RECEIVE_FILTER_UNICAST);\r
992   }\r
993 \r
994   return 0;\r
995 }\r
996 \r
997 \r
998 /**\r
999   TODO: Add function description\r
1000 \r
1001   @param  AdapterInfo                     TODO: add argument description\r
1002   @param  cpb                             TODO: add argument description\r
1003   @param  opflags                         TODO: add argument description\r
1004 \r
1005   @return TODO: add return values\r
1006 \r
1007 **/\r
1008 UINTN\r
1009 E100bTransmit (\r
1010   NIC_DATA_INSTANCE *AdapterInfo,\r
1011   UINT64            cpb,\r
1012   UINT16            opflags\r
1013   )\r
1014 {\r
1015   PXE_CPB_TRANSMIT_FRAGMENTS  *tx_ptr_f;\r
1016   PXE_CPB_TRANSMIT            *tx_ptr_1;\r
1017   TxCB                        *tcb_ptr;\r
1018   UINT64                      Tmp_ptr;\r
1019   UINTN                       stat;\r
1020   INT32                       Index;\r
1021   UINT16                      wait_sec;\r
1022 \r
1023   tx_ptr_1  = (PXE_CPB_TRANSMIT *) (UINTN) cpb;\r
1024   tx_ptr_f  = (PXE_CPB_TRANSMIT_FRAGMENTS *) (UINTN) cpb;\r
1025 \r
1026   //\r
1027   // stop reentrancy here\r
1028   //\r
1029   if (AdapterInfo->in_transmit) {\r
1030     return PXE_STATCODE_BUSY;\r
1031 \r
1032   }\r
1033 \r
1034   AdapterInfo->in_transmit = TRUE;\r
1035 \r
1036   //\r
1037   // Prevent interrupts from changing the Tx ring from underneath us.\r
1038   //\r
1039   // Calculate the Tx descriptor entry.\r
1040   //\r
1041   if ((tcb_ptr = GetFreeCB (AdapterInfo)) == NULL) {\r
1042     AdapterInfo->in_transmit = FALSE;\r
1043     return PXE_STATCODE_QUEUE_FULL;\r
1044   }\r
1045 \r
1046   AdapterInfo->TxTotals++;\r
1047 \r
1048   tcb_ptr->cb_header.command  = (CmdSuspend | CmdTx | CmdTxFlex);\r
1049   tcb_ptr->cb_header.status   = 0;\r
1050 \r
1051   //\r
1052   // no immediate data, set EOF in the ByteCount\r
1053   //\r
1054   tcb_ptr->ByteCount = 0x8000;\r
1055 \r
1056   //\r
1057   // The data region is always in one buffer descriptor, Tx FIFO\r
1058   // threshold of 256.\r
1059   // 82557 multiplies the threashold value by 8, so give 256/8\r
1060   //\r
1061   tcb_ptr->Threshold = 32;\r
1062   if ((opflags & PXE_OPFLAGS_TRANSMIT_FRAGMENTED) != 0) {\r
1063 \r
1064     if (tx_ptr_f->FragCnt > MAX_XMIT_FRAGMENTS) {\r
1065       SetFreeCB (AdapterInfo, tcb_ptr);\r
1066       AdapterInfo->in_transmit = FALSE;\r
1067       return PXE_STATCODE_INVALID_PARAMETER;\r
1068     }\r
1069 \r
1070     tcb_ptr->TBDCount = (UINT8) tx_ptr_f->FragCnt;\r
1071 \r
1072     for (Index = 0; Index < tx_ptr_f->FragCnt; Index++) {\r
1073       stat = MapIt (\r
1074               AdapterInfo,\r
1075               tx_ptr_f->FragDesc[Index].FragAddr,\r
1076               tx_ptr_f->FragDesc[Index].FragLen,\r
1077               TO_DEVICE,\r
1078               (UINT64)(UINTN) &Tmp_ptr\r
1079               );\r
1080       if (stat != 0) {\r
1081         SetFreeCB (AdapterInfo, tcb_ptr);\r
1082         AdapterInfo->in_transmit = FALSE;\r
1083         return PXE_STATCODE_INVALID_PARAMETER;\r
1084       }\r
1085 \r
1086       tcb_ptr->TBDArray[Index].phys_buf_addr  = (UINT32) Tmp_ptr;\r
1087       tcb_ptr->TBDArray[Index].buf_len        = tx_ptr_f->FragDesc[Index].FragLen;\r
1088     }\r
1089 \r
1090     tcb_ptr->free_data_ptr = tx_ptr_f->FragDesc[0].FragAddr;\r
1091 \r
1092   } else {\r
1093     //\r
1094     // non fragmented case\r
1095     //\r
1096     tcb_ptr->TBDCount = 1;\r
1097     stat = MapIt (\r
1098             AdapterInfo,\r
1099             tx_ptr_1->FrameAddr,\r
1100             tx_ptr_1->DataLen + tx_ptr_1->MediaheaderLen,\r
1101             TO_DEVICE,\r
1102             (UINT64)(UINTN) &Tmp_ptr\r
1103             );\r
1104     if (stat != 0) {\r
1105       SetFreeCB (AdapterInfo, tcb_ptr);\r
1106       AdapterInfo->in_transmit = FALSE;\r
1107       return PXE_STATCODE_INVALID_PARAMETER;\r
1108     }\r
1109 \r
1110     tcb_ptr->TBDArray[0].phys_buf_addr  = (UINT32) (Tmp_ptr);\r
1111     tcb_ptr->TBDArray[0].buf_len        = tx_ptr_1->DataLen + tx_ptr_1->MediaheaderLen;\r
1112     tcb_ptr->free_data_ptr              = tx_ptr_1->FrameAddr;\r
1113   }\r
1114 \r
1115   //\r
1116   // must wait for previous command completion only if it was a non-transmit\r
1117   //\r
1118   BlockIt (AdapterInfo, TRUE);\r
1119   IssueCB (AdapterInfo, tcb_ptr);\r
1120   BlockIt (AdapterInfo, FALSE);\r
1121 \r
1122   //\r
1123   // see if we need to wait for completion here\r
1124   //\r
1125   if ((opflags & PXE_OPFLAGS_TRANSMIT_BLOCK) != 0) {\r
1126     //\r
1127     // don't wait for more than 1 second!!!\r
1128     //\r
1129     wait_sec = 1000;\r
1130     while (tcb_ptr->cb_header.status == 0) {\r
1131       DelayIt (AdapterInfo, 10);\r
1132       wait_sec--;\r
1133       if (wait_sec == 0) {\r
1134         break;\r
1135       }\r
1136     }\r
1137     //\r
1138     // we need to un-map any mapped buffers here\r
1139     //\r
1140     if ((opflags & PXE_OPFLAGS_TRANSMIT_FRAGMENTED) != 0) {\r
1141 \r
1142       for (Index = 0; Index < tx_ptr_f->FragCnt; Index++) {\r
1143         Tmp_ptr = tcb_ptr->TBDArray[Index].phys_buf_addr;\r
1144         UnMapIt (\r
1145           AdapterInfo,\r
1146           tx_ptr_f->FragDesc[Index].FragAddr,\r
1147           tx_ptr_f->FragDesc[Index].FragLen,\r
1148           TO_DEVICE,\r
1149           (UINT64) Tmp_ptr\r
1150           );\r
1151       }\r
1152     } else {\r
1153       Tmp_ptr = tcb_ptr->TBDArray[0].phys_buf_addr;\r
1154       UnMapIt (\r
1155         AdapterInfo,\r
1156         tx_ptr_1->FrameAddr,\r
1157         tx_ptr_1->DataLen + tx_ptr_1->MediaheaderLen,\r
1158         TO_DEVICE,\r
1159         (UINT64) Tmp_ptr\r
1160         );\r
1161     }\r
1162 \r
1163     if (tcb_ptr->cb_header.status == 0) {\r
1164       SetFreeCB (AdapterInfo, tcb_ptr);\r
1165       AdapterInfo->in_transmit = FALSE;\r
1166       return PXE_STATCODE_DEVICE_FAILURE;\r
1167     }\r
1168 \r
1169     SetFreeCB (AdapterInfo, tcb_ptr);\r
1170   }\r
1171   //\r
1172   // CB will be set free later in get_status (or when we run out of xmit buffers\r
1173   //\r
1174   AdapterInfo->in_transmit = FALSE;\r
1175 \r
1176   return 0;\r
1177 }\r
1178 \r
1179 \r
1180 /**\r
1181   TODO: Add function description\r
1182 \r
1183   @param  AdapterInfo                     TODO: add argument description\r
1184   @param  cpb                             TODO: add argument description\r
1185   @param  db                              TODO: add argument description\r
1186 \r
1187   @return TODO: add return values\r
1188 \r
1189 **/\r
1190 UINTN\r
1191 E100bReceive (\r
1192   NIC_DATA_INSTANCE *AdapterInfo,\r
1193   UINT64            cpb,\r
1194   UINT64            db\r
1195   )\r
1196 {\r
1197   PXE_CPB_RECEIVE *rx_cpbptr;\r
1198   PXE_DB_RECEIVE  *rx_dbptr;\r
1199   RxFD            *rx_ptr;\r
1200   INT32           status;\r
1201   INT32           Index;\r
1202   UINT16          pkt_len;\r
1203   UINT16          ret_code;\r
1204   PXE_FRAME_TYPE  pkt_type;\r
1205   UINT16          Tmp_len;\r
1206   EtherHeader     *hdr_ptr;\r
1207   ret_code  = PXE_STATCODE_NO_DATA;\r
1208   pkt_type  = PXE_FRAME_TYPE_NONE;\r
1209   status    = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBStatus);\r
1210   AdapterInfo->Int_Status = (UINT16) (AdapterInfo->Int_Status | status);\r
1211   //\r
1212   // acknoledge the interrupts\r
1213   //\r
1214   OutWord (AdapterInfo, (UINT16) (status & 0xfc00), (UINT32) (AdapterInfo->ioaddr + SCBStatus));\r
1215 \r
1216   //\r
1217   // include the prev ints as well\r
1218   //\r
1219   status = AdapterInfo->Int_Status;\r
1220   rx_cpbptr = (PXE_CPB_RECEIVE *) (UINTN) cpb;\r
1221   rx_dbptr  = (PXE_DB_RECEIVE *) (UINTN) db;\r
1222 \r
1223   rx_ptr    = &AdapterInfo->rx_ring[AdapterInfo->cur_rx_ind];\r
1224 \r
1225   //\r
1226   // be in a loop just in case (we may drop a pkt)\r
1227   //\r
1228   while ((status = rx_ptr->cb_header.status) & RX_COMPLETE) {\r
1229 \r
1230     AdapterInfo->RxTotals++;\r
1231     //\r
1232     // If we own the next entry, it's a new packet. Send it up.\r
1233     //\r
1234     if (rx_ptr->forwarded) {\r
1235       goto FreeRFD;\r
1236 \r
1237     }\r
1238 \r
1239     //\r
1240     // discard bad frames\r
1241     //\r
1242 \r
1243     //\r
1244     // crc, align, dma overrun, too short, receive error (v22 no coll)\r
1245     //\r
1246     if ((status & 0x0D90) != 0) {\r
1247       goto FreeRFD;\r
1248 \r
1249     }\r
1250 \r
1251     //\r
1252     // make sure the status is OK\r
1253     //\r
1254     if ((status & 0x02000) == 0) {\r
1255       goto FreeRFD;\r
1256     }\r
1257 \r
1258     pkt_len = (UINT16) (rx_ptr->ActualCount & 0x3fff);\r
1259 \r
1260     if (pkt_len != 0) {\r
1261 \r
1262       Tmp_len = pkt_len;\r
1263       if (pkt_len > rx_cpbptr->BufferLen) {\r
1264         Tmp_len = (UINT16) rx_cpbptr->BufferLen;\r
1265       }\r
1266 \r
1267       CopyMem ((INT8 *) (UINTN) rx_cpbptr->BufferAddr, (INT8 *) &rx_ptr->RFDBuffer, Tmp_len);\r
1268 \r
1269       hdr_ptr = (EtherHeader *) &rx_ptr->RFDBuffer;\r
1270       //\r
1271       // fill the CDB and break the loop\r
1272       //\r
1273 \r
1274       //\r
1275       // includes header\r
1276       //\r
1277       rx_dbptr->FrameLen = pkt_len;\r
1278       rx_dbptr->MediaHeaderLen = PXE_MAC_HEADER_LEN_ETHER;\r
1279 \r
1280       for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {\r
1281         if (hdr_ptr->dest_addr[Index] != AdapterInfo->CurrentNodeAddress[Index]) {\r
1282           break;\r
1283         }\r
1284       }\r
1285 \r
1286       if (Index >= PXE_HWADDR_LEN_ETHER) {\r
1287         pkt_type = PXE_FRAME_TYPE_UNICAST;\r
1288       } else {\r
1289         for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {\r
1290           if (hdr_ptr->dest_addr[Index] != AdapterInfo->BroadcastNodeAddress[Index]) {\r
1291             break;\r
1292           }\r
1293         }\r
1294 \r
1295         if (Index >= PXE_HWADDR_LEN_ETHER) {\r
1296           pkt_type = PXE_FRAME_TYPE_BROADCAST;\r
1297         } else {\r
1298           if ((hdr_ptr->dest_addr[0] & 1) == 1) {\r
1299             //\r
1300             // mcast\r
1301             //\r
1302 \r
1303             pkt_type = PXE_FRAME_TYPE_FILTERED_MULTICAST;\r
1304           } else {\r
1305             pkt_type = PXE_FRAME_TYPE_PROMISCUOUS;\r
1306           }\r
1307         }\r
1308       }\r
1309 \r
1310       rx_dbptr->Type      = pkt_type;\r
1311       rx_dbptr->Protocol  = hdr_ptr->type;\r
1312 \r
1313       for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {\r
1314         rx_dbptr->SrcAddr[Index]  = hdr_ptr->src_addr[Index];\r
1315         rx_dbptr->DestAddr[Index] = hdr_ptr->dest_addr[Index];\r
1316       }\r
1317 \r
1318       rx_ptr->forwarded = TRUE;\r
1319       //\r
1320       // success\r
1321       //\r
1322       ret_code          = 0;\r
1323       Recycle_RFD (AdapterInfo, AdapterInfo->cur_rx_ind);\r
1324       AdapterInfo->cur_rx_ind++;\r
1325       if (AdapterInfo->cur_rx_ind == AdapterInfo->RxBufCnt) {\r
1326         AdapterInfo->cur_rx_ind = 0;\r
1327       }\r
1328       break;\r
1329     }\r
1330 \r
1331 FreeRFD:\r
1332     Recycle_RFD (AdapterInfo, AdapterInfo->cur_rx_ind);\r
1333     AdapterInfo->cur_rx_ind++;\r
1334     if (AdapterInfo->cur_rx_ind == AdapterInfo->RxBufCnt) {\r
1335       AdapterInfo->cur_rx_ind = 0;\r
1336     }\r
1337 \r
1338     rx_ptr = &AdapterInfo->rx_ring[AdapterInfo->cur_rx_ind];\r
1339   }\r
1340 \r
1341   if (pkt_type == PXE_FRAME_TYPE_NONE) {\r
1342     AdapterInfo->Int_Status &= (~SCB_STATUS_FR);\r
1343   }\r
1344 \r
1345   status = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBStatus);\r
1346   if ((status & SCB_RUS_NO_RESOURCES) != 0) {\r
1347     //\r
1348     // start the receive unit here!\r
1349     // leave all the filled frames,\r
1350     //\r
1351     SetupReceiveQueues (AdapterInfo);\r
1352     OutLong (AdapterInfo, (UINT32) AdapterInfo->rx_phy_addr, AdapterInfo->ioaddr + SCBPointer);\r
1353     OutWord (AdapterInfo, RX_START, AdapterInfo->ioaddr + SCBCmd);\r
1354     AdapterInfo->cur_rx_ind = 0;\r
1355   }\r
1356 \r
1357   return ret_code;\r
1358 }\r
1359 \r
1360 \r
1361 /**\r
1362   TODO: Add function description\r
1363 \r
1364   @param  AdapterInfo                     TODO: add argument description\r
1365 \r
1366   @return TODO: add return values\r
1367 \r
1368 **/\r
1369 INT16\r
1370 E100bReadEepromAndStationAddress (\r
1371   NIC_DATA_INSTANCE *AdapterInfo\r
1372   )\r
1373 {\r
1374   INT32   Index;\r
1375   INT32   Index2;\r
1376   UINT16  sum;\r
1377   UINT16  eeprom_len;\r
1378   UINT8   addr_len;\r
1379   UINT16  *eedata;\r
1380 \r
1381   eedata    = (UINT16 *) (&AdapterInfo->NVData[0]);\r
1382 \r
1383   sum       = 0;\r
1384   addr_len  = E100bGetEepromAddrLen (AdapterInfo);\r
1385 \r
1386   //\r
1387   // in words\r
1388   //\r
1389   AdapterInfo->NVData_Len = eeprom_len = (UINT16) (1 << addr_len);\r
1390   for (Index2 = 0, Index = 0; Index < eeprom_len; Index++) {\r
1391     UINT16  value;\r
1392     value         = E100bReadEeprom (AdapterInfo, Index, addr_len);\r
1393     eedata[Index] = value;\r
1394     sum           = (UINT16) (sum + value);\r
1395     if (Index < 3) {\r
1396       AdapterInfo->PermNodeAddress[Index2++]  = (UINT8) value;\r
1397       AdapterInfo->PermNodeAddress[Index2++]  = (UINT8) (value >> 8);\r
1398     }\r
1399   }\r
1400 \r
1401   if (sum != 0xBABA) {\r
1402     return -1;\r
1403   }\r
1404 \r
1405   for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {\r
1406     AdapterInfo->CurrentNodeAddress[Index] = AdapterInfo->PermNodeAddress[Index];\r
1407   }\r
1408 \r
1409   for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {\r
1410     AdapterInfo->BroadcastNodeAddress[Index] = 0xff;\r
1411   }\r
1412 \r
1413   for (Index = PXE_HWADDR_LEN_ETHER; Index < PXE_MAC_LENGTH; Index++) {\r
1414     AdapterInfo->CurrentNodeAddress[Index]    = 0;\r
1415     AdapterInfo->PermNodeAddress[Index]       = 0;\r
1416     AdapterInfo->BroadcastNodeAddress[Index]  = 0;\r
1417   }\r
1418 \r
1419   return 0;\r
1420 }\r
1421 \r
1422 //\r
1423 //  CBList is a circular linked list\r
1424 //  1) When all are free, Tail->next == Head and FreeCount == # allocated\r
1425 //  2) When none are free, Tail == Head and FreeCount == 0\r
1426 //  3) when one is free, Tail == Head and Freecount == 1\r
1427 //  4) First non-Free frame is always at Tail->next\r
1428 //\r
1429 \r
1430 /**\r
1431   TODO: Add function description\r
1432 \r
1433   @param  AdapterInfo                     TODO: add argument description\r
1434 \r
1435   @return TODO: add return values\r
1436 \r
1437 **/\r
1438 UINT8\r
1439 SetupCBlink (\r
1440   NIC_DATA_INSTANCE *AdapterInfo\r
1441   )\r
1442 {\r
1443   TxCB  *head_ptr;\r
1444   TxCB  *tail_ptr;\r
1445   TxCB  *cur_ptr;\r
1446   INT32 Index;\r
1447   UINTN array_off;\r
1448 \r
1449   cur_ptr   = &(AdapterInfo->tx_ring[0]);\r
1450   array_off = (UINTN) (&cur_ptr->TBDArray) - (UINTN) cur_ptr;\r
1451   for (Index = 0; Index < AdapterInfo->TxBufCnt; Index++) {\r
1452     cur_ptr[Index].cb_header.status   = 0;\r
1453     cur_ptr[Index].cb_header.command  = 0;\r
1454 \r
1455     cur_ptr[Index].PhysTCBAddress     =\r
1456     (UINT32) AdapterInfo->tx_phy_addr + (Index * sizeof (TxCB));\r
1457 \r
1458     cur_ptr[Index].PhysArrayAddr      = (UINT32)(cur_ptr[Index].PhysTCBAddress + array_off);\r
1459     cur_ptr[Index].PhysTBDArrayAddres = (UINT32)(cur_ptr[Index].PhysTCBAddress + array_off);\r
1460 \r
1461     cur_ptr->free_data_ptr = (UINT64) 0;\r
1462 \r
1463     if (Index < AdapterInfo->TxBufCnt - 1) {\r
1464       cur_ptr[Index].cb_header.link             = cur_ptr[Index].PhysTCBAddress + sizeof (TxCB);\r
1465       cur_ptr[Index].NextTCBVirtualLinkPtr      = &cur_ptr[Index + 1];\r
1466       cur_ptr[Index + 1].PrevTCBVirtualLinkPtr  = &cur_ptr[Index];\r
1467     }\r
1468   }\r
1469 \r
1470   head_ptr                        = &cur_ptr[0];\r
1471   tail_ptr                        = &cur_ptr[AdapterInfo->TxBufCnt - 1];\r
1472   tail_ptr->cb_header.link        = head_ptr->PhysTCBAddress;\r
1473   tail_ptr->NextTCBVirtualLinkPtr = head_ptr;\r
1474   head_ptr->PrevTCBVirtualLinkPtr = tail_ptr;\r
1475 \r
1476   AdapterInfo->FreeCBCount        = AdapterInfo->TxBufCnt;\r
1477   AdapterInfo->FreeTxHeadPtr      = head_ptr;\r
1478   //\r
1479   // set tail of the free list, next to this would be either in use\r
1480   // or the head itself\r
1481   //\r
1482   AdapterInfo->FreeTxTailPtr  = tail_ptr;\r
1483 \r
1484   AdapterInfo->xmit_done_head = AdapterInfo->xmit_done_tail = 0;\r
1485 \r
1486   return 0;\r
1487 }\r
1488 \r
1489 \r
1490 /**\r
1491   TODO: Add function description\r
1492 \r
1493   @param  AdapterInfo                     TODO: add argument description\r
1494 \r
1495   @return TODO: add return values\r
1496 \r
1497 **/\r
1498 TxCB *\r
1499 GetFreeCB (\r
1500   NIC_DATA_INSTANCE *AdapterInfo\r
1501   )\r
1502 {\r
1503   TxCB  *free_cb_ptr;\r
1504 \r
1505   //\r
1506   // claim any hanging free CBs\r
1507   //\r
1508   if (AdapterInfo->FreeCBCount <= 1) {\r
1509     CheckCBList (AdapterInfo);\r
1510   }\r
1511 \r
1512   //\r
1513   // don't use up the last CB problem if the previous CB that the CU used\r
1514   // becomes the last CB we submit because of the SUSPEND bit we set.\r
1515   // the CU thinks it was never cleared.\r
1516   //\r
1517 \r
1518   if (AdapterInfo->FreeCBCount <= 1) {\r
1519     return NULL;\r
1520   }\r
1521 \r
1522   BlockIt (AdapterInfo, TRUE);\r
1523   free_cb_ptr                 = AdapterInfo->FreeTxHeadPtr;\r
1524   AdapterInfo->FreeTxHeadPtr  = free_cb_ptr->NextTCBVirtualLinkPtr;\r
1525   --AdapterInfo->FreeCBCount;\r
1526   BlockIt (AdapterInfo, FALSE);\r
1527   return free_cb_ptr;\r
1528 }\r
1529 \r
1530 \r
1531 /**\r
1532   TODO: Add function description\r
1533 \r
1534   @param  AdapterInfo                     TODO: add argument description\r
1535   @param  cb_ptr                          TODO: add argument description\r
1536 \r
1537   @return TODO: add return values\r
1538 \r
1539 **/\r
1540 VOID\r
1541 SetFreeCB (\r
1542   IN NIC_DATA_INSTANCE *AdapterInfo,\r
1543   IN TxCB              *cb_ptr\r
1544   )\r
1545 {\r
1546   //\r
1547   // here we assume cb are returned in the order they are taken out\r
1548   // and we link the newly freed cb at the tail of free cb list\r
1549   //\r
1550   cb_ptr->cb_header.status    = 0;\r
1551   cb_ptr->free_data_ptr       = (UINT64) 0;\r
1552 \r
1553   AdapterInfo->FreeTxTailPtr  = cb_ptr;\r
1554   ++AdapterInfo->FreeCBCount;\r
1555   return ;\r
1556 }\r
1557 \r
1558 \r
1559 /**\r
1560   TODO: Add function description\r
1561 \r
1562   @param  ind                             TODO: add argument description\r
1563 \r
1564   @return TODO: add return values\r
1565 \r
1566 **/\r
1567 UINT16\r
1568 next (\r
1569   IN UINT16 ind\r
1570   )\r
1571 {\r
1572   UINT16  Tmp;\r
1573 \r
1574   Tmp = (UINT16) (ind + 1);\r
1575   if (Tmp >= (TX_BUFFER_COUNT << 1)) {\r
1576     Tmp = 0;\r
1577   }\r
1578 \r
1579   return Tmp;\r
1580 }\r
1581 \r
1582 \r
1583 /**\r
1584   TODO: Add function description\r
1585 \r
1586   @param  AdapterInfo                     TODO: add argument description\r
1587 \r
1588   @return TODO: add return values\r
1589 \r
1590 **/\r
1591 UINT16\r
1592 CheckCBList (\r
1593   IN NIC_DATA_INSTANCE *AdapterInfo\r
1594   )\r
1595 {\r
1596   TxCB    *Tmp_ptr;\r
1597   UINT16  cnt;\r
1598 \r
1599   cnt = 0;\r
1600   while (1) {\r
1601     Tmp_ptr = AdapterInfo->FreeTxTailPtr->NextTCBVirtualLinkPtr;\r
1602     if ((Tmp_ptr->cb_header.status & CMD_STATUS_MASK) != 0) {\r
1603       //\r
1604       // check if Q is full\r
1605       //\r
1606       if (next (AdapterInfo->xmit_done_tail) != AdapterInfo->xmit_done_head) {\r
1607         AdapterInfo->xmit_done[AdapterInfo->xmit_done_tail] = Tmp_ptr->free_data_ptr;\r
1608 \r
1609         UnMapIt (\r
1610           AdapterInfo,\r
1611           Tmp_ptr->free_data_ptr,\r
1612           Tmp_ptr->TBDArray[0].buf_len,\r
1613           TO_DEVICE,\r
1614           (UINT64) Tmp_ptr->TBDArray[0].phys_buf_addr\r
1615           );\r
1616 \r
1617         AdapterInfo->xmit_done_tail = next (AdapterInfo->xmit_done_tail);\r
1618       }\r
1619 \r
1620       SetFreeCB (AdapterInfo, Tmp_ptr);\r
1621     } else {\r
1622       break;\r
1623     }\r
1624   }\r
1625 \r
1626   return cnt;\r
1627 }\r
1628 //\r
1629 // Description : Initialize the RFD list list by linking each element together\r
1630 //               in a circular list.  The simplified memory model is used.\r
1631 //               All data is in the RFD.  The RFDs are linked together and the\r
1632 //               last one points back to the first one.  When the current RFD\r
1633 //               is processed (frame received), its EL bit is set and the EL\r
1634 //               bit in the previous RXFD is cleared.\r
1635 //               Allocation done during INIT, this is making linked list.\r
1636 //\r
1637 \r
1638 /**\r
1639   TODO: Add function description\r
1640 \r
1641   @param  AdapterInfo                     TODO: add argument description\r
1642 \r
1643   @return TODO: add return values\r
1644 \r
1645 **/\r
1646 UINT8\r
1647 SetupReceiveQueues (\r
1648   IN NIC_DATA_INSTANCE *AdapterInfo\r
1649   )\r
1650 {\r
1651   RxFD    *rx_ptr;\r
1652   RxFD    *tail_ptr;\r
1653   UINT16  Index;\r
1654 \r
1655   AdapterInfo->cur_rx_ind = 0;\r
1656   rx_ptr                  = (&AdapterInfo->rx_ring[0]);\r
1657 \r
1658   for (Index = 0; Index < AdapterInfo->RxBufCnt; Index++) {\r
1659     rx_ptr[Index].cb_header.status  = 0;\r
1660     rx_ptr[Index].cb_header.command = 0;\r
1661     rx_ptr[Index].RFDSize           = RX_BUFFER_SIZE;\r
1662     rx_ptr[Index].ActualCount       = 0;\r
1663     //\r
1664     // RBDs not used, simple memory model\r
1665     //\r
1666     rx_ptr[Index].rx_buf_addr       = (UINT32) (-1);\r
1667 \r
1668     //\r
1669     // RBDs not used, simple memory model\r
1670     //\r
1671     rx_ptr[Index].forwarded = FALSE;\r
1672 \r
1673     //\r
1674     // don't use Tmp_ptr if it is beyond the last one\r
1675     //\r
1676     if (Index < AdapterInfo->RxBufCnt - 1) {\r
1677       rx_ptr[Index].cb_header.link = (UINT32) AdapterInfo->rx_phy_addr + ((Index + 1) * sizeof (RxFD));\r
1678     }\r
1679   }\r
1680 \r
1681   tail_ptr                    = (&AdapterInfo->rx_ring[AdapterInfo->RxBufCnt - 1]);\r
1682   tail_ptr->cb_header.link    = (UINT32) AdapterInfo->rx_phy_addr;\r
1683 \r
1684   //\r
1685   // set the EL bit\r
1686   //\r
1687   tail_ptr->cb_header.command = 0xC000;\r
1688   AdapterInfo->RFDTailPtr = tail_ptr;\r
1689   return 0;\r
1690 }\r
1691 \r
1692 \r
1693 /**\r
1694   TODO: Add function description\r
1695 \r
1696   @param  AdapterInfo                     TODO: add argument description\r
1697   @param  rx_index                        TODO: add argument description\r
1698 \r
1699   @return TODO: add return values\r
1700 \r
1701 **/\r
1702 VOID\r
1703 Recycle_RFD (\r
1704   IN NIC_DATA_INSTANCE *AdapterInfo,\r
1705   IN UINT16            rx_index\r
1706   )\r
1707 {\r
1708   RxFD  *rx_ptr;\r
1709   RxFD  *tail_ptr;\r
1710   //\r
1711   // change the EL bit and change the AdapterInfo->RxTailPtr\r
1712   // rx_ptr is assumed to be the head of the Q\r
1713   // AdapterInfo->rx_forwarded[rx_index] = FALSE;\r
1714   //\r
1715   rx_ptr                    = &AdapterInfo->rx_ring[rx_index];\r
1716   tail_ptr                  = AdapterInfo->RFDTailPtr;\r
1717   //\r
1718   // set el_bit and suspend bit\r
1719   //\r
1720   rx_ptr->cb_header.command = 0xc000;\r
1721   rx_ptr->cb_header.status    = 0;\r
1722   rx_ptr->ActualCount         = 0;\r
1723   rx_ptr->forwarded           = FALSE;\r
1724   AdapterInfo->RFDTailPtr     = rx_ptr;\r
1725   //\r
1726   // resetting the el_bit.\r
1727   //\r
1728   tail_ptr->cb_header.command = 0;\r
1729   //\r
1730   // check the receive unit, fix if there is any problem\r
1731   //\r
1732   return ;\r
1733 }\r
1734 //\r
1735 // Serial EEPROM section.\r
1736 //\r
1737 //  EEPROM_Ctrl bits.\r
1738 //\r
1739 #define EE_SHIFT_CLK  0x01  /* EEPROM shift clock. */\r
1740 #define EE_CS         0x02  /* EEPROM chip select. */\r
1741 #define EE_DI         0x04  /* EEPROM chip data in. */\r
1742 #define EE_WRITE_0    0x01\r
1743 #define EE_WRITE_1    0x05\r
1744 #define EE_DO         0x08  /* EEPROM chip data out. */\r
1745 #define EE_ENB        (0x4800 | EE_CS)\r
1746 \r
1747 //\r
1748 // Delay between EEPROM clock transitions.\r
1749 // This will actually work with no delay on 33Mhz PCI.\r
1750 //\r
1751 #define eeprom_delay(nanosec) DelayIt (AdapterInfo, nanosec);\r
1752 \r
1753 //\r
1754 // The EEPROM commands include the alway-set leading bit.\r
1755 //\r
1756 #define EE_WRITE_CMD  5 // 101b\r
1757 #define EE_READ_CMD   6 // 110b\r
1758 #define EE_ERASE_CMD  (7 << 6)\r
1759 \r
1760 VOID\r
1761 shift_bits_out (\r
1762   IN NIC_DATA_INSTANCE *AdapterInfo,\r
1763   IN UINT16            val,\r
1764   IN UINT8             num_bits\r
1765   )\r
1766 /*++\r
1767 \r
1768 Routine Description:\r
1769 \r
1770   TODO: Add function description\r
1771 \r
1772 Arguments:\r
1773 \r
1774   AdapterInfo - TODO: add argument description\r
1775   val         - TODO: add argument description\r
1776   num_bits    - TODO: add argument description\r
1777 \r
1778 Returns:\r
1779 \r
1780   TODO: add return values\r
1781 \r
1782 --*/\r
1783 {\r
1784   INT32   Index;\r
1785   UINT8   Tmp;\r
1786   UINT32  EEAddr;\r
1787 \r
1788   EEAddr = AdapterInfo->ioaddr + SCBeeprom;\r
1789 \r
1790   for (Index = num_bits; Index >= 0; Index--) {\r
1791     INT16 dataval;\r
1792 \r
1793     //\r
1794     // will be 0 or 4\r
1795     //\r
1796     dataval = (INT16) ((val & (1 << Index)) ? EE_DI : 0);\r
1797 \r
1798     //\r
1799     // mask off the data_in bit\r
1800     //\r
1801     Tmp = (UINT8) (InByte (AdapterInfo, EEAddr) &~EE_DI);\r
1802     Tmp = (UINT8) (Tmp | dataval);\r
1803     OutByte (AdapterInfo, Tmp, EEAddr);\r
1804     eeprom_delay (100);\r
1805     //\r
1806     // raise the eeprom clock\r
1807     //\r
1808     OutByte (AdapterInfo, (UINT8) (Tmp | EE_SHIFT_CLK), EEAddr);\r
1809     eeprom_delay (150);\r
1810     //\r
1811     // lower the eeprom clock\r
1812     //\r
1813     OutByte (AdapterInfo, (UINT8) (Tmp &~EE_SHIFT_CLK), EEAddr);\r
1814     eeprom_delay (150);\r
1815   }\r
1816 }\r
1817 \r
1818 \r
1819 /**\r
1820   TODO: Add function description\r
1821 \r
1822   @param  AdapterInfo                     TODO: add argument description\r
1823 \r
1824   @return TODO: add return values\r
1825 \r
1826 **/\r
1827 UINT16\r
1828 shift_bits_in (\r
1829   IN NIC_DATA_INSTANCE *AdapterInfo\r
1830   )\r
1831 {\r
1832   UINT8   Tmp;\r
1833   INT32   Index;\r
1834   UINT16  retval;\r
1835   UINT32  EEAddr;\r
1836 \r
1837   EEAddr  = AdapterInfo->ioaddr + SCBeeprom;\r
1838 \r
1839   retval  = 0;\r
1840   for (Index = 15; Index >= 0; Index--) {\r
1841     //\r
1842     // raise the clock\r
1843     //\r
1844 \r
1845     //\r
1846     // mask off the data_in bit\r
1847     //\r
1848     Tmp = InByte (AdapterInfo, EEAddr);\r
1849     OutByte (AdapterInfo, (UINT8) (Tmp | EE_SHIFT_CLK), EEAddr);\r
1850     eeprom_delay (100);\r
1851     Tmp     = InByte (AdapterInfo, EEAddr);\r
1852     retval  = (UINT16) ((retval << 1) | ((Tmp & EE_DO) ? 1 : 0));\r
1853     //\r
1854     // lower the clock\r
1855     //\r
1856     OutByte (AdapterInfo, (UINT8) (Tmp &~EE_SHIFT_CLK), EEAddr);\r
1857     eeprom_delay (100);\r
1858   }\r
1859 \r
1860   return retval;\r
1861 }\r
1862 \r
1863 \r
1864 /**\r
1865   This routine sets the EEPROM lockout bit to gain exclusive access to the\r
1866   eeprom. the access bit is the most significant bit in the General Control\r
1867   Register 2 in the SCB space.\r
1868 \r
1869   @param  AdapterInfo                     Pointer to the NIC data structure\r
1870                                           information which the UNDI driver is\r
1871                                           layering on..\r
1872 \r
1873   @retval TRUE                            if it got the access\r
1874   @retval FALSE                           if it fails to get the exclusive access\r
1875 \r
1876 **/\r
1877 BOOLEAN\r
1878 E100bSetEepromLockOut (\r
1879   IN NIC_DATA_INSTANCE  *AdapterInfo\r
1880   )\r
1881 {\r
1882   UINTN wait;\r
1883   UINT8 tmp;\r
1884 \r
1885   if ((AdapterInfo->DeviceID == D102_DEVICE_ID) ||\r
1886       (AdapterInfo->RevID >= D102_REVID)) {\r
1887 \r
1888     wait = 500;\r
1889 \r
1890     while (wait--) {\r
1891 \r
1892       tmp = InByte (AdapterInfo, AdapterInfo->ioaddr + SCBGenCtrl2);\r
1893       tmp |= GCR2_EEPROM_ACCESS_SEMAPHORE;\r
1894       OutByte (AdapterInfo, tmp, AdapterInfo->ioaddr + SCBGenCtrl2);\r
1895 \r
1896       DelayIt (AdapterInfo, 50);\r
1897       tmp = InByte (AdapterInfo, AdapterInfo->ioaddr + SCBGenCtrl2);\r
1898 \r
1899       if (tmp & GCR2_EEPROM_ACCESS_SEMAPHORE) {\r
1900         return TRUE;\r
1901       }\r
1902     }\r
1903 \r
1904     return FALSE;\r
1905   }\r
1906 \r
1907   return TRUE;\r
1908 }\r
1909 \r
1910 \r
1911 /**\r
1912   This routine Resets the EEPROM lockout bit to giveup access to the\r
1913   eeprom. the access bit is the most significant bit in the General Control\r
1914   Register 2 in the SCB space.\r
1915 \r
1916   @param  AdapterInfo                     Pointer to the NIC data structure\r
1917                                           information which the UNDI driver is\r
1918                                           layering on..\r
1919 \r
1920   @return None\r
1921 \r
1922 **/\r
1923 VOID\r
1924 E100bReSetEepromLockOut (\r
1925   IN NIC_DATA_INSTANCE  *AdapterInfo\r
1926   )\r
1927 {\r
1928   UINT8 tmp;\r
1929 \r
1930   if ((AdapterInfo->DeviceID == D102_DEVICE_ID) ||\r
1931       (AdapterInfo->RevID >= D102_REVID)) {\r
1932 \r
1933     tmp = InByte (AdapterInfo, AdapterInfo->ioaddr + SCBGenCtrl2);\r
1934     tmp &= ~(GCR2_EEPROM_ACCESS_SEMAPHORE);\r
1935     OutByte (AdapterInfo, tmp, AdapterInfo->ioaddr + SCBGenCtrl2);\r
1936 \r
1937     DelayIt (AdapterInfo, 50);\r
1938   }\r
1939 }\r
1940 \r
1941 \r
1942 /**\r
1943   Using the NIC data structure information, read the EEPROM to get a Word of data for the MAC address.\r
1944 \r
1945   @param  AdapterInfo                     Pointer to the NIC data structure\r
1946                                           information which the UNDI driver is\r
1947                                           layering on..\r
1948   @param  Location                        Word offset into the MAC address to read.\r
1949   @param  AddrLen                         Number of bits of address length.\r
1950 \r
1951   @retval RetVal                          The word read from the EEPROM.\r
1952 \r
1953 **/\r
1954 UINT16\r
1955 E100bReadEeprom (\r
1956   IN NIC_DATA_INSTANCE  *AdapterInfo,\r
1957   IN INT32              Location,\r
1958   IN UINT8              AddrLen\r
1959   )\r
1960 {\r
1961   UINT16  RetVal;\r
1962   UINT8   Tmp;\r
1963 \r
1964   UINT32  EEAddr;\r
1965   UINT16  ReadCmd;\r
1966 \r
1967   EEAddr  = AdapterInfo->ioaddr + SCBeeprom;\r
1968   ReadCmd = (UINT16) (Location | (EE_READ_CMD << AddrLen));\r
1969 \r
1970   RetVal  = 0;\r
1971 \r
1972   //\r
1973   // get exclusive access to the eeprom first!\r
1974   //\r
1975   E100bSetEepromLockOut (AdapterInfo);\r
1976 \r
1977   //\r
1978   // eeprom control reg bits: x,x,x,x,DO,DI,CS,SK\r
1979   // to write the opcode+data value out one bit at a time in DI starting at msb\r
1980   // and then out a 1 to sk, wait, out 0 to SK and wait\r
1981   // repeat this for all the bits to be written\r
1982   //\r
1983 \r
1984   //\r
1985   // 11110010b\r
1986   //\r
1987   Tmp = (UINT8) (InByte (AdapterInfo, EEAddr) & 0xF2);\r
1988   OutByte (AdapterInfo, (UINT8) (Tmp | EE_CS), EEAddr);\r
1989 \r
1990   //\r
1991   // 3 for the read opcode 110b\r
1992   //\r
1993   shift_bits_out (AdapterInfo, ReadCmd, (UINT8) (3 + AddrLen));\r
1994 \r
1995   //\r
1996   // read the eeprom word one bit at a time\r
1997   //\r
1998   RetVal = shift_bits_in (AdapterInfo);\r
1999 \r
2000   //\r
2001   // Terminate the EEPROM access and leave eeprom in a clean state.\r
2002   //\r
2003   Tmp = InByte (AdapterInfo, EEAddr);\r
2004   Tmp &= ~(EE_CS | EE_DI);\r
2005   OutByte (AdapterInfo, Tmp, EEAddr);\r
2006 \r
2007   //\r
2008   // raise the clock and lower the eeprom shift clock\r
2009   //\r
2010   OutByte (AdapterInfo, (UINT8) (Tmp | EE_SHIFT_CLK), EEAddr);\r
2011   eeprom_delay (100);\r
2012 \r
2013   OutByte (AdapterInfo, (UINT8) (Tmp &~EE_SHIFT_CLK), EEAddr);\r
2014   eeprom_delay (100);\r
2015 \r
2016   //\r
2017   // giveup access to the eeprom\r
2018   //\r
2019   E100bReSetEepromLockOut (AdapterInfo);\r
2020 \r
2021   return RetVal;\r
2022 }\r
2023 \r
2024 \r
2025 /**\r
2026   Using the NIC data structure information, read the EEPROM to determine how many bits of address length\r
2027   this EEPROM is in Words.\r
2028 \r
2029   @param  AdapterInfo                     Pointer to the NIC data structure\r
2030                                           information which the UNDI driver is\r
2031                                           layering on..\r
2032 \r
2033   @retval RetVal                          The word read from the EEPROM.\r
2034 \r
2035 **/\r
2036 UINT8\r
2037 E100bGetEepromAddrLen (\r
2038   IN NIC_DATA_INSTANCE *AdapterInfo\r
2039   )\r
2040 {\r
2041   UINT8   Tmp;\r
2042   UINT8   AddrLen;\r
2043   UINT32  EEAddr;\r
2044   //\r
2045   // assume 64word eeprom (so,6 bits of address_length)\r
2046   //\r
2047   UINT16  ReadCmd;\r
2048 \r
2049   EEAddr  = AdapterInfo->ioaddr + SCBeeprom;\r
2050   ReadCmd = (EE_READ_CMD << 6);\r
2051 \r
2052   //\r
2053   // get exclusive access to the eeprom first!\r
2054   //\r
2055   E100bSetEepromLockOut (AdapterInfo);\r
2056 \r
2057   //\r
2058   // address we are trying to read is 0\r
2059   // eeprom control reg bits: x,x,x,x,DO,,DI,,CS,SK\r
2060   // to write the opcode+data value out one bit at a time in DI starting at msb\r
2061   // and then out a 1 to sk, wait, out 0 to SK and wait\r
2062   // repeat this for all the bits to be written\r
2063   //\r
2064   Tmp = (UINT8) (InByte (AdapterInfo, EEAddr) & 0xF2);\r
2065 \r
2066   //\r
2067   // enable eeprom access\r
2068   //\r
2069   OutByte (AdapterInfo, (UINT8) (Tmp | EE_CS), EEAddr);\r
2070 \r
2071   //\r
2072   // 3 for opcode, 6 for the default address len\r
2073   //\r
2074   shift_bits_out (AdapterInfo, ReadCmd, (UINT8) (3 + 6));\r
2075 \r
2076   //\r
2077   // (in case of a 64 word eeprom).\r
2078   // read the "dummy zero" from EE_DO to say that the address we wrote\r
2079   // (six 0s) is accepted, write more zeros (until 8) to get a "dummy zero"\r
2080   //\r
2081 \r
2082   //\r
2083   // assume the smallest\r
2084   //\r
2085   AddrLen = 6;\r
2086   Tmp     = InByte (AdapterInfo, EEAddr);\r
2087   while ((AddrLen < 8) && ((Tmp & EE_DO) != 0)) {\r
2088     OutByte (AdapterInfo, (UINT8) (Tmp &~EE_DI), EEAddr);\r
2089     eeprom_delay (100);\r
2090 \r
2091     //\r
2092     // raise the eeprom clock\r
2093     //\r
2094     OutByte (AdapterInfo, (UINT8) (Tmp | EE_SHIFT_CLK), EEAddr);\r
2095     eeprom_delay (150);\r
2096 \r
2097     //\r
2098     // lower the eeprom clock\r
2099     //\r
2100     OutByte (AdapterInfo, (UINT8) (Tmp &~EE_SHIFT_CLK), EEAddr);\r
2101     eeprom_delay (150);\r
2102     Tmp = InByte (AdapterInfo, EEAddr);\r
2103     AddrLen++;\r
2104   }\r
2105 \r
2106   //\r
2107   // read the eeprom word, even though we don't need this\r
2108   //\r
2109   shift_bits_in (AdapterInfo);\r
2110 \r
2111   //\r
2112   // Terminate the EEPROM access.\r
2113   //\r
2114   Tmp = InByte (AdapterInfo, EEAddr);\r
2115   Tmp &= ~(EE_CS | EE_DI);\r
2116   OutByte (AdapterInfo, Tmp, EEAddr);\r
2117 \r
2118   //\r
2119   // raise the clock and lower the eeprom shift clock\r
2120   //\r
2121   OutByte (AdapterInfo, (UINT8) (Tmp | EE_SHIFT_CLK), EEAddr);\r
2122   eeprom_delay (100);\r
2123 \r
2124   OutByte (AdapterInfo, (UINT8) (Tmp &~EE_SHIFT_CLK), EEAddr);\r
2125   eeprom_delay (100);\r
2126 \r
2127   //\r
2128   // giveup access to the eeprom!\r
2129   //\r
2130   E100bReSetEepromLockOut (AdapterInfo);\r
2131 \r
2132   return AddrLen;\r
2133 }\r
2134 \r
2135 \r
2136 /**\r
2137   TODO: Add function description\r
2138 \r
2139   @param  AdapterInfo                     TODO: add argument description\r
2140   @param  DBaddr                          TODO: add argument description\r
2141   @param  DBsize                          TODO: add argument description\r
2142 \r
2143   @return TODO: add return values\r
2144 \r
2145 **/\r
2146 UINTN\r
2147 E100bStatistics (\r
2148   NIC_DATA_INSTANCE *AdapterInfo,\r
2149   UINT64            DBaddr,\r
2150   UINT16            DBsize\r
2151   )\r
2152 {\r
2153   PXE_DB_STATISTICS db;\r
2154   //\r
2155   // wait upto one second (each wait is 100 micro s)\r
2156   //\r
2157   UINT32            Wait;\r
2158   Wait = 10000;\r
2159   wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);\r
2160 \r
2161   //\r
2162   // Clear statistics done marker.\r
2163   //\r
2164   AdapterInfo->statistics->done_marker = 0;\r
2165 \r
2166   //\r
2167   // Issue statistics dump (or dump w/ reset) command.\r
2168   //\r
2169   OutByte (\r
2170     AdapterInfo,\r
2171     (UINT8) (DBsize ? CU_SHOWSTATS : CU_DUMPSTATS),\r
2172     (UINT32) (AdapterInfo->ioaddr + SCBCmd)\r
2173     );\r
2174 \r
2175   //\r
2176   // Wait for command to complete.\r
2177   //\r
2178   // zero the db here just to chew up a little more time.\r
2179   //\r
2180 \r
2181   ZeroMem ((VOID *) &db, sizeof db);\r
2182 \r
2183   while (Wait != 0) {\r
2184     //\r
2185     // Wait a bit before checking.\r
2186     //\r
2187 \r
2188     DelayIt (AdapterInfo, 100);\r
2189 \r
2190     //\r
2191     // Look for done marker at end of statistics.\r
2192     //\r
2193 \r
2194     switch (AdapterInfo->statistics->done_marker) {\r
2195     case 0xA005:\r
2196     case 0xA007:\r
2197       break;\r
2198 \r
2199     default:\r
2200       Wait--;\r
2201       continue;\r
2202     }\r
2203 \r
2204     //\r
2205     // if we did not "continue" from the above switch, we are done,\r
2206     //\r
2207     break;\r
2208   }\r
2209 \r
2210   //\r
2211   // If this is a reset, we are out of here!\r
2212   //\r
2213   if (DBsize == 0) {\r
2214     return PXE_STATCODE_SUCCESS;\r
2215   }\r
2216 \r
2217   //\r
2218   // Convert NIC statistics counter format to EFI/UNDI\r
2219   // specification statistics counter format.\r
2220   //\r
2221 \r
2222   //\r
2223   //                54 3210 fedc ba98 7654 3210\r
2224   // db.Supported = 01 0000 0100 1101 0001 0111;\r
2225   //\r
2226   db.Supported = 0x104D17;\r
2227 \r
2228   //\r
2229   // Statistics from the NIC\r
2230   //\r
2231 \r
2232   db.Data[0x01] = AdapterInfo->statistics->rx_good_frames;\r
2233 \r
2234   db.Data[0x02] = AdapterInfo->statistics->rx_runt_errs;\r
2235 \r
2236   db.Data[0x08] = AdapterInfo->statistics->rx_crc_errs +\r
2237                   AdapterInfo->statistics->rx_align_errs;\r
2238 \r
2239   db.Data[0x04] = db.Data[0x02] +\r
2240                   db.Data[0x08] +\r
2241                   AdapterInfo->statistics->rx_resource_errs +\r
2242                   AdapterInfo->statistics->rx_overrun_errs;\r
2243 \r
2244   db.Data[0x00] = db.Data[0x01] + db.Data[0x04];\r
2245 \r
2246   db.Data[0x0B] = AdapterInfo->statistics->tx_good_frames;\r
2247 \r
2248   db.Data[0x0E] = AdapterInfo->statistics->tx_coll16_errs +\r
2249     AdapterInfo->statistics->tx_late_colls +\r
2250     AdapterInfo->statistics->tx_underruns +\r
2251     AdapterInfo->statistics->tx_one_colls +\r
2252     AdapterInfo->statistics->tx_multi_colls;\r
2253 \r
2254   db.Data[0x14] = AdapterInfo->statistics->tx_total_colls;\r
2255 \r
2256   db.Data[0x0A] = db.Data[0x0B] +\r
2257                   db.Data[0x0E] +\r
2258                   AdapterInfo->statistics->tx_lost_carrier;\r
2259 \r
2260   if (DBsize > sizeof db) {\r
2261     DBsize = sizeof db;\r
2262   }\r
2263 \r
2264   CopyMem ((VOID *) (UINTN) DBaddr, (VOID *) &db, (UINTN) DBsize);\r
2265 \r
2266   return PXE_STATCODE_SUCCESS;\r
2267 }\r
2268 \r
2269 \r
2270 /**\r
2271   TODO: Add function description\r
2272 \r
2273   @param  AdapterInfo                     TODO: add argument description\r
2274   @param  OpFlags                         TODO: add argument description\r
2275 \r
2276   @return TODO: add return values\r
2277 \r
2278 **/\r
2279 UINTN\r
2280 E100bReset (\r
2281   IN NIC_DATA_INSTANCE *AdapterInfo,\r
2282   IN INT32             OpFlags\r
2283   )\r
2284 {\r
2285 \r
2286   UINT16  save_filter;\r
2287   //\r
2288   // disable the interrupts\r
2289   //\r
2290   OutWord (AdapterInfo, INT_MASK, AdapterInfo->ioaddr + SCBCmd);\r
2291 \r
2292   //\r
2293   // wait for the tx queue to complete\r
2294   //\r
2295   CheckCBList (AdapterInfo);\r
2296 \r
2297   XmitWaitForCompletion (AdapterInfo);\r
2298 \r
2299   if (AdapterInfo->Receive_Started) {\r
2300     StopRU (AdapterInfo);\r
2301   }\r
2302 \r
2303   InitializeChip (AdapterInfo);\r
2304 \r
2305   //\r
2306   // check the opflags and restart receive filters\r
2307   //\r
2308   if ((OpFlags & PXE_OPFLAGS_RESET_DISABLE_FILTERS) == 0) {\r
2309 \r
2310     save_filter = AdapterInfo->Rx_Filter;\r
2311     //\r
2312     // if we give the filter same as Rx_Filter,\r
2313     // this routine will not set mcast list (it thinks there is no change)\r
2314     // to force it, we will reset that flag in the Rx_Filter\r
2315     //\r
2316     AdapterInfo->Rx_Filter &= (~PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST);\r
2317     E100bSetfilter (AdapterInfo, save_filter, (UINT64) 0, (UINT32) 0);\r
2318   }\r
2319 \r
2320   if ((OpFlags & PXE_OPFLAGS_RESET_DISABLE_INTERRUPTS) != 0) {\r
2321     //\r
2322     // disable the interrupts\r
2323     //\r
2324     AdapterInfo->int_mask = 0;\r
2325   }\r
2326   //\r
2327   // else leave the interrupt in the pre-set state!!!\r
2328   //\r
2329   E100bSetInterruptState (AdapterInfo);\r
2330 \r
2331   return 0;\r
2332 }\r
2333 \r
2334 \r
2335 /**\r
2336   TODO: Add function description\r
2337 \r
2338   @param  AdapterInfo                     TODO: add argument description\r
2339 \r
2340   @return TODO: add return values\r
2341 \r
2342 **/\r
2343 UINTN\r
2344 E100bShutdown (\r
2345   IN NIC_DATA_INSTANCE *AdapterInfo\r
2346   )\r
2347 {\r
2348   //\r
2349   // disable the interrupts\r
2350   //\r
2351   OutWord (AdapterInfo, INT_MASK, AdapterInfo->ioaddr + SCBCmd);\r
2352 \r
2353   //\r
2354   // stop the receive unit\r
2355   //\r
2356   if (AdapterInfo->Receive_Started) {\r
2357     StopRU (AdapterInfo);\r
2358   }\r
2359 \r
2360   //\r
2361   // wait for the tx queue to complete\r
2362   //\r
2363   CheckCBList (AdapterInfo);\r
2364   if (AdapterInfo->FreeCBCount != AdapterInfo->TxBufCnt) {\r
2365     wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);\r
2366   }\r
2367 \r
2368   //\r
2369   // we do not want to reset the phy, it takes a long time to renegotiate the\r
2370   // link after that (3-4 seconds)\r
2371   //\r
2372   InitializeChip (AdapterInfo);\r
2373   SelectiveReset (AdapterInfo);\r
2374   return 0;\r
2375 }\r
2376 \r
2377 \r
2378 /**\r
2379   This routine will write a value to the specified MII register\r
2380   of an external MDI compliant device (e.g. PHY 100).  The command will\r
2381   execute in polled mode.\r
2382 \r
2383   @param  AdapterInfo                     pointer to the structure that contains\r
2384                                           the NIC's context.\r
2385   @param  RegAddress                      The MII register that we are writing to\r
2386   @param  PhyAddress                      The MDI address of the Phy component.\r
2387   @param  DataValue                       The value that we are writing to the MII\r
2388                                           register.\r
2389 \r
2390   @return nothing\r
2391 \r
2392 **/\r
2393 VOID\r
2394 MdiWrite (\r
2395   IN NIC_DATA_INSTANCE *AdapterInfo,\r
2396   IN UINT8             RegAddress,\r
2397   IN UINT8             PhyAddress,\r
2398   IN UINT16            DataValue\r
2399   )\r
2400 {\r
2401   UINT32  WriteCommand;\r
2402 \r
2403   WriteCommand = ((UINT32) DataValue) |\r
2404                  ((UINT32)(RegAddress << 16)) |\r
2405                  ((UINT32)(PhyAddress << 21)) |\r
2406                  ((UINT32)(MDI_WRITE << 26));\r
2407 \r
2408   //\r
2409   // Issue the write command to the MDI control register.\r
2410   //\r
2411   OutLong (AdapterInfo, WriteCommand, AdapterInfo->ioaddr + SCBCtrlMDI);\r
2412 \r
2413   //\r
2414   // wait 20usec before checking status\r
2415   //\r
2416   DelayIt (AdapterInfo, 20);\r
2417 \r
2418   //\r
2419   // poll for the mdi write to complete\r
2420   while ((InLong (AdapterInfo, AdapterInfo->ioaddr + SCBCtrlMDI) &\r
2421                     MDI_PHY_READY) == 0){\r
2422     DelayIt (AdapterInfo, 20);\r
2423   }\r
2424 }\r
2425 \r
2426 \r
2427 /**\r
2428   This routine will read a value from the specified MII register\r
2429   of an external MDI compliant device (e.g. PHY 100), and return\r
2430   it to the calling routine.  The command will execute in polled mode.\r
2431 \r
2432   @param  AdapterInfo                     pointer to the structure that contains\r
2433                                           the NIC's context.\r
2434   @param  RegAddress                      The MII register that we are reading from\r
2435   @param  PhyAddress                      The MDI address of the Phy component.\r
2436   @param  DataValue                       pointer to the value that we read from\r
2437                                           the MII register.\r
2438 \r
2439 \r
2440 **/\r
2441 VOID\r
2442 MdiRead (\r
2443   IN NIC_DATA_INSTANCE *AdapterInfo,\r
2444   IN UINT8             RegAddress,\r
2445   IN UINT8             PhyAddress,\r
2446   IN OUT UINT16        *DataValue\r
2447   )\r
2448 {\r
2449   UINT32  ReadCommand;\r
2450 \r
2451   ReadCommand = ((UINT32) (RegAddress << 16)) |\r
2452                 ((UINT32) (PhyAddress << 21)) |\r
2453                 ((UINT32) (MDI_READ << 26));\r
2454 \r
2455   //\r
2456   // Issue the read command to the MDI control register.\r
2457   //\r
2458   OutLong (AdapterInfo, ReadCommand, AdapterInfo->ioaddr + SCBCtrlMDI);\r
2459 \r
2460   //\r
2461   // wait 20usec before checking status\r
2462   //\r
2463   DelayIt (AdapterInfo, 20);\r
2464 \r
2465   //\r
2466   // poll for the mdi read to complete\r
2467   //\r
2468   while ((InLong (AdapterInfo, AdapterInfo->ioaddr + SCBCtrlMDI) &\r
2469           MDI_PHY_READY) == 0) {\r
2470     DelayIt (AdapterInfo, 20);\r
2471 \r
2472   }\r
2473 \r
2474   *DataValue = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBCtrlMDI);\r
2475 }\r
2476 \r
2477 \r
2478 /**\r
2479   This routine will reset the PHY that the adapter is currently\r
2480   configured to use.\r
2481 \r
2482   @param  AdapterInfo                     pointer to the structure that contains\r
2483                                           the NIC's context.\r
2484 \r
2485 \r
2486 **/\r
2487 VOID\r
2488 PhyReset (\r
2489   NIC_DATA_INSTANCE *AdapterInfo\r
2490   )\r
2491 {\r
2492   UINT16  MdiControlReg;\r
2493 \r
2494   MdiControlReg = (MDI_CR_AUTO_SELECT |\r
2495                   MDI_CR_RESTART_AUTO_NEG |\r
2496                   MDI_CR_RESET);\r
2497 \r
2498   //\r
2499   // Write the MDI control register with our new Phy configuration\r
2500   //\r
2501   MdiWrite (\r
2502     AdapterInfo,\r
2503     MDI_CONTROL_REG,\r
2504     AdapterInfo->PhyAddress,\r
2505     MdiControlReg\r
2506     );\r
2507 \r
2508   return ;\r
2509 }\r
2510 \r
2511 \r
2512 /**\r
2513   This routine will detect what phy we are using, set the line\r
2514   speed, FDX or HDX, and configure the phy if necessary.\r
2515   The following combinations are supported:\r
2516   - TX or T4 PHY alone at PHY address 1\r
2517   - T4 or TX PHY at address 1 and MII PHY at address 0\r
2518   - 82503 alone (10Base-T mode, no full duplex support)\r
2519   - 82503 and MII PHY (TX or T4) at address 0\r
2520   The sequence / priority of detection is as follows:\r
2521   - PHY 1 with cable termination\r
2522   - PHY 0 with cable termination\r
2523   - PHY 1 (if found) without cable termination\r
2524   - 503 interface\r
2525   Additionally auto-negotiation capable (NWAY) and parallel\r
2526   detection PHYs are supported. The flow-chart is described in\r
2527   the 82557 software writer's manual.\r
2528   NOTE:  1.  All PHY MDI registers are read in polled mode.\r
2529   2.  The routines assume that the 82557 has been RESET and we have\r
2530   obtained the virtual memory address of the CSR.\r
2531   3.  PhyDetect will not RESET the PHY.\r
2532   4.  If FORCEFDX is set, SPEED should also be set. The driver will\r
2533   check the values for inconsistency with the detected PHY\r
2534   technology.\r
2535   5.  PHY 1 (the PHY on the adapter) may have an address in the range\r
2536   1 through 31 inclusive. The driver will accept addresses in\r
2537   this range.\r
2538   6.  Driver ignores FORCEFDX and SPEED overrides if a 503 interface\r
2539   is detected.\r
2540 \r
2541   @param  AdapterInfo                     pointer to the structure that contains\r
2542                                           the NIC's context.\r
2543 \r
2544   @retval TRUE                            If a Phy was detected, and configured\r
2545                                           correctly.\r
2546   @retval FALSE                           If a valid phy could not be detected and\r
2547                                           configured.\r
2548 \r
2549 **/\r
2550 BOOLEAN\r
2551 PhyDetect (\r
2552   NIC_DATA_INSTANCE *AdapterInfo\r
2553   )\r
2554 {\r
2555   UINT16  *eedata;\r
2556   UINT16  MdiControlReg;\r
2557   UINT16  MdiStatusReg;\r
2558   BOOLEAN FoundPhy1;\r
2559   UINT8   ReNegotiateTime;\r
2560 \r
2561   eedata          = (UINT16 *) (&AdapterInfo->NVData[0]);\r
2562 \r
2563   FoundPhy1       = FALSE;\r
2564   ReNegotiateTime = 35;\r
2565   //\r
2566   // EEPROM word [6] contains the Primary PHY record in which the least 3 bits\r
2567   // indicate the PHY address\r
2568   // and word [7] contains the secondary PHY record\r
2569   //\r
2570   AdapterInfo->PhyRecord[0] = eedata[6];\r
2571   AdapterInfo->PhyRecord[1] = eedata[7];\r
2572   AdapterInfo->PhyAddress   = (UINT8) (AdapterInfo->PhyRecord[0] & 7);\r
2573 \r
2574   //\r
2575   // Check for a phy address over-ride of 32 which indicates force use of 82503\r
2576   // not detecting the link in this case\r
2577   //\r
2578   if (AdapterInfo->PhyAddress == 32) {\r
2579     //\r
2580     // 503 interface over-ride\r
2581     // Record the current speed and duplex.  We will be in half duplex\r
2582     // mode unless the user used the force full duplex over-ride.\r
2583     //\r
2584     AdapterInfo->LinkSpeed = 10;\r
2585     return (TRUE);\r
2586   }\r
2587 \r
2588   //\r
2589   // If the Phy Address is between 1-31 then we must first look for phy 1,\r
2590   // at that address.\r
2591   //\r
2592   if ((AdapterInfo->PhyAddress > 0) && (AdapterInfo->PhyAddress < 32)) {\r
2593 \r
2594     //\r
2595     // Read the MDI control and status registers at phy 1\r
2596     // and check if we found a valid phy\r
2597     //\r
2598     MdiRead (\r
2599       AdapterInfo,\r
2600       MDI_CONTROL_REG,\r
2601       AdapterInfo->PhyAddress,\r
2602       &MdiControlReg\r
2603       );\r
2604 \r
2605     MdiRead (\r
2606       AdapterInfo,\r
2607       MDI_STATUS_REG,\r
2608       AdapterInfo->PhyAddress,\r
2609       &MdiStatusReg\r
2610       );\r
2611 \r
2612     if (!((MdiControlReg == 0xffff) ||\r
2613           ((MdiStatusReg == 0) && (MdiControlReg == 0)))) {\r
2614 \r
2615       //\r
2616       // we have a valid phy1\r
2617       // Read the status register again because of sticky bits\r
2618       //\r
2619       FoundPhy1 = TRUE;\r
2620       MdiRead (\r
2621         AdapterInfo,\r
2622         MDI_STATUS_REG,\r
2623         AdapterInfo->PhyAddress,\r
2624         &MdiStatusReg\r
2625         );\r
2626 \r
2627       //\r
2628       // If there is a valid link then use this Phy.\r
2629       //\r
2630       if (MdiStatusReg & MDI_SR_LINK_STATUS) {\r
2631         return (SetupPhy(AdapterInfo));\r
2632       }\r
2633     }\r
2634   }\r
2635 \r
2636   //\r
2637   // Next try to detect a PHY at address 0x00 because there was no Phy 1,\r
2638   // or Phy 1 didn't have link, or we had a phy 0 over-ride\r
2639   //\r
2640 \r
2641   //\r
2642   // Read the MDI control and status registers at phy 0\r
2643   //\r
2644   MdiRead (AdapterInfo, MDI_CONTROL_REG, 0, &MdiControlReg);\r
2645   MdiRead (AdapterInfo, MDI_STATUS_REG, 0, &MdiStatusReg);\r
2646 \r
2647   //\r
2648   // check if we found a valid phy 0\r
2649   //\r
2650   if (((MdiControlReg == 0xffff) ||\r
2651        ((MdiStatusReg == 0) && (MdiControlReg == 0)))) {\r
2652 \r
2653     //\r
2654     // we don't have a valid phy at address 0\r
2655     // if phy address was forced to 0, then error out because we\r
2656     // didn't find a phy at that address\r
2657     //\r
2658     if (AdapterInfo->PhyAddress == 0x0000) {\r
2659       return (FALSE);\r
2660     } else {\r
2661       //\r
2662       // at this point phy1 does not have link and there is no phy 0 at all\r
2663       // if we are forced to detect the cable, error out here!\r
2664       //\r
2665       if (AdapterInfo->CableDetect != 0) {\r
2666         return FALSE;\r
2667 \r
2668       }\r
2669 \r
2670       if (FoundPhy1) {\r
2671         //\r
2672         // no phy 0, but there is a phy 1 (no link I guess), so use phy 1\r
2673         //\r
2674         return SetupPhy (AdapterInfo);\r
2675       } else {\r
2676         //\r
2677         // didn't find phy 0 or phy 1, so assume a 503 interface\r
2678         //\r
2679         AdapterInfo->PhyAddress = 32;\r
2680 \r
2681         //\r
2682         // Record the current speed and duplex.  We'll be in half duplex\r
2683         // mode unless the user used the force full duplex over-ride.\r
2684         //\r
2685         AdapterInfo->LinkSpeed = 10;\r
2686         return (TRUE);\r
2687       }\r
2688     }\r
2689   } else {\r
2690     //\r
2691     // We have a valid phy at address 0.  If phy 0 has a link then we use\r
2692     // phy 0.  If Phy 0 doesn't have a link then we use Phy 1 (no link)\r
2693     // if phy 1 is present, or phy 0 if phy 1 is not present\r
2694     // If phy 1 was present, then we must isolate phy 1 before we enable\r
2695     // phy 0 to see if Phy 0 has a link.\r
2696     //\r
2697     if (FoundPhy1) {\r
2698       //\r
2699       // isolate phy 1\r
2700       //\r
2701       MdiWrite (\r
2702         AdapterInfo,\r
2703         MDI_CONTROL_REG,\r
2704         AdapterInfo->PhyAddress,\r
2705         MDI_CR_ISOLATE\r
2706         );\r
2707 \r
2708       //\r
2709       // wait 100 microseconds for the phy to isolate.\r
2710       //\r
2711       DelayIt (AdapterInfo, 100);\r
2712     }\r
2713 \r
2714     //\r
2715     // Since this Phy is at address 0, we must enable it.  So clear\r
2716     // the isolate bit, and set the auto-speed select bit\r
2717     //\r
2718     MdiWrite (\r
2719       AdapterInfo,\r
2720       MDI_CONTROL_REG,\r
2721       0,\r
2722       MDI_CR_AUTO_SELECT\r
2723       );\r
2724 \r
2725     //\r
2726     // wait 100 microseconds for the phy to be enabled.\r
2727     //\r
2728     DelayIt (AdapterInfo, 100);\r
2729 \r
2730     //\r
2731     // restart the auto-negotion process\r
2732     //\r
2733     MdiWrite (\r
2734       AdapterInfo,\r
2735       MDI_CONTROL_REG,\r
2736       0,\r
2737       MDI_CR_RESTART_AUTO_NEG | MDI_CR_AUTO_SELECT\r
2738       );\r
2739 \r
2740     //\r
2741     // wait no more than 3.5 seconds for auto-negotiation to complete\r
2742     //\r
2743     while (ReNegotiateTime) {\r
2744       //\r
2745       // Read the status register twice because of sticky bits\r
2746       //\r
2747       MdiRead (AdapterInfo, MDI_STATUS_REG, 0, &MdiStatusReg);\r
2748       MdiRead (AdapterInfo, MDI_STATUS_REG, 0, &MdiStatusReg);\r
2749 \r
2750       if (MdiStatusReg & MDI_SR_AUTO_NEG_COMPLETE) {\r
2751         break;\r
2752       }\r
2753 \r
2754       DelayIt (AdapterInfo, 100);\r
2755       ReNegotiateTime--;\r
2756     }\r
2757 \r
2758     //\r
2759     // Read the status register again because of sticky bits\r
2760     //\r
2761     MdiRead (AdapterInfo, MDI_STATUS_REG, 0, &MdiStatusReg);\r
2762 \r
2763     //\r
2764     // If the link was not set\r
2765     //\r
2766     if ((MdiStatusReg & MDI_SR_LINK_STATUS) == 0) {\r
2767       //\r
2768       // PHY1 does not have a link and phy 0 does not have a link\r
2769       // do not proceed if we need to detect the link!\r
2770       //\r
2771       if (AdapterInfo->CableDetect != 0) {\r
2772         return FALSE;\r
2773       }\r
2774 \r
2775       //\r
2776       // the link wasn't set, so use phy 1 if phy 1 was present\r
2777       //\r
2778       if (FoundPhy1) {\r
2779         //\r
2780         // isolate phy 0\r
2781         //\r
2782         MdiWrite (AdapterInfo, MDI_CONTROL_REG, 0, MDI_CR_ISOLATE);\r
2783 \r
2784         //\r
2785         // wait 100 microseconds for the phy to isolate.\r
2786         //\r
2787         DelayIt (AdapterInfo, 100);\r
2788 \r
2789         //\r
2790         // Now re-enable PHY 1\r
2791         //\r
2792         MdiWrite (\r
2793           AdapterInfo,\r
2794           MDI_CONTROL_REG,\r
2795           AdapterInfo->PhyAddress,\r
2796           MDI_CR_AUTO_SELECT\r
2797           );\r
2798 \r
2799         //\r
2800         // wait 100 microseconds for the phy to be enabled\r
2801         //\r
2802         DelayIt (AdapterInfo, 100);\r
2803 \r
2804         //\r
2805         // restart the auto-negotion process\r
2806         //\r
2807         MdiWrite (\r
2808           AdapterInfo,\r
2809           MDI_CONTROL_REG,\r
2810           AdapterInfo->PhyAddress,\r
2811           MDI_CR_RESTART_AUTO_NEG | MDI_CR_AUTO_SELECT\r
2812           );\r
2813 \r
2814         //\r
2815         // Don't wait for it to complete (we didn't have link earlier)\r
2816         //\r
2817         return (SetupPhy (AdapterInfo));\r
2818       }\r
2819     }\r
2820 \r
2821     //\r
2822     // Definitely using Phy 0\r
2823     //\r
2824     AdapterInfo->PhyAddress = 0;\r
2825     return (SetupPhy(AdapterInfo));\r
2826   }\r
2827 }\r
2828 \r
2829 \r
2830 /**\r
2831   This routine will setup phy 1 or phy 0 so that it is configured\r
2832   to match a speed and duplex over-ride option.  If speed or\r
2833   duplex mode is not explicitly specified in the registry, the\r
2834   driver will skip the speed and duplex over-ride code, and\r
2835   assume the adapter is automatically setting the line speed, and\r
2836   the duplex mode.  At the end of this routine, any truly Phy\r
2837   specific code will be executed (each Phy has its own quirks,\r
2838   and some require that certain special bits are set).\r
2839   NOTE:  The driver assumes that SPEED and FORCEFDX are specified at the\r
2840   same time. If FORCEDPX is set without speed being set, the driver\r
2841   will encouter a fatal error and log a message into the event viewer.\r
2842 \r
2843   @param  AdapterInfo                     pointer to the structure that contains\r
2844                                           the NIC's context.\r
2845 \r
2846   @retval TRUE                            If the phy could be configured correctly\r
2847   @retval FALSE                           If the phy couldn't be configured\r
2848                                           correctly, because an unsupported\r
2849                                           over-ride option was used\r
2850 \r
2851 **/\r
2852 BOOLEAN\r
2853 SetupPhy (\r
2854   IN NIC_DATA_INSTANCE *AdapterInfo\r
2855   )\r
2856 {\r
2857   UINT16  MdiControlReg;\r
2858   UINT16  MdiStatusReg;\r
2859   UINT16  MdiIdLowReg;\r
2860   UINT16  MdiIdHighReg;\r
2861   UINT16  MdiMiscReg;\r
2862   UINT32  PhyId;\r
2863   BOOLEAN ForcePhySetting;\r
2864 \r
2865   ForcePhySetting = FALSE;\r
2866 \r
2867   //\r
2868   // If we are NOT forcing a setting for line speed or full duplex, then\r
2869   // we won't force a link setting, and we'll jump down to the phy\r
2870   // specific code.\r
2871   //\r
2872   if (((AdapterInfo->LinkSpeedReq) || (AdapterInfo->DuplexReq))) {\r
2873     //\r
2874     // Find out what kind of technology this Phy is capable of.\r
2875     //\r
2876     MdiRead (\r
2877       AdapterInfo,\r
2878       MDI_STATUS_REG,\r
2879       AdapterInfo->PhyAddress,\r
2880       &MdiStatusReg\r
2881       );\r
2882 \r
2883     //\r
2884     // Read the MDI control register at our phy\r
2885     //\r
2886     MdiRead (\r
2887       AdapterInfo,\r
2888       MDI_CONTROL_REG,\r
2889       AdapterInfo->PhyAddress,\r
2890       &MdiControlReg\r
2891       );\r
2892 \r
2893     //\r
2894     // Now check the validity of our forced option.  If the force option is\r
2895     // valid, then force the setting.  If the force option is not valid,\r
2896     // we'll set a flag indicating that we should error out.\r
2897     //\r
2898 \r
2899     //\r
2900     // If speed is forced to 10mb\r
2901     //\r
2902     if (AdapterInfo->LinkSpeedReq == 10) {\r
2903       //\r
2904       // If half duplex is forced\r
2905       //\r
2906       if ((AdapterInfo->DuplexReq & PXE_FORCE_HALF_DUPLEX) != 0) {\r
2907         if (MdiStatusReg & MDI_SR_10T_HALF_DPX) {\r
2908 \r
2909           MdiControlReg &= ~(MDI_CR_10_100 | MDI_CR_AUTO_SELECT | MDI_CR_FULL_HALF);\r
2910           ForcePhySetting = TRUE;\r
2911         }\r
2912       } else if ((AdapterInfo->DuplexReq & PXE_FORCE_FULL_DUPLEX) != 0) {\r
2913 \r
2914         //\r
2915         // If full duplex is forced\r
2916         //\r
2917         if (MdiStatusReg & MDI_SR_10T_FULL_DPX) {\r
2918 \r
2919           MdiControlReg &= ~(MDI_CR_10_100 | MDI_CR_AUTO_SELECT);\r
2920           MdiControlReg |= MDI_CR_FULL_HALF;\r
2921           ForcePhySetting = TRUE;\r
2922         }\r
2923       } else {\r
2924         //\r
2925         // If auto duplex (we actually set phy to 1/2)\r
2926         //\r
2927         if (MdiStatusReg & (MDI_SR_10T_FULL_DPX | MDI_SR_10T_HALF_DPX)) {\r
2928 \r
2929           MdiControlReg &= ~(MDI_CR_10_100 | MDI_CR_AUTO_SELECT | MDI_CR_FULL_HALF);\r
2930           ForcePhySetting = TRUE;\r
2931         }\r
2932       }\r
2933     }\r
2934 \r
2935     //\r
2936     // If speed is forced to 100mb\r
2937     //\r
2938     else if (AdapterInfo->LinkSpeedReq == 100) {\r
2939       //\r
2940       // If half duplex is forced\r
2941       //\r
2942       if ((AdapterInfo->DuplexReq & PXE_FORCE_HALF_DUPLEX) != 0) {\r
2943         if (MdiStatusReg & (MDI_SR_TX_HALF_DPX | MDI_SR_T4_CAPABLE)) {\r
2944 \r
2945           MdiControlReg &= ~(MDI_CR_AUTO_SELECT | MDI_CR_FULL_HALF);\r
2946           MdiControlReg |= MDI_CR_10_100;\r
2947           ForcePhySetting = TRUE;\r
2948         }\r
2949       } else if ((AdapterInfo->DuplexReq & PXE_FORCE_FULL_DUPLEX) != 0) {\r
2950         //\r
2951         // If full duplex is forced\r
2952         //\r
2953         if (MdiStatusReg & MDI_SR_TX_FULL_DPX) {\r
2954           MdiControlReg &= ~MDI_CR_AUTO_SELECT;\r
2955           MdiControlReg |= (MDI_CR_10_100 | MDI_CR_FULL_HALF);\r
2956           ForcePhySetting = TRUE;\r
2957         }\r
2958       } else {\r
2959         //\r
2960         // If auto duplex (we set phy to 1/2)\r
2961         //\r
2962         if (MdiStatusReg & (MDI_SR_TX_HALF_DPX | MDI_SR_T4_CAPABLE)) {\r
2963 \r
2964           MdiControlReg &= ~(MDI_CR_AUTO_SELECT | MDI_CR_FULL_HALF);\r
2965           MdiControlReg |= MDI_CR_10_100;\r
2966           ForcePhySetting = TRUE;\r
2967         }\r
2968       }\r
2969     }\r
2970 \r
2971     if (!ForcePhySetting) {\r
2972       return (FALSE);\r
2973     }\r
2974 \r
2975     //\r
2976     // Write the MDI control register with our new Phy configuration\r
2977     //\r
2978     MdiWrite (\r
2979       AdapterInfo,\r
2980       MDI_CONTROL_REG,\r
2981       AdapterInfo->PhyAddress,\r
2982       MdiControlReg\r
2983       );\r
2984 \r
2985     //\r
2986     // wait 100 milliseconds for auto-negotiation to complete\r
2987     //\r
2988     DelayIt (AdapterInfo, 100);\r
2989   }\r
2990 \r
2991   //\r
2992   // Find out specifically what Phy this is.  We do this because for certain\r
2993   // phys there are specific bits that must be set so that the phy and the\r
2994   // 82557 work together properly.\r
2995   //\r
2996 \r
2997   MdiRead (\r
2998     AdapterInfo,\r
2999     PHY_ID_REG_1,\r
3000     AdapterInfo->PhyAddress,\r
3001     &MdiIdLowReg\r
3002     );\r
3003   MdiRead (\r
3004     AdapterInfo,\r
3005     PHY_ID_REG_2,\r
3006     AdapterInfo->PhyAddress,\r
3007     &MdiIdHighReg\r
3008     );\r
3009 \r
3010   PhyId = ((UINT32) MdiIdLowReg | ((UINT32) MdiIdHighReg << 16));\r
3011 \r
3012   //\r
3013   // And out the revsion field of the Phy ID so that we'll be able to detect\r
3014   // future revs of the same Phy.\r
3015   //\r
3016   PhyId &= PHY_MODEL_REV_ID_MASK;\r
3017 \r
3018   //\r
3019   // Handle the National TX\r
3020   //\r
3021   if (PhyId == PHY_NSC_TX) {\r
3022 \r
3023     MdiRead (\r
3024       AdapterInfo,\r
3025       NSC_CONG_CONTROL_REG,\r
3026       AdapterInfo->PhyAddress,\r
3027       &MdiMiscReg\r
3028       );\r
3029 \r
3030     MdiMiscReg |= (NSC_TX_CONG_TXREADY | NSC_TX_CONG_F_CONNECT);\r
3031 \r
3032     MdiWrite (\r
3033       AdapterInfo,\r
3034       NSC_CONG_CONTROL_REG,\r
3035       AdapterInfo->PhyAddress,\r
3036       MdiMiscReg\r
3037       );\r
3038   }\r
3039 \r
3040   FindPhySpeedAndDpx (AdapterInfo, PhyId);\r
3041 \r
3042   //\r
3043   // We put a hardware fix on to our adapters to work-around the PHY_100 errata\r
3044   // described below.  The following code is only compiled in, if we wanted\r
3045   // to attempt a software workaround to the PHY_100 A/B step problem.\r
3046   //\r
3047 \r
3048   return (TRUE);\r
3049 }\r
3050 \r
3051 \r
3052 /**\r
3053   This routine will figure out what line speed and duplex mode\r
3054   the PHY is currently using.\r
3055 \r
3056   @param  AdapterInfo                     pointer to the structure that contains\r
3057                                           the NIC's context.\r
3058   @param  PhyId                           The ID of the PHY in question.\r
3059 \r
3060   @return NOTHING\r
3061 \r
3062 **/\r
3063 VOID\r
3064 FindPhySpeedAndDpx (\r
3065   IN NIC_DATA_INSTANCE *AdapterInfo,\r
3066   IN UINT32            PhyId\r
3067   )\r
3068 {\r
3069   UINT16  MdiStatusReg;\r
3070   UINT16  MdiMiscReg;\r
3071   UINT16  MdiOwnAdReg;\r
3072   UINT16  MdiLinkPartnerAdReg;\r
3073 \r
3074   //\r
3075   // If there was a speed and/or duplex override, then set our current\r
3076   // value accordingly\r
3077   //\r
3078   AdapterInfo->LinkSpeed  = AdapterInfo->LinkSpeedReq;\r
3079   AdapterInfo->Duplex = (UINT8) ((AdapterInfo->DuplexReq & PXE_FORCE_FULL_DUPLEX) ?\r
3080                         FULL_DUPLEX : HALF_DUPLEX);\r
3081 \r
3082   //\r
3083   // If speed and duplex were forced, then we know our current settings, so\r
3084   // we'll just return.  Otherwise, we'll need to figure out what NWAY set\r
3085   // us to.\r
3086   //\r
3087   if (AdapterInfo->LinkSpeed && AdapterInfo->Duplex) {\r
3088     return ;\r
3089 \r
3090   }\r
3091   //\r
3092   // If we didn't have a valid link, then we'll assume that our current\r
3093   // speed is 10mb half-duplex.\r
3094   //\r
3095 \r
3096   //\r
3097   // Read the status register twice because of sticky bits\r
3098   //\r
3099   MdiRead (\r
3100     AdapterInfo,\r
3101     MDI_STATUS_REG,\r
3102     AdapterInfo->PhyAddress,\r
3103     &MdiStatusReg\r
3104     );\r
3105   MdiRead (\r
3106     AdapterInfo,\r
3107     MDI_STATUS_REG,\r
3108     AdapterInfo->PhyAddress,\r
3109     &MdiStatusReg\r
3110     );\r
3111 \r
3112   //\r
3113   // If there wasn't a valid link then use default speed & duplex\r
3114   //\r
3115   if (!(MdiStatusReg & MDI_SR_LINK_STATUS)) {\r
3116 \r
3117     AdapterInfo->LinkSpeed  = 10;\r
3118     AdapterInfo->Duplex     = HALF_DUPLEX;\r
3119     return ;\r
3120   }\r
3121 \r
3122   //\r
3123   // If this is an Intel PHY (a T4 PHY_100 or a TX PHY_TX), then read bits\r
3124   // 1 and 0 of extended register 0, to get the current speed and duplex\r
3125   // settings.\r
3126   //\r
3127   if ((PhyId == PHY_100_A) || (PhyId == PHY_100_C) || (PhyId == PHY_TX_ID)) {\r
3128     //\r
3129     // Read extended register 0\r
3130     //\r
3131     MdiRead (\r
3132       AdapterInfo,\r
3133       EXTENDED_REG_0,\r
3134       AdapterInfo->PhyAddress,\r
3135       &MdiMiscReg\r
3136       );\r
3137 \r
3138     //\r
3139     // Get current speed setting\r
3140     //\r
3141     if (MdiMiscReg & PHY_100_ER0_SPEED_INDIC) {\r
3142       AdapterInfo->LinkSpeed = 100;\r
3143     } else {\r
3144       AdapterInfo->LinkSpeed = 10;\r
3145     }\r
3146 \r
3147     //\r
3148     // Get current duplex setting -- if bit is set then FDX is enabled\r
3149     //\r
3150     if (MdiMiscReg & PHY_100_ER0_FDX_INDIC) {\r
3151       AdapterInfo->Duplex = FULL_DUPLEX;\r
3152     } else {\r
3153       AdapterInfo->Duplex = HALF_DUPLEX;\r
3154     }\r
3155 \r
3156     return ;\r
3157   }\r
3158   //\r
3159   // Read our link partner's advertisement register\r
3160   //\r
3161   MdiRead (\r
3162     AdapterInfo,\r
3163     AUTO_NEG_LINK_PARTNER_REG,\r
3164     AdapterInfo->PhyAddress,\r
3165     &MdiLinkPartnerAdReg\r
3166     );\r
3167 \r
3168   //\r
3169   // See if Auto-Negotiation was complete (bit 5, reg 1)\r
3170   //\r
3171   MdiRead (\r
3172     AdapterInfo,\r
3173     MDI_STATUS_REG,\r
3174     AdapterInfo->PhyAddress,\r
3175     &MdiStatusReg\r
3176     );\r
3177 \r
3178   //\r
3179   // If a True NWAY connection was made, then we can detect speed/duplex by\r
3180   // ANDing our adapter's advertised abilities with our link partner's\r
3181   // advertised ablilities, and then assuming that the highest common\r
3182   // denominator was chosed by NWAY.\r
3183   //\r
3184   if ((MdiLinkPartnerAdReg & NWAY_LP_ABILITY) &&\r
3185       (MdiStatusReg & MDI_SR_AUTO_NEG_COMPLETE)) {\r
3186 \r
3187     //\r
3188     // Read our advertisement register\r
3189     //\r
3190     MdiRead (\r
3191       AdapterInfo,\r
3192       AUTO_NEG_ADVERTISE_REG,\r
3193       AdapterInfo->PhyAddress,\r
3194       &MdiOwnAdReg\r
3195       );\r
3196 \r
3197     //\r
3198     // AND the two advertisement registers together, and get rid of any\r
3199     // extraneous bits.\r
3200     //\r
3201     MdiOwnAdReg = (UINT16) (MdiOwnAdReg & (MdiLinkPartnerAdReg & NWAY_LP_ABILITY));\r
3202 \r
3203     //\r
3204     // Get speed setting\r
3205     //\r
3206     if (MdiOwnAdReg & (NWAY_AD_TX_HALF_DPX | NWAY_AD_TX_FULL_DPX | NWAY_AD_T4_CAPABLE)) {\r
3207       AdapterInfo->LinkSpeed = 100;\r
3208     } else {\r
3209       AdapterInfo->LinkSpeed = 10;\r
3210     }\r
3211 \r
3212     //\r
3213     // Get duplex setting -- use priority resolution algorithm\r
3214     //\r
3215     if (MdiOwnAdReg & (NWAY_AD_T4_CAPABLE)) {\r
3216       AdapterInfo->Duplex = HALF_DUPLEX;\r
3217       return ;\r
3218     } else if (MdiOwnAdReg & (NWAY_AD_TX_FULL_DPX)) {\r
3219       AdapterInfo->Duplex = FULL_DUPLEX;\r
3220       return ;\r
3221     } else if (MdiOwnAdReg & (NWAY_AD_TX_HALF_DPX)) {\r
3222       AdapterInfo->Duplex = HALF_DUPLEX;\r
3223       return ;\r
3224     } else if (MdiOwnAdReg & (NWAY_AD_10T_FULL_DPX)) {\r
3225       AdapterInfo->Duplex = FULL_DUPLEX;\r
3226       return ;\r
3227     } else {\r
3228       AdapterInfo->Duplex = HALF_DUPLEX;\r
3229       return ;\r
3230     }\r
3231   }\r
3232 \r
3233   //\r
3234   // If we are connected to a dumb (non-NWAY) repeater or hub, and the line\r
3235   // speed was determined automatically by parallel detection, then we have\r
3236   // no way of knowing exactly what speed the PHY is set to unless that PHY\r
3237   // has a propietary register which indicates speed in this situation.  The\r
3238   // NSC TX PHY does have such a register.  Also, since NWAY didn't establish\r
3239   // the connection, the duplex setting should HALF duplex.\r
3240   //\r
3241   AdapterInfo->Duplex = HALF_DUPLEX;\r
3242 \r
3243   if (PhyId == PHY_NSC_TX) {\r
3244     //\r
3245     // Read register 25 to get the SPEED_10 bit\r
3246     //\r
3247     MdiRead (\r