Transmit.c revision cd7bfc2c632841fab110f601a32850eeddc16af1
1/** @file
2    Implementation of transmitting a packet.
3
4Copyright (c) 2004 - 2007, 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 create the meadia header for the given data buffer.
20
21  @param  Snp              Pointer to SNP driver structure.
22  @param  MacHeaderPtr     Address where the media header will be filled in.
23  @param  HeaderSize       Size of the memory at MacHeaderPtr.
24  @param  Buffer           Data buffer pointer.
25  @param  BufferSize       Size of data in the Buffer
26  @param  DestAddr         Address of the destination mac address buffer.
27  @param  SrcAddr          Address of the source mac address buffer.
28  @param  ProtocolPtr      Address of the protocol type.
29
30  @retval EFI_SUCCESS      Successfully completed the undi call.
31  @retval Other            Error return from undi call.
32
33**/
34EFI_STATUS
35PxeFillHeader (
36  SNP_DRIVER      *Snp,
37  VOID            *MacHeaderPtr,
38  UINTN           HeaderSize,
39  VOID            *Buffer,
40  UINTN           BufferSize,
41  EFI_MAC_ADDRESS *DestAddr,
42  EFI_MAC_ADDRESS *SrcAddr,
43  UINT16          *ProtocolPtr
44  )
45{
46  PXE_CPB_FILL_HEADER_FRAGMENTED  *Cpb;
47
48  Cpb = Snp->Cpb;
49  if (SrcAddr != NULL) {
50    CopyMem (
51      (VOID *) Cpb->SrcAddr,
52      (VOID *) SrcAddr,
53      Snp->Mode.HwAddressSize
54      );
55  } else {
56    CopyMem (
57      (VOID *) Cpb->SrcAddr,
58      (VOID *) &(Snp->Mode.CurrentAddress),
59      Snp->Mode.HwAddressSize
60      );
61  }
62
63  CopyMem (
64    (VOID *) Cpb->DestAddr,
65    (VOID *) DestAddr,
66    Snp->Mode.HwAddressSize
67    );
68
69  //
70  // we need to do the byte swapping
71  //
72  Cpb->Protocol             = (UINT16) PXE_SWAP_UINT16 (*ProtocolPtr);
73
74  Cpb->PacketLen            = (UINT32) (BufferSize);
75  Cpb->MediaHeaderLen       = (UINT16) HeaderSize;
76
77  Cpb->FragCnt              = 2;
78  Cpb->reserved             = 0;
79
80  Cpb->FragDesc[0].FragAddr = (UINT64)(UINTN) MacHeaderPtr;
81  Cpb->FragDesc[0].FragLen  = (UINT32) HeaderSize;
82  Cpb->FragDesc[1].FragAddr = (UINT64)(UINTN) Buffer;
83  Cpb->FragDesc[1].FragLen  = (UINT32) BufferSize;
84
85  Cpb->FragDesc[0].reserved = Cpb->FragDesc[1].reserved = 0;
86
87  Snp->Cdb.OpCode     = PXE_OPCODE_FILL_HEADER;
88  Snp->Cdb.OpFlags    = PXE_OPFLAGS_FILL_HEADER_FRAGMENTED;
89
90  Snp->Cdb.DBsize     = PXE_DBSIZE_NOT_USED;
91  Snp->Cdb.DBaddr     = PXE_DBADDR_NOT_USED;
92
93  Snp->Cdb.CPBsize    = (UINT16) sizeof (PXE_CPB_FILL_HEADER_FRAGMENTED);
94  Snp->Cdb.CPBaddr    = (UINT64)(UINTN) Cpb;
95
96  Snp->Cdb.StatCode   = PXE_STATCODE_INITIALIZE;
97  Snp->Cdb.StatFlags  = PXE_STATFLAGS_INITIALIZE;
98  Snp->Cdb.IFnum      = Snp->IfNum;
99  Snp->Cdb.Control    = PXE_CONTROL_LAST_CDB_IN_LIST;
100
101  //
102  // Issue UNDI command and check result.
103  //
104  DEBUG ((EFI_D_NET, "\nSnp->undi.fill_header()  "));
105
106  (*Snp->IssueUndi32Command) ((UINT64) (UINTN) &Snp->Cdb);
107
108  switch (Snp->Cdb.StatCode) {
109  case PXE_STATCODE_SUCCESS:
110    return EFI_SUCCESS;
111
112  case PXE_STATCODE_INVALID_PARAMETER:
113    DEBUG (
114      (EFI_D_ERROR,
115      "\nSnp->undi.fill_header()  %xh:%xh\n",
116      Snp->Cdb.StatFlags,
117      Snp->Cdb.StatCode)
118      );
119
120    return EFI_INVALID_PARAMETER;
121
122  default:
123    DEBUG (
124      (EFI_D_ERROR,
125      "\nSnp->undi.fill_header()  %xh:%xh\n",
126      Snp->Cdb.StatFlags,
127      Snp->Cdb.StatCode)
128      );
129
130    return EFI_DEVICE_ERROR;
131  }
132}
133
134
135/**
136  This routine calls undi to transmit the given data buffer
137
138  @param  Snp                 pointer to SNP driver structure
139  @param  Buffer           data buffer pointer
140  @param  BufferSize        Size of data in the Buffer
141
142  @retval EFI_SUCCESS         if successfully completed the undi call
143  @retval Other               error return from undi call.
144
145**/
146EFI_STATUS
147PxeTransmit (
148  SNP_DRIVER *Snp,
149  VOID       *Buffer,
150  UINTN      BufferSize
151  )
152{
153  PXE_CPB_TRANSMIT  *Cpb;
154  EFI_STATUS        Status;
155
156  Cpb             = Snp->Cpb;
157  Cpb->FrameAddr  = (UINT64) (UINTN) Buffer;
158  Cpb->DataLen    = (UINT32) BufferSize;
159
160  Cpb->MediaheaderLen = 0;
161  Cpb->reserved       = 0;
162
163  Snp->Cdb.OpFlags    = PXE_OPFLAGS_TRANSMIT_WHOLE;
164
165  Snp->Cdb.CPBsize    = (UINT16) sizeof (PXE_CPB_TRANSMIT);
166  Snp->Cdb.CPBaddr    = (UINT64)(UINTN) Cpb;
167
168  Snp->Cdb.OpCode     = PXE_OPCODE_TRANSMIT;
169  Snp->Cdb.DBsize     = PXE_DBSIZE_NOT_USED;
170  Snp->Cdb.DBaddr     = PXE_DBADDR_NOT_USED;
171
172  Snp->Cdb.StatCode   = PXE_STATCODE_INITIALIZE;
173  Snp->Cdb.StatFlags  = PXE_STATFLAGS_INITIALIZE;
174  Snp->Cdb.IFnum      = Snp->IfNum;
175  Snp->Cdb.Control    = PXE_CONTROL_LAST_CDB_IN_LIST;
176
177  //
178  // Issue UNDI command and check result.
179  //
180  DEBUG ((EFI_D_NET, "\nSnp->undi.transmit()  "));
181  DEBUG ((EFI_D_NET, "\nSnp->Cdb.OpCode  == %x", Snp->Cdb.OpCode));
182  DEBUG ((EFI_D_NET, "\nSnp->Cdb.CPBaddr == %LX", Snp->Cdb.CPBaddr));
183  DEBUG ((EFI_D_NET, "\nSnp->Cdb.DBaddr  == %LX", Snp->Cdb.DBaddr));
184  DEBUG ((EFI_D_NET, "\nCpb->FrameAddr   == %LX\n", Cpb->FrameAddr));
185
186  (*Snp->IssueUndi32Command) ((UINT64) (UINTN) &Snp->Cdb);
187
188  DEBUG ((EFI_D_NET, "\nexit Snp->undi.transmit()  "));
189  DEBUG ((EFI_D_NET, "\nSnp->Cdb.StatCode == %r", Snp->Cdb.StatCode));
190
191  //
192  // we will unmap the buffers in get_status call, not here
193  //
194  switch (Snp->Cdb.StatCode) {
195  case PXE_STATCODE_SUCCESS:
196    return EFI_SUCCESS;
197
198  case PXE_STATCODE_QUEUE_FULL:
199  case PXE_STATCODE_BUSY:
200    Status = EFI_NOT_READY;
201    break;
202
203  default:
204    Status = EFI_DEVICE_ERROR;
205  }
206
207  DEBUG (
208    (EFI_D_ERROR,
209    "\nSnp->undi.transmit()  %xh:%xh\n",
210    Snp->Cdb.StatFlags,
211    Snp->Cdb.StatCode)
212    );
213
214  return Status;
215}
216
217/**
218  Places a packet in the transmit queue of a network interface.
219
220  This function places the packet specified by Header and Buffer on the transmit
221  queue. If HeaderSize is nonzero and HeaderSize is not equal to
222  This->Mode->MediaHeaderSize, then EFI_INVALID_PARAMETER will be returned. If
223  BufferSize is less than This->Mode->MediaHeaderSize, then EFI_BUFFER_TOO_SMALL
224  will be returned. If Buffer is NULL, then EFI_INVALID_PARAMETER will be
225  returned. If HeaderSize is nonzero and DestAddr or Protocol is NULL, then
226  EFI_INVALID_PARAMETER will be returned. If the transmit engine of the network
227  interface is busy, then EFI_NOT_READY will be returned. If this packet can be
228  accepted by the transmit engine of the network interface, the packet contents
229  specified by Buffer will be placed on the transmit queue of the network
230  interface, and EFI_SUCCESS will be returned. GetStatus() can be used to
231  determine when the packet has actually been transmitted. The contents of the
232  Buffer must not be modified until the packet has actually been transmitted.
233  The Transmit() function performs nonblocking I/O. A caller who wants to perform
234  blocking I/O, should call Transmit(), and then GetStatus() until the
235  transmitted buffer shows up in the recycled transmit buffer.
236  If the driver has not been initialized, EFI_DEVICE_ERROR will be returned.
237
238  @param This       A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
239  @param HeaderSize The size, in bytes, of the media header to be filled in by the
240                    Transmit() function. If HeaderSize is nonzero, then it must
241                    be equal to This->Mode->MediaHeaderSize and the DestAddr and
242                    Protocol parameters must not be NULL.
243  @param BufferSize The size, in bytes, of the entire packet (media header and
244                    data) to be transmitted through the network interface.
245  @param Buffer     A pointer to the packet (media header followed by data) to be
246                    transmitted. This parameter cannot be NULL. If HeaderSize is
247                    zero, then the media header in Buffer must already be filled
248                    in by the caller. If HeaderSize is nonzero, then the media
249                    header will be filled in by the Transmit() function.
250  @param SrcAddr    The source HW MAC address. If HeaderSize is zero, then this
251                    parameter is ignored. If HeaderSize is nonzero and SrcAddr
252                    is NULL, then This->Mode->CurrentAddress is used for the
253                    source HW MAC address.
254  @param DestAddr   The destination HW MAC address. If HeaderSize is zero, then
255                    this parameter is ignored.
256  @param Protocol   The type of header to build. If HeaderSize is zero, then this
257                    parameter is ignored. See RFC 1700, section "Ether Types,"
258                    for examples.
259
260  @retval EFI_SUCCESS           The packet was placed on the transmit queue.
261  @retval EFI_NOT_STARTED       The network interface has not been started.
262  @retval EFI_NOT_READY         The network interface is too busy to accept this
263                                transmit request.
264  @retval EFI_BUFFER_TOO_SMALL  The BufferSize parameter is too small.
265  @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported
266                                value.
267  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
268  @retval EFI_UNSUPPORTED       This function is not supported by the network interface.
269
270**/
271EFI_STATUS
272EFIAPI
273SnpUndi32Transmit (
274  IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
275  IN UINTN                       HeaderSize,
276  IN UINTN                       BufferSize,
277  IN VOID                        *Buffer,
278  IN EFI_MAC_ADDRESS             *SrcAddr,  OPTIONAL
279  IN EFI_MAC_ADDRESS             *DestAddr, OPTIONAL
280  IN UINT16                      *Protocol  OPTIONAL
281  )
282{
283  SNP_DRIVER  *Snp;
284  EFI_STATUS  Status;
285  EFI_TPL     OldTpl;
286
287  if (This == NULL) {
288    return EFI_INVALID_PARAMETER;
289  }
290
291  Snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);
292
293  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
294
295  if (Snp == NULL) {
296    return EFI_DEVICE_ERROR;
297  }
298
299  switch (Snp->Mode.State) {
300  case EfiSimpleNetworkInitialized:
301    break;
302
303  case EfiSimpleNetworkStopped:
304    Status = EFI_NOT_STARTED;
305    goto ON_EXIT;
306
307  default:
308    Status = EFI_DEVICE_ERROR;
309    goto ON_EXIT;
310  }
311
312  if (Buffer == NULL) {
313    Status = EFI_INVALID_PARAMETER;
314    goto ON_EXIT;
315  }
316
317  if (BufferSize < Snp->Mode.MediaHeaderSize) {
318    Status = EFI_BUFFER_TOO_SMALL;
319    goto ON_EXIT;
320  }
321
322  //
323  // if the HeaderSize is non-zero, we need to fill up the header and for that
324  // we need the destination address and the protocol
325  //
326  if (HeaderSize != 0) {
327    if (HeaderSize != Snp->Mode.MediaHeaderSize || DestAddr == 0 || Protocol == 0) {
328      Status = EFI_INVALID_PARAMETER;
329      goto ON_EXIT;
330    }
331
332    Status = PxeFillHeader (
333              Snp,
334              Buffer,
335              HeaderSize,
336              (UINT8 *) Buffer + HeaderSize,
337              BufferSize - HeaderSize,
338              DestAddr,
339              SrcAddr,
340              Protocol
341              );
342
343    if (EFI_ERROR (Status)) {
344      goto ON_EXIT;
345    }
346  }
347
348  Status = PxeTransmit (Snp, Buffer, BufferSize);
349
350ON_EXIT:
351  gBS->RestoreTPL (OldTpl);
352
353  return Status;
354}
355