sync comments, fix function header, rename variable name to follow coding style.
[mirror_edk2.git] / MdeModulePkg / Universal / Network / SnpDxe / Callback.c
1 /** @file\r
2   This file contains two sets of callback routines for undi3.0 and undi3.1.\r
3   the callback routines for Undi3.1 have an extra parameter UniqueId which\r
4   stores the interface context for the NIC that snp is trying to talk.\r
5 \r
6 Copyright (c) 2006 - 2008, Intel Corporation. <BR>\r
7 All rights reserved. This program and the accompanying materials\r
8 are licensed and made available under the terms and conditions of the BSD License\r
9 which accompanies this distribution.  The full text of the license may be found at\r
10 http://opensource.org/licenses/bsd-license.php\r
11 \r
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
14 \r
15 **/\r
16 \r
17 #include "Snp.h"\r
18 \r
19 //\r
20 // Global variables\r
21 // these 2 global variables are used only for 3.0 undi. we could not place\r
22 // them in the snp structure because we will not know which snp structure\r
23 // in the callback context!\r
24 //\r
25 BOOLEAN              mInitializeLock = TRUE;\r
26 EFI_LOCK             mLock;\r
27 \r
28 //\r
29 // End Global variables\r
30 //\r
31 extern EFI_PCI_IO_PROTOCOL  *mPciIo;\r
32 \r
33 /**\r
34   This is a callback routine supplied to UNDI at undi_start time.\r
35   UNDI call this routine with a virtual or CPU address that SNP provided to \r
36   convert it to a physical or device address. Since EFI uses the identical \r
37   mapping, this routine returns the physical address same as the virtual address\r
38   for most of the addresses. an address above 4GB cannot generally be used as a \r
39   device address, it needs to be mapped to a lower physical address. This \r
40   routine does not call the map routine itself, but it assumes that the mapping\r
41   was done at the time of providing the address to UNDI. This routine just \r
42   looks up the address in a map table (which is the v2p structure chain). \r
43   \r
44   @param  CpuAddr        virtual address of a buffer.\r
45   @param  DeviceAddrPtr  pointer to the physical address.\r
46                          The DeviceAddrPtr will contain 0 in case of any error.\r
47 \r
48 **/\r
49 VOID\r
50 SnpUndi32CallbackV2p30 (\r
51   IN UINT64     CpuAddr,\r
52   IN OUT UINT64 DeviceAddrPtr\r
53   )\r
54 {\r
55   V2P  *V2p;\r
56   //\r
57   // Do nothing if virtual address is zero or physical pointer is NULL.\r
58   // No need to map if the virtual address is within 4GB limit since\r
59   // EFI uses identical mapping\r
60   //\r
61   if ((CpuAddr == 0) || (DeviceAddrPtr == 0)) {\r
62     DEBUG ((EFI_D_NET, "\nv2p: Null virtual address or physical pointer.\n"));\r
63     return ;\r
64   }\r
65 \r
66   if (CpuAddr < FOUR_GIGABYTES) {\r
67     *(UINT64 *) (UINTN) DeviceAddrPtr = CpuAddr;\r
68     return ;\r
69   }\r
70   //\r
71   // SNP creates a vaddr tp paddr mapping at the time of calling undi with any\r
72   // big address, this callback routine just looks up in the v2p list and\r
73   // returns the physical address for any given virtual address.\r
74   //\r
75   if (FindV2p (&V2p, (VOID *) (UINTN) CpuAddr) != EFI_SUCCESS) {\r
76     *(UINT64 *) (UINTN) DeviceAddrPtr = CpuAddr;\r
77   } else {\r
78     *(UINT64 *) (UINTN) DeviceAddrPtr = V2p->PhysicalAddress;\r
79   }\r
80 }\r
81 \r
82 /**\r
83   This is a callback routine supplied to UNDI at undi_start time.\r
84   UNDI call this routine when it wants to have exclusive access to a critical\r
85   section of the code/data.\r
86 \r
87   @param Enable   non-zero indicates acquire\r
88                   zero indicates release\r
89 \r
90 **/\r
91 VOID\r
92 SnpUndi32CallbackBlock30 (\r
93   IN UINT32 Enable\r
94   )\r
95 {\r
96   //\r
97   // tcpip was calling snp at tpl_notify and if we acquire a lock that was\r
98   // created at a lower level (TPL_CALLBACK) it gives an assert!\r
99   //\r
100   if (mInitializeLock) {\r
101     EfiInitializeLock (&mLock, TPL_NOTIFY);\r
102     mInitializeLock = FALSE;\r
103   }\r
104 \r
105   if (Enable != 0) {\r
106     EfiAcquireLock (&mLock);\r
107   } else {\r
108     EfiReleaseLock (&mLock);\r
109   }\r
110 }\r
111 \r
112 /**\r
113   This is a callback routine supplied to UNDI at undi_start time.\r
114   UNDI call this routine with the number of micro seconds when it wants to\r
115   pause.\r
116 \r
117   @param MicroSeconds  number of micro seconds to pause, ususlly multiple of 10.\r
118 \r
119 **/\r
120 VOID\r
121 SnpUndi32CallbackDelay30 (\r
122   IN UINT64 MicroSeconds\r
123   )\r
124 {\r
125   if (MicroSeconds != 0) {\r
126     gBS->Stall ((UINTN) MicroSeconds);\r
127   }\r
128 }\r
129 \r
130 /**\r
131   This is a callback routine supplied to UNDI at undi_start time.\r
132   This is the IO routine for UNDI. This is not currently being used by UNDI3.0\r
133   because Undi3.0 uses io/mem offsets relative to the beginning of the device\r
134   io/mem address and so it needs to use the PCI_IO_FUNCTION that abstracts the\r
135   start of the device's io/mem addresses. Since SNP cannot retrive the context\r
136   of the undi3.0 interface it cannot use the PCI_IO_FUNCTION that specific for\r
137   that NIC and uses one global IO functions structure, this does not work.\r
138   This however works fine for EFI1.0 Undis because they use absolute addresses\r
139   for io/mem access.\r
140 \r
141   @param ReadOrWrite  indicates read or write, IO or Memory\r
142   @param NumBytes     number of bytes to read or write\r
143   @param Address      IO or memory address to read from or write to\r
144   @param BufferAddr   memory location to read into or that contains the bytes to \r
145                       write\r
146 \r
147 **/\r
148 VOID\r
149 SnpUndi32CallbackMemio30 (\r
150   IN UINT8      ReadOrWrite,\r
151   IN UINT8      NumBytes,\r
152   IN UINT64     Address,\r
153   IN OUT UINT64 BufferAddr\r
154   )\r
155 {\r
156   EFI_PCI_IO_PROTOCOL_WIDTH Width;\r
157 \r
158   switch (NumBytes) {\r
159   case 2:\r
160     Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 1;\r
161     break;\r
162 \r
163   case 4:\r
164     Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 2;\r
165     break;\r
166 \r
167   case 8:\r
168     Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 3;\r
169     break;\r
170 \r
171   default:\r
172     Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 0;\r
173   }\r
174 \r
175   switch (ReadOrWrite) {\r
176   case PXE_IO_READ:\r
177     mPciIo->Io.Read (\r
178                  mPciIo,\r
179                  Width,\r
180                  1,    // BAR 1, IO base address\r
181                  Address,\r
182                  1,    // count\r
183                  (VOID *) (UINTN) BufferAddr\r
184                  );\r
185     break;\r
186 \r
187   case PXE_IO_WRITE:\r
188     mPciIo->Io.Write (\r
189                  mPciIo,\r
190                  Width,\r
191                  1,    // BAR 1, IO base address\r
192                  Address,\r
193                  1,    // count\r
194                  (VOID *) (UINTN) BufferAddr\r
195                  );\r
196     break;\r
197 \r
198   case PXE_MEM_READ:\r
199     mPciIo->Mem.Read (\r
200                   mPciIo,\r
201                   Width,\r
202                   0,  // BAR 0, Memory base address\r
203                   Address,\r
204                   1,  // count\r
205                   (VOID *) (UINTN) BufferAddr\r
206                   );\r
207     break;\r
208 \r
209   case PXE_MEM_WRITE:\r
210     mPciIo->Mem.Write (\r
211                   mPciIo,\r
212                   Width,\r
213                   0,  // BAR 0, Memory base address\r
214                   Address,\r
215                   1,  // count\r
216                   (VOID *) (UINTN) BufferAddr\r
217                   );\r
218     break;\r
219   }\r
220 \r
221   return ;\r
222 }\r
223 \r
224 /**\r
225   This is a callback routine supplied to UNDI3.1 at undi_start time.\r
226   UNDI call this routine when it wants to have exclusive access to a critical\r
227   section of the code/data.\r
228   New callbacks for 3.1:\r
229   there won't be a virtual2physical callback for UNDI 3.1 because undi3.1 uses\r
230   the MemMap call to map the required address by itself!\r
231 \r
232   @param UniqueId  This was supplied to UNDI at Undi_Start, SNP uses this to \r
233                                                                  store Undi interface context (Undi does not read or write\r
234                                                                  this variable)\r
235   @param Enable    non-zero indicates acquire\r
236                    zero indicates release\r
237 **/\r
238 VOID\r
239 SnpUndi32CallbackBlock (\r
240   IN UINT64 UniqueId,\r
241   IN UINT32 Enable\r
242   )\r
243 {\r
244   SNP_DRIVER  *Snp;\r
245 \r
246   Snp = (SNP_DRIVER *) (UINTN) UniqueId;\r
247   //\r
248   // tcpip was calling snp at tpl_notify and when we acquire a lock that was\r
249   // created at a lower level (TPL_CALLBACK) it gives an assert!\r
250   //\r
251   if (Enable != 0) {\r
252     EfiAcquireLock (&Snp->Lock);\r
253   } else {\r
254     EfiReleaseLock (&Snp->Lock);\r
255   }\r
256 }\r
257 \r
258 /**\r
259   This is a callback routine supplied to UNDI at undi_start time.\r
260   UNDI call this routine with the number of micro seconds when it wants to\r
261   pause.\r
262 \r
263   @param UniqueId      This was supplied to UNDI at Undi_Start, SNP uses this to\r
264                                                                      store Undi interface context (Undi does not read or write\r
265                                                                      this variable)\r
266   @param MicroSeconds  number of micro seconds to pause, ususlly multiple of 10.\r
267 **/\r
268 VOID\r
269 SnpUndi32CallbackDelay (\r
270   IN UINT64 UniqueId,\r
271   IN UINT64 MicroSeconds\r
272   )\r
273 {\r
274   if (MicroSeconds != 0) {\r
275     gBS->Stall ((UINTN) MicroSeconds);\r
276   }\r
277 }\r
278 \r
279 /**\r
280   This is a callback routine supplied to UNDI at undi_start time.\r
281   This is the IO routine for UNDI3.1 to start CPB.\r
282 \r
283   @param UniqueId       This was supplied to UNDI at Undi_Start, SNP uses this \r
284                                                                                         to store Undi interface context (Undi does not read or\r
285                                                                                         write this variable)\r
286   @param ReadOrWrite    indicates read or write, IO or Memory.\r
287   @param NumBytes       number of bytes to read or write.\r
288   @param MemOrPortAddr  IO or memory address to read from or write to.\r
289   @param BufferPtr      memory location to read into or that contains the bytes\r
290                         to write.\r
291 **/\r
292 VOID\r
293 SnpUndi32CallbackMemio (\r
294   IN UINT64     UniqueId,\r
295   IN UINT8      ReadOrWrite,\r
296   IN UINT8      NumBytes,\r
297   IN UINT64     MemOrPortAddr,\r
298   IN OUT UINT64 BufferPtr\r
299   )\r
300 {\r
301   SNP_DRIVER                *Snp;\r
302   EFI_PCI_IO_PROTOCOL_WIDTH Width;\r
303 \r
304   Snp   = (SNP_DRIVER *) (UINTN) UniqueId;\r
305 \r
306   Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 0;\r
307   switch (NumBytes) {\r
308   case 2:\r
309     Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 1;\r
310     break;\r
311 \r
312   case 4:\r
313     Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 2;\r
314     break;\r
315 \r
316   case 8:\r
317     Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 3;\r
318     break;\r
319   }\r
320 \r
321   switch (ReadOrWrite) {\r
322   case PXE_IO_READ:\r
323     Snp->PciIo->Io.Read (\r
324                      Snp->PciIo,\r
325                      Width,\r
326                      Snp->IoBarIndex,      // BAR 1 (for 32bit regs), IO base address\r
327                      MemOrPortAddr,\r
328                      1,                    // count\r
329                      (VOID *) (UINTN) BufferPtr\r
330                      );\r
331     break;\r
332 \r
333   case PXE_IO_WRITE:\r
334     Snp->PciIo->Io.Write (\r
335                      Snp->PciIo,\r
336                      Width,\r
337                      Snp->IoBarIndex,      // BAR 1 (for 32bit regs), IO base address\r
338                      MemOrPortAddr,\r
339                      1,                    // count\r
340                      (VOID *) (UINTN) BufferPtr\r
341                      );\r
342     break;\r
343 \r
344   case PXE_MEM_READ:\r
345     Snp->PciIo->Mem.Read (\r
346                       Snp->PciIo,\r
347                       Width,\r
348                       Snp->MemoryBarIndex,  // BAR 0, Memory base address\r
349                       MemOrPortAddr,\r
350                       1,                    // count\r
351                       (VOID *) (UINTN) BufferPtr\r
352                       );\r
353     break;\r
354 \r
355   case PXE_MEM_WRITE:\r
356     Snp->PciIo->Mem.Write (\r
357                       Snp->PciIo,\r
358                       Width,\r
359                       Snp->MemoryBarIndex,  // BAR 0, Memory base address\r
360                       MemOrPortAddr,\r
361                       1,                    // count\r
362                       (VOID *) (UINTN) BufferPtr\r
363                       );\r
364     break;\r
365   }\r
366 \r
367   return ;\r
368 }\r
369 \r
370 /**\r
371   This is a callback routine supplied to UNDI at undi_start time.\r
372   UNDI call this routine when it has to map a CPU address to a device\r
373   address.\r
374 \r
375   @param UniqueId      - This was supplied to UNDI at Undi_Start, SNP uses this to store\r
376                          Undi interface context (Undi does not read or write this variable)\r
377   @param CpuAddr       - Virtual address to be mapped!\r
378   @param NumBytes      - size of memory to be mapped\r
379   @param Direction     - direction of data flow for this memory's usage:\r
380                          cpu->device, device->cpu or both ways\r
381   @param DeviceAddrPtr - pointer to return the mapped device address\r
382 \r
383 **/\r
384 VOID\r
385 SnpUndi32CallbackMap (\r
386   IN UINT64     UniqueId,\r
387   IN UINT64     CpuAddr,\r
388   IN UINT32     NumBytes,\r
389   IN UINT32     Direction,\r
390   IN OUT UINT64 DeviceAddrPtr\r
391   )\r
392 {\r
393   EFI_PHYSICAL_ADDRESS          *DevAddrPtr;\r
394   EFI_PCI_IO_PROTOCOL_OPERATION DirectionFlag;\r
395   UINTN                         BuffSize;\r
396   SNP_DRIVER                    *Snp;\r
397   UINTN                         Index;\r
398   EFI_STATUS                    Status;\r
399 \r
400   BuffSize    = (UINTN) NumBytes;\r
401   Snp         = (SNP_DRIVER *) (UINTN) UniqueId;\r
402   DevAddrPtr  = (EFI_PHYSICAL_ADDRESS *) (UINTN) DeviceAddrPtr;\r
403 \r
404   if (CpuAddr == 0) {\r
405     *DevAddrPtr = 0;\r
406     return ;\r
407   }\r
408 \r
409   switch (Direction) {\r
410   case TO_AND_FROM_DEVICE:\r
411     DirectionFlag = EfiPciIoOperationBusMasterCommonBuffer;\r
412     break;\r
413 \r
414   case FROM_DEVICE:\r
415     DirectionFlag = EfiPciIoOperationBusMasterWrite;\r
416     break;\r
417 \r
418   case TO_DEVICE:\r
419     DirectionFlag = EfiPciIoOperationBusMasterRead;\r
420     break;\r
421 \r
422   default:\r
423     *DevAddrPtr = 0;\r
424     //\r
425     // any non zero indicates error!\r
426     //\r
427     return ;\r
428   }\r
429   //\r
430   // find an unused map_list entry\r
431   //\r
432   for (Index = 0; Index < MAX_MAP_LENGTH; Index++) {\r
433     if (Snp->MapList[Index].VirtualAddress == 0) {\r
434       break;\r
435     }\r
436   }\r
437 \r
438   if (Index >= MAX_MAP_LENGTH) {\r
439     DEBUG ((EFI_D_INFO, "SNP maplist is FULL\n"));\r
440     *DevAddrPtr = 0;\r
441     return ;\r
442   }\r
443 \r
444   Snp->MapList[Index].VirtualAddress = (EFI_PHYSICAL_ADDRESS) CpuAddr;\r
445 \r
446   Status = Snp->PciIo->Map (\r
447                          Snp->PciIo,\r
448                          DirectionFlag,\r
449                          (VOID *) (UINTN) CpuAddr,\r
450                          &BuffSize,\r
451                          DevAddrPtr,\r
452                          &(Snp->MapList[Index].MapCookie)\r
453                          );\r
454   if (Status != EFI_SUCCESS) {\r
455     *DevAddrPtr                        = 0;\r
456     Snp->MapList[Index].VirtualAddress = 0;\r
457   }\r
458 \r
459   return ;\r
460 }\r
461 \r
462 /**\r
463   This is a callback routine supplied to UNDI at undi_start time.\r
464   UNDI call this routine when it wants to unmap an address that was previously\r
465   mapped using map callback.\r
466 \r
467   @param UniqueId    This was supplied to UNDI at Undi_Start, SNP uses this to store.\r
468                      Undi interface context (Undi does not read or write this variable)\r
469   @param CpuAddr     Virtual address that was mapped!\r
470   @param NumBytes    size of memory mapped\r
471   @param Direction   direction of data flow for this memory's usage:\r
472                      cpu->device, device->cpu or both ways\r
473   @param DeviceAddr  the mapped device address\r
474 \r
475 **/\r
476 VOID\r
477 SnpUndi32CallbackUnmap (\r
478   IN UINT64 UniqueId,\r
479   IN UINT64 CpuAddr,\r
480   IN UINT32 NumBytes,\r
481   IN UINT32 Direction,\r
482   IN UINT64 DeviceAddr\r
483   )\r
484 {\r
485   SNP_DRIVER  *Snp;\r
486   UINT16      Index;\r
487 \r
488   Snp = (SNP_DRIVER *) (UINTN) UniqueId;\r
489 \r
490   for (Index = 0; Index < MAX_MAP_LENGTH; Index++) {\r
491     if (Snp->MapList[Index].VirtualAddress == CpuAddr) {\r
492       break;\r
493     }\r
494   }\r
495 \r
496   if (Index >= MAX_MAP_LENGTH)\r
497   {\r
498     DEBUG ((EFI_D_ERROR, "SNP could not find a mapping, failed to unmap.\n"));\r
499     return ;\r
500   }\r
501 \r
502   Snp->PciIo->Unmap (Snp->PciIo, Snp->MapList[Index].MapCookie);\r
503   Snp->MapList[Index].VirtualAddress = 0;\r
504   Snp->MapList[Index].MapCookie      = NULL;\r
505   return ;\r
506 }\r
507 \r
508 /**\r
509   This is a callback routine supplied to UNDI at undi_start time.\r
510   UNDI call this routine when it wants synchronize the virtual buffer contents\r
511   with the mapped buffer contents. The virtual and mapped buffers need not\r
512   correspond to the same physical memory (especially if the virtual address is\r
513   > 4GB). Depending on the direction for which the buffer is mapped, undi will\r
514   need to synchronize their contents whenever it writes to/reads from the buffer\r
515   using either the cpu address or the device address.\r
516 \r
517   EFI does not provide a sync call, since virt=physical, we sould just do\r
518   the synchronization ourself here!\r
519 \r
520   @param UniqueId    This was supplied to UNDI at Undi_Start, SNP uses this to store\r
521                      Undi interface context (Undi does not read or write this variable)\r
522   @param CpuAddr     Virtual address that was mapped!\r
523   @param NumBytes    size of memory mapped.\r
524   @param Direction   direction of data flow for this memory's usage:\r
525                      cpu->device, device->cpu or both ways.\r
526   @param DeviceAddr  the mapped device address.\r
527 \r
528 **/\r
529 VOID\r
530 SnpUndi32CallbackSync (\r
531   IN UINT64             UniqueId,\r
532   IN UINT64             CpuAddr,\r
533   IN UINT32             NumBytes,\r
534   IN UINT32             Direction,\r
535   IN UINT64             DeviceAddr\r
536   )\r
537 {\r
538   if ((CpuAddr == 0) || (DeviceAddr == 0) || (NumBytes == 0)) {\r
539     return ;\r
540 \r
541   }\r
542 \r
543   switch (Direction) {\r
544   case FROM_DEVICE:\r
545     CopyMem ((UINT8 *) (UINTN) CpuAddr, (UINT8 *) (UINTN) DeviceAddr, NumBytes);\r
546     break;\r
547 \r
548   case TO_DEVICE:\r
549     CopyMem ((UINT8 *) (UINTN) DeviceAddr, (UINT8 *) (UINTN) CpuAddr, NumBytes);\r
550     break;\r
551   }\r
552 \r
553   return ;\r
554 }\r