[undi] Work around broken UNDI polling behaviour in winBoot/i
[people/mcb30/gpxe.git] / src / interface / pxe / pxe_undi.c
1 /** @file
2  *
3  * PXE UNDI API
4  *
5  */
6
7 /*
8  * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License as
12  * published by the Free Software Foundation; either version 2 of the
13  * License, or any later version.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  */
24
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <byteswap.h>
29 #include <basemem_packet.h>
30 #include <gpxe/netdevice.h>
31 #include <gpxe/iobuf.h>
32 #include <gpxe/device.h>
33 #include <gpxe/pci.h>
34 #include <gpxe/if_ether.h>
35 #include <gpxe/ip.h>
36 #include <gpxe/arp.h>
37 #include <gpxe/rarp.h>
38 #include "pxe.h"
39
40 /**
41  * Count of outstanding transmitted packets
42  *
43  * This is incremented each time PXENV_UNDI_TRANSMIT is called, and
44  * decremented each time that PXENV_UNDI_ISR is called with the TX
45  * queue empty, stopping when the count reaches zero.  This allows us
46  * to provide a pessimistic approximation of TX completion events to
47  * the PXE NBP simply by monitoring the netdev's TX queue.
48  */
49 static int undi_tx_count = 0;
50
51 struct net_device *pxe_netdev = NULL;
52
53 /**
54  * Set network device as current PXE network device
55  *
56  * @v netdev            Network device, or NULL
57  */
58 void pxe_set_netdev ( struct net_device *netdev ) {
59         if ( pxe_netdev )
60                 netdev_put ( pxe_netdev );
61         pxe_netdev = NULL;
62         if ( netdev )
63                 pxe_netdev = netdev_get ( netdev );
64 }
65
66 /**
67  * Open PXE network device
68  *
69  * @ret rc              Return status code
70  */
71 static int pxe_netdev_open ( void ) {
72         int rc;
73
74         if ( ( rc = netdev_open ( pxe_netdev ) ) != 0 )
75                 return rc;
76
77         netdev_irq ( pxe_netdev, 1 );
78         return 0;
79 }
80
81 /**
82  * Close PXE network device
83  *
84  */
85 static void pxe_netdev_close ( void ) {
86         netdev_irq ( pxe_netdev, 0 );
87         netdev_close ( pxe_netdev );
88         undi_tx_count = 0;
89 }
90
91 /* PXENV_UNDI_STARTUP
92  *
93  * Status: working
94  */
95 PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
96         DBG ( "PXENV_UNDI_STARTUP" );
97
98         undi_startup->Status = PXENV_STATUS_SUCCESS;
99         return PXENV_EXIT_SUCCESS;
100 }
101
102 /* PXENV_UNDI_CLEANUP
103  *
104  * Status: working
105  */
106 PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) {
107         DBG ( "PXENV_UNDI_CLEANUP" );
108
109         pxe_netdev_close();
110
111         undi_cleanup->Status = PXENV_STATUS_SUCCESS;
112         return PXENV_EXIT_SUCCESS;
113 }
114
115 /* PXENV_UNDI_INITIALIZE
116  *
117  * Status: working
118  */
119 PXENV_EXIT_t pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE
120                                      *undi_initialize ) {
121         DBG ( "PXENV_UNDI_INITIALIZE" );
122
123         undi_initialize->Status = PXENV_STATUS_SUCCESS;
124         return PXENV_EXIT_SUCCESS;
125 }
126
127 /* PXENV_UNDI_RESET_ADAPTER
128  *
129  * Status: working
130  */
131 PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET
132                                         *undi_reset_adapter ) {
133         int rc;
134
135         DBG ( "PXENV_UNDI_RESET_ADAPTER" );
136
137         pxe_netdev_close();
138         if ( ( rc = pxe_netdev_open() ) != 0 ) {
139                 undi_reset_adapter->Status = PXENV_STATUS ( rc );
140                 return PXENV_EXIT_FAILURE;
141         }
142
143         undi_reset_adapter->Status = PXENV_STATUS_SUCCESS;
144         return PXENV_EXIT_SUCCESS;
145 }
146
147 /* PXENV_UNDI_SHUTDOWN
148  *
149  * Status: working
150  */
151 PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN
152                                    *undi_shutdown ) {
153         DBG ( "PXENV_UNDI_SHUTDOWN" );
154
155         pxe_netdev_close();
156
157         undi_shutdown->Status = PXENV_STATUS_SUCCESS;
158         return PXENV_EXIT_SUCCESS;
159 }
160
161 /* PXENV_UNDI_OPEN
162  *
163  * Status: working
164  */
165 PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) {
166         int rc;
167
168         DBG ( "PXENV_UNDI_OPEN" );
169
170         if ( ( rc = pxe_netdev_open() ) != 0 ) {
171                 undi_open->Status = PXENV_STATUS ( rc );
172                 return PXENV_EXIT_FAILURE;
173         }
174
175         undi_open->Status = PXENV_STATUS_SUCCESS;
176         return PXENV_EXIT_SUCCESS;
177 }
178
179 /* PXENV_UNDI_CLOSE
180  *
181  * Status: working
182  */
183 PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) {
184         DBG ( "PXENV_UNDI_CLOSE" );
185
186         pxe_netdev_close();
187
188         undi_close->Status = PXENV_STATUS_SUCCESS;
189         return PXENV_EXIT_SUCCESS;
190 }
191
192 /* PXENV_UNDI_TRANSMIT
193  *
194  * Status: working
195  */
196 PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT
197                                    *undi_transmit ) {
198         struct s_PXENV_UNDI_TBD tbd;
199         struct DataBlk *datablk;
200         struct io_buffer *iobuf;
201         struct net_protocol *net_protocol;
202         char destaddr[MAX_LL_ADDR_LEN];
203         const void *ll_dest;
204         size_t ll_hlen = pxe_netdev->ll_protocol->ll_header_len;
205         size_t len;
206         unsigned int i;
207         int rc;
208
209         DBG ( "PXENV_UNDI_TRANSMIT" );
210
211         /* Identify network-layer protocol */
212         switch ( undi_transmit->Protocol ) {
213         case P_IP:      net_protocol = &ipv4_protocol;  break;
214         case P_ARP:     net_protocol = &arp_protocol;   break;
215         case P_RARP:    net_protocol = &rarp_protocol;  break;
216         case P_UNKNOWN:
217                 net_protocol = NULL;
218                 ll_hlen = 0;
219                 break;
220         default:
221                 undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
222                 return PXENV_EXIT_FAILURE;
223         }
224         DBG ( " %s", ( net_protocol ? net_protocol->name : "RAW" ) );
225
226         /* Calculate total packet length */
227         copy_from_real ( &tbd, undi_transmit->TBD.segment,
228                          undi_transmit->TBD.offset, sizeof ( tbd ) );
229         len = tbd.ImmedLength;
230         DBG ( " %d", tbd.ImmedLength );
231         for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
232                 datablk = &tbd.DataBlock[i];
233                 len += datablk->TDDataLen;
234                 DBG ( "+%d", datablk->TDDataLen );
235         }
236
237         /* Allocate and fill I/O buffer */
238         iobuf = alloc_iob ( ll_hlen + len );
239         if ( ! iobuf ) {
240                 undi_transmit->Status = PXENV_STATUS_OUT_OF_RESOURCES;
241                 return PXENV_EXIT_FAILURE;
242         }
243         iob_reserve ( iobuf, ll_hlen );
244         copy_from_real ( iob_put ( iobuf, tbd.ImmedLength ), tbd.Xmit.segment,
245                          tbd.Xmit.offset, tbd.ImmedLength );
246         for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
247                 datablk = &tbd.DataBlock[i];
248                 copy_from_real ( iob_put ( iobuf, datablk->TDDataLen ),
249                                  datablk->TDDataPtr.segment,
250                                  datablk->TDDataPtr.offset,
251                                  datablk->TDDataLen );
252         }
253
254         /* Transmit packet */
255         if ( net_protocol == NULL ) {
256                 /* Link-layer header already present */
257                 rc = netdev_tx ( pxe_netdev, iobuf );
258         } else {
259                 /* Calculate destination address */
260                 if ( undi_transmit->XmitFlag == XMT_DESTADDR ) {
261                         copy_from_real ( destaddr,
262                                          undi_transmit->DestAddr.segment,
263                                          undi_transmit->DestAddr.offset,
264                                          pxe_netdev->ll_protocol->ll_addr_len );
265                         ll_dest = destaddr;
266                 } else {
267                         DBG ( " BCAST" );
268                         ll_dest = pxe_netdev->ll_protocol->ll_broadcast;
269                 }
270                 rc = net_tx ( iobuf, pxe_netdev, net_protocol, ll_dest );
271         }
272
273         /* Flag transmission as in-progress */
274         undi_tx_count++;
275
276         undi_transmit->Status = PXENV_STATUS ( rc );
277         return ( ( rc == 0 ) ? PXENV_EXIT_SUCCESS : PXENV_EXIT_FAILURE );
278 }
279
280 /* PXENV_UNDI_SET_MCAST_ADDRESS
281  *
282  * Status: stub (no PXE multicast support)
283  */
284 PXENV_EXIT_t
285 pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS
286                                *undi_set_mcast_address ) {
287         DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" );
288
289         undi_set_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
290         return PXENV_EXIT_FAILURE;
291 }
292
293 /* PXENV_UNDI_SET_STATION_ADDRESS
294  *
295  * Status: working
296  */
297 PXENV_EXIT_t 
298 pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS
299                                  *undi_set_station_address ) {
300
301         DBG ( "PXENV_UNDI_SET_STATION_ADDRESS" );
302
303         /* If adapter is open, the change will have no effect; return
304          * an error
305          */
306         if ( pxe_netdev->state & NETDEV_OPEN ) {
307                 undi_set_station_address->Status =
308                         PXENV_STATUS_UNDI_INVALID_STATE;
309                 return PXENV_EXIT_FAILURE;
310         }
311
312         /* Update MAC address */
313         memcpy ( pxe_netdev->ll_addr,
314                  &undi_set_station_address->StationAddress,
315                  pxe_netdev->ll_protocol->ll_addr_len );
316
317         undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
318         return PXENV_EXIT_SUCCESS;
319 }
320
321 /* PXENV_UNDI_SET_PACKET_FILTER
322  *
323  * Status: won't implement (would require driver API changes for no
324  * real benefit)
325  */
326 PXENV_EXIT_t
327 pxenv_undi_set_packet_filter ( struct s_PXENV_UNDI_SET_PACKET_FILTER
328                                *undi_set_packet_filter ) {
329         DBG ( "PXENV_UNDI_SET_PACKET_FILTER" );
330
331         undi_set_packet_filter->Status = PXENV_STATUS_UNSUPPORTED;
332         return PXENV_EXIT_FAILURE;
333 }
334
335 /* PXENV_UNDI_GET_INFORMATION
336  *
337  * Status: working
338  */
339 PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
340                                           *undi_get_information ) {
341         struct device *dev = pxe_netdev->dev;
342         struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
343
344         DBG ( "PXENV_UNDI_GET_INFORMATION" );
345
346         undi_get_information->BaseIo = dev->desc.ioaddr;
347         undi_get_information->IntNumber = dev->desc.irq;
348         /* Cheat: assume all cards can cope with this */
349         undi_get_information->MaxTranUnit = ETH_MAX_MTU;
350         undi_get_information->HwType = ntohs ( ll_protocol->ll_proto );
351         undi_get_information->HwAddrLen = ll_protocol->ll_addr_len;
352         /* Cheat: assume card is always configured with its permanent
353          * node address.  This is a valid assumption within Etherboot
354          * at the time of writing.
355          */
356         memcpy ( &undi_get_information->CurrentNodeAddress,
357                  pxe_netdev->ll_addr,
358                  sizeof ( undi_get_information->CurrentNodeAddress ) );
359         memcpy ( &undi_get_information->PermNodeAddress,
360                  pxe_netdev->ll_addr,
361                  sizeof ( undi_get_information->PermNodeAddress ) );
362         undi_get_information->ROMAddress = 0;
363                 /* nic.rom_info->rom_segment; */
364         /* We only provide the ability to receive or transmit a single
365          * packet at a time.  This is a bootloader, not an OS.
366          */
367         undi_get_information->RxBufCt = 1;
368         undi_get_information->TxBufCt = 1;
369
370         undi_get_information->Status = PXENV_STATUS_SUCCESS;
371         return PXENV_EXIT_SUCCESS;
372 }
373
374 /* PXENV_UNDI_GET_STATISTICS
375  *
376  * Status: working
377  */
378 PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
379                                          *undi_get_statistics ) {
380         DBG ( "PXENV_UNDI_GET_STATISTICS" );
381
382         undi_get_statistics->XmtGoodFrames = pxe_netdev->stats.tx_ok;
383         undi_get_statistics->RcvGoodFrames = pxe_netdev->stats.rx_ok;
384         undi_get_statistics->RcvCRCErrors = pxe_netdev->stats.rx_err;
385         undi_get_statistics->RcvResourceErrors = pxe_netdev->stats.rx_err;
386
387         undi_get_statistics->Status = PXENV_STATUS_SUCCESS;
388         return PXENV_EXIT_SUCCESS;
389 }
390
391 /* PXENV_UNDI_CLEAR_STATISTICS
392  *
393  * Status: working
394  */
395 PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
396                                            *undi_clear_statistics ) {
397         DBG ( "PXENV_UNDI_CLEAR_STATISTICS" );
398
399         memset ( &pxe_netdev->stats, 0, sizeof ( pxe_netdev->stats ) );
400
401         undi_clear_statistics->Status = PXENV_STATUS_SUCCESS;
402         return PXENV_EXIT_SUCCESS;
403 }
404
405 /* PXENV_UNDI_INITIATE_DIAGS
406  *
407  * Status: won't implement (would require driver API changes for no
408  * real benefit)
409  */
410 PXENV_EXIT_t pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
411                                          *undi_initiate_diags ) {
412         DBG ( "PXENV_UNDI_INITIATE_DIAGS" );
413
414         undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
415         return PXENV_EXIT_FAILURE;
416 }
417
418 /* PXENV_UNDI_FORCE_INTERRUPT
419  *
420  * Status: won't implement (would require driver API changes for no
421  * perceptible benefit)
422  */
423 PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
424                                           *undi_force_interrupt ) {
425         DBG ( "PXENV_UNDI_FORCE_INTERRUPT" );
426
427         undi_force_interrupt->Status = PXENV_STATUS_UNSUPPORTED;
428         return PXENV_EXIT_FAILURE;
429 }
430
431 /* PXENV_UNDI_GET_MCAST_ADDRESS
432  *
433  * Status: stub (no PXE multicast support)
434  */
435 PXENV_EXIT_t
436 pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS
437                                *undi_get_mcast_address ) {
438         DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS" );
439
440         undi_get_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
441         return PXENV_EXIT_FAILURE;
442 }
443
444 /* PXENV_UNDI_GET_NIC_TYPE
445  *
446  * Status: working
447  */
448 PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE
449                                        *undi_get_nic_type ) {
450         struct device *dev = pxe_netdev->dev;
451
452         DBG ( "PXENV_UNDI_GET_NIC_TYPE" );
453
454         memset ( &undi_get_nic_type->info, 0,
455                  sizeof ( undi_get_nic_type->info ) );
456
457         switch ( dev->desc.bus_type ) {
458         case BUS_TYPE_PCI: {
459                 struct pci_nic_info *info = &undi_get_nic_type->info.pci;
460
461                 undi_get_nic_type->NicType = PCI_NIC;
462                 info->Vendor_ID = dev->desc.vendor;
463                 info->Dev_ID = dev->desc.device;
464                 info->Base_Class = PCI_BASE_CLASS ( dev->desc.class );
465                 info->Sub_Class = PCI_SUB_CLASS ( dev->desc.class );
466                 info->Prog_Intf = PCI_PROG_INTF ( dev->desc.class );
467                 info->BusDevFunc = dev->desc.location;
468                 /* Cheat: remaining fields are probably unnecessary,
469                  * and would require adding extra code to pci.c.
470                  */
471                 undi_get_nic_type->info.pci.SubVendor_ID = 0xffff;
472                 undi_get_nic_type->info.pci.SubDevice_ID = 0xffff;
473                 break; }
474         case BUS_TYPE_ISAPNP: {
475                 struct pnp_nic_info *info = &undi_get_nic_type->info.pnp;
476
477                 undi_get_nic_type->NicType = PnP_NIC;
478                 info->EISA_Dev_ID = ( ( dev->desc.vendor << 16 ) |
479                                       dev->desc.device );
480                 info->CardSelNum = dev->desc.location;
481                 /* Cheat: remaining fields are probably unnecessary,
482                  * and would require adding extra code to isapnp.c.
483                  */
484                 break; }
485         default:
486                 undi_get_nic_type->Status = PXENV_STATUS_FAILURE;
487                 return PXENV_EXIT_FAILURE;
488         }
489
490         undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
491         return PXENV_EXIT_SUCCESS;
492 }
493
494 /* PXENV_UNDI_GET_IFACE_INFO
495  *
496  * Status: working
497  */
498 PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO
499                                          *undi_get_iface_info ) {
500         DBG ( "PXENV_UNDI_GET_IFACE_INFO" );
501
502         /* Just hand back some info, doesn't really matter what it is.
503          * Most PXE stacks seem to take this approach.
504          */
505         snprintf ( ( char * ) undi_get_iface_info->IfaceType,
506                    sizeof ( undi_get_iface_info->IfaceType ), "gPXE" );
507         undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
508         undi_get_iface_info->ServiceFlags = 0;
509         memset ( undi_get_iface_info->Reserved, 0,
510                  sizeof(undi_get_iface_info->Reserved) );
511
512         undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
513         return PXENV_EXIT_SUCCESS;
514 }
515
516 /* PXENV_UNDI_GET_STATE
517  *
518  * Status: impossible
519  */
520 PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE
521                                     *undi_get_state ) {
522         DBG ( "PXENV_UNDI_GET_STATE" );
523
524         undi_get_state->Status = PXENV_STATUS_UNSUPPORTED;
525         return PXENV_EXIT_FAILURE;
526 };
527
528 /* PXENV_UNDI_ISR
529  *
530  * Status: working
531  */
532 PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
533         struct io_buffer *iobuf;
534         size_t len;
535
536         DBG ( "PXENV_UNDI_ISR" );
537
538         /* Just in case some idiot actually looks at these fields when
539          * we weren't meant to fill them in...
540          */
541         undi_isr->BufferLength = 0;
542         undi_isr->FrameLength = 0;
543         undi_isr->FrameHeaderLength = 0;
544         undi_isr->ProtType = 0;
545         undi_isr->PktType = 0;
546
547         switch ( undi_isr->FuncFlag ) {
548         case PXENV_UNDI_ISR_IN_START :
549                 DBG ( " START" );
550
551                 /* Call poll().  This should acknowledge the device
552                  * interrupt and queue up any received packet.
553                  */
554                 netdev_poll ( pxe_netdev );
555
556                 /* Disable interrupts to avoid interrupt storm */
557                 netdev_irq ( pxe_netdev, 0 );
558
559                 /* Always say it was ours for the sake of simplicity */
560                 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
561                 break;
562         case PXENV_UNDI_ISR_IN_PROCESS :
563                 DBG ( " PROCESS" );
564                 /* Fall through */
565         case PXENV_UNDI_ISR_IN_GET_NEXT :
566                 DBG ( " GET_NEXT" );
567
568                 /* Some dumb NBPs (e.g. emBoot's winBoot/i) never call
569                  * PXENV_UNDI_ISR with FuncFlag=PXENV_UNDI_ISR_START;
570                  * they just sit in a tight polling loop merrily
571                  * violating the PXE spec with repeated calls to
572                  * PXENV_UNDI_ISR_IN_PROCESS.  Force extra polls to
573                  * cope with these out-of-spec clients.
574                  */
575                 netdev_poll ( pxe_netdev );
576
577                 /* If we have not yet marked a TX as complete, and the
578                  * netdev TX queue is empty, report the TX completion.
579                  */
580                 if ( undi_tx_count && list_empty ( &pxe_netdev->tx_queue ) ) {
581                         DBG ( " TXC" );
582                         undi_tx_count--;
583                         undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_TRANSMIT;
584                         break;
585                 }
586
587                 /* Remove first packet from netdev RX queue */
588                 iobuf = netdev_rx_dequeue ( pxe_netdev );
589                 if ( ! iobuf ) {
590                         DBG ( " DONE" );
591                         /* No more packets remaining */
592                         undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
593                         /* Re-enable interrupts */
594                         netdev_irq ( pxe_netdev, 1 );
595                         break;
596                 }
597
598                 /* Copy packet to base memory buffer */
599                 len = iob_len ( iobuf );
600                 DBG ( " RX %zd", len );
601                 if ( len > sizeof ( basemem_packet ) ) {
602                         /* Should never happen */
603                         len = sizeof ( basemem_packet );
604                 }
605                 memcpy ( basemem_packet, iobuf->data, len );
606
607                 /* Fill in UNDI_ISR structure */
608                 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
609                 undi_isr->BufferLength = len;
610                 undi_isr->FrameLength = len;
611                 undi_isr->FrameHeaderLength =
612                         pxe_netdev->ll_protocol->ll_header_len;
613                 undi_isr->Frame.segment = rm_ds;
614                 undi_isr->Frame.offset = __from_data16 ( basemem_packet );
615                 /* Probably ought to fill in packet type */
616                 undi_isr->ProtType = P_UNKNOWN;
617                 undi_isr->PktType = XMT_DESTADDR;
618
619                 /* Free packet */
620                 free_iob ( iobuf );
621                 break;
622         default :
623                 DBG ( " INVALID(%04x)", undi_isr->FuncFlag );
624
625                 /* Should never happen */
626                 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
627                 undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
628                 return PXENV_EXIT_FAILURE;
629         }
630
631         undi_isr->Status = PXENV_STATUS_SUCCESS;
632         return PXENV_EXIT_SUCCESS;
633 }