1/** @file
2    Implementation of reading the MAC address of a network adapter.
3
4Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.<BR>
5This program and the accompanying materials are licensed
6and made available under the terms and conditions of the BSD License which
7accompanies this distribution. The full text of the license may be found at
8http://opensource.org/licenses/bsd-license.php
9
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13**/
14
15#include "Snp.h"
16
17
18/**
19  Call UNDI to read the MAC address of the NIC and update the mode structure
20  with the address.
21
22  @param  Snp         Pointer to snp driver structure.
23
24  @retval EFI_SUCCESS       The MAC address of the NIC is read successfully.
25  @retval EFI_DEVICE_ERROR  Failed to read the MAC address of the NIC.
26
27**/
28EFI_STATUS
29PxeGetStnAddr (
30  SNP_DRIVER *Snp
31  )
32{
33  PXE_DB_STATION_ADDRESS  *Db;
34
35  Db                  = Snp->Db;
36  Snp->Cdb.OpCode     = PXE_OPCODE_STATION_ADDRESS;
37  Snp->Cdb.OpFlags    = PXE_OPFLAGS_STATION_ADDRESS_READ;
38
39  Snp->Cdb.CPBaddr    = PXE_CPBADDR_NOT_USED;
40  Snp->Cdb.CPBsize    = PXE_CPBSIZE_NOT_USED;
41
42  Snp->Cdb.DBsize     = (UINT16) sizeof (PXE_DB_STATION_ADDRESS);
43  Snp->Cdb.DBaddr     = (UINT64)(UINTN) Db;
44
45  Snp->Cdb.StatCode   = PXE_STATCODE_INITIALIZE;
46  Snp->Cdb.StatFlags  = PXE_STATFLAGS_INITIALIZE;
47  Snp->Cdb.IFnum      = Snp->IfNum;
48  Snp->Cdb.Control    = PXE_CONTROL_LAST_CDB_IN_LIST;
49
50  //
51  // Issue UNDI command and check result.
52  //
53  DEBUG ((EFI_D_NET, "\nsnp->undi.station_addr()  "));
54
55  (*Snp->IssueUndi32Command) ((UINT64)(UINTN) &Snp->Cdb);
56
57  if (Snp->Cdb.StatCode != PXE_STATCODE_SUCCESS) {
58    DEBUG (
59      (EFI_D_ERROR,
60      "\nsnp->undi.station_addr()  %xh:%xh\n",
61      Snp->Cdb.StatFlags,
62      Snp->Cdb.StatCode)
63      );
64
65    return EFI_DEVICE_ERROR;
66  }
67  //
68  // Set new station address in SNP->Mode structure and return success.
69  //
70  CopyMem (
71    &(Snp->Mode.CurrentAddress),
72    &Db->StationAddr,
73    Snp->Mode.HwAddressSize
74    );
75
76  CopyMem (
77    &Snp->Mode.BroadcastAddress,
78    &Db->BroadcastAddr,
79    Snp->Mode.HwAddressSize
80    );
81
82  CopyMem (
83    &Snp->Mode.PermanentAddress,
84    &Db->PermanentAddr,
85    Snp->Mode.HwAddressSize
86    );
87
88  return EFI_SUCCESS;
89}
90
91
92/**
93  Call UNDI to set a new MAC address for the NIC.
94
95  @param  Snp         Pointer to Snp driver structure.
96  @param  NewMacAddr  Pointer to a MAC address to be set for the NIC, if this is
97                      NULL then this routine resets the mac address to the NIC's
98                      original address.
99
100
101**/
102EFI_STATUS
103PxeSetStnAddr (
104  SNP_DRIVER      *Snp,
105  EFI_MAC_ADDRESS *NewMacAddr
106  )
107{
108  PXE_CPB_STATION_ADDRESS *Cpb;
109  PXE_DB_STATION_ADDRESS  *Db;
110
111  Cpb             = Snp->Cpb;
112  Db              = Snp->Db;
113  Snp->Cdb.OpCode = PXE_OPCODE_STATION_ADDRESS;
114
115  if (NewMacAddr == NULL) {
116    Snp->Cdb.OpFlags  = PXE_OPFLAGS_STATION_ADDRESS_RESET;
117    Snp->Cdb.CPBsize  = PXE_CPBSIZE_NOT_USED;
118    Snp->Cdb.CPBaddr  = PXE_CPBADDR_NOT_USED;
119  } else {
120    Snp->Cdb.OpFlags = PXE_OPFLAGS_STATION_ADDRESS_WRITE;
121    //
122    // Supplying a new address in the CPB will make undi change the mac address to the new one.
123    //
124    CopyMem (&Cpb->StationAddr, NewMacAddr, Snp->Mode.HwAddressSize);
125
126    Snp->Cdb.CPBsize  = (UINT16) sizeof (PXE_CPB_STATION_ADDRESS);
127    Snp->Cdb.CPBaddr  = (UINT64)(UINTN) Cpb;
128  }
129
130  Snp->Cdb.DBsize     = (UINT16) sizeof (PXE_DB_STATION_ADDRESS);
131  Snp->Cdb.DBaddr     = (UINT64)(UINTN) Db;
132
133  Snp->Cdb.StatCode   = PXE_STATCODE_INITIALIZE;
134  Snp->Cdb.StatFlags  = PXE_STATFLAGS_INITIALIZE;
135  Snp->Cdb.IFnum      = Snp->IfNum;
136  Snp->Cdb.Control    = PXE_CONTROL_LAST_CDB_IN_LIST;
137
138  //
139  // Issue UNDI command and check result.
140  //
141  DEBUG ((EFI_D_NET, "\nsnp->undi.station_addr()  "));
142
143  (*Snp->IssueUndi32Command) ((UINT64)(UINTN) &Snp->Cdb);
144
145  if (Snp->Cdb.StatCode != PXE_STATCODE_SUCCESS) {
146    DEBUG (
147      (EFI_D_ERROR,
148      "\nsnp->undi.station_addr()  %xh:%xh\n",
149      Snp->Cdb.StatFlags,
150      Snp->Cdb.StatCode)
151      );
152
153    //
154    // UNDI command failed.  Return UNDI status to caller.
155    //
156    return EFI_DEVICE_ERROR;
157  }
158  //
159  // read the changed address and save it in SNP->Mode structure
160  //
161  PxeGetStnAddr (Snp);
162
163  return EFI_SUCCESS;
164}
165
166
167/**
168  Modifies or resets the current station address, if supported.
169
170  This function modifies or resets the current station address of a network
171  interface, if supported. If Reset is TRUE, then the current station address is
172  set to the network interface's permanent address. If Reset is FALSE, and the
173  network interface allows its station address to be modified, then the current
174  station address is changed to the address specified by New. If the network
175  interface does not allow its station address to be modified, then
176  EFI_INVALID_PARAMETER will be returned. If the station address is successfully
177  updated on the network interface, EFI_SUCCESS will be returned. If the driver
178  has not been initialized, EFI_DEVICE_ERROR will be returned.
179
180  @param This  A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
181  @param Reset Flag used to reset the station address to the network interface's
182               permanent address.
183  @param New   New station address to be used for the network interface.
184
185
186  @retval EFI_SUCCESS           The network interface's station address was updated.
187  @retval EFI_NOT_STARTED       The Simple Network Protocol interface has not been
188                                started by calling Start().
189  @retval EFI_INVALID_PARAMETER The New station address was not accepted by the NIC.
190  @retval EFI_INVALID_PARAMETER Reset is FALSE and New is NULL.
191  @retval EFI_DEVICE_ERROR      The Simple Network Protocol interface has not
192                                been initialized by calling Initialize().
193  @retval EFI_DEVICE_ERROR      An error occurred attempting to set the new
194                                station address.
195  @retval EFI_UNSUPPORTED       The NIC does not support changing the network
196                                interface's station address.
197
198**/
199EFI_STATUS
200EFIAPI
201SnpUndi32StationAddress (
202  IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
203  IN BOOLEAN                     Reset,
204  IN EFI_MAC_ADDRESS             *New OPTIONAL
205  )
206{
207  SNP_DRIVER  *Snp;
208  EFI_STATUS  Status;
209  EFI_TPL     OldTpl;
210
211  //
212  // Check for invalid parameter combinations.
213  //
214  if ((This == NULL) ||
215    (!Reset && (New == NULL))) {
216    return EFI_INVALID_PARAMETER;
217  }
218
219  Snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);
220
221  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
222
223  //
224  // Return error if the SNP is not initialized.
225  //
226  switch (Snp->Mode.State) {
227  case EfiSimpleNetworkInitialized:
228    break;
229
230  case EfiSimpleNetworkStopped:
231    Status = EFI_NOT_STARTED;
232    goto ON_EXIT;
233
234  default:
235    Status = EFI_DEVICE_ERROR;
236    goto ON_EXIT;
237  }
238
239  if (Reset) {
240    Status = PxeSetStnAddr (Snp, NULL);
241  } else {
242    Status = PxeSetStnAddr (Snp, New);
243  }
244
245ON_EXIT:
246  gBS->RestoreTPL (OldTpl);
247
248  return Status;
249}
250