[netdevice] Change link-layer push() and pull() methods to take raw types
[people/sha0/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         struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
203         char destaddr[MAX_LL_ADDR_LEN];
204         const void *ll_dest;
205         size_t ll_hlen = ll_protocol->ll_header_len;
206         size_t len;
207         unsigned int i;
208         int rc;
209
210         DBG ( "PXENV_UNDI_TRANSMIT" );
211
212         /* Identify network-layer protocol */
213         switch ( undi_transmit->Protocol ) {
214         case P_IP:      net_protocol = &ipv4_protocol;  break;
215         case P_ARP:     net_protocol = &arp_protocol;   break;
216         case P_RARP:    net_protocol = &rarp_protocol;  break;
217         case P_UNKNOWN:
218                 net_protocol = NULL;
219                 ll_hlen = 0;
220                 break;
221         default:
222                 undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
223                 return PXENV_EXIT_FAILURE;
224         }
225         DBG ( " %s", ( net_protocol ? net_protocol->name : "RAW" ) );
226
227         /* Calculate total packet length */
228         copy_from_real ( &tbd, undi_transmit->TBD.segment,
229                          undi_transmit->TBD.offset, sizeof ( tbd ) );
230         len = tbd.ImmedLength;
231         DBG ( " %d", tbd.ImmedLength );
232         for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
233                 datablk = &tbd.DataBlock[i];
234                 len += datablk->TDDataLen;
235                 DBG ( "+%d", datablk->TDDataLen );
236         }
237
238         /* Allocate and fill I/O buffer */
239         iobuf = alloc_iob ( ll_hlen + len );
240         if ( ! iobuf ) {
241                 undi_transmit->Status = PXENV_STATUS_OUT_OF_RESOURCES;
242                 return PXENV_EXIT_FAILURE;
243         }
244         iob_reserve ( iobuf, ll_hlen );
245         copy_from_real ( iob_put ( iobuf, tbd.ImmedLength ), tbd.Xmit.segment,
246                          tbd.Xmit.offset, tbd.ImmedLength );
247         for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
248                 datablk = &tbd.DataBlock[i];
249                 copy_from_real ( iob_put ( iobuf, datablk->TDDataLen ),
250                                  datablk->TDDataPtr.segment,
251                                  datablk->TDDataPtr.offset,
252                                  datablk->TDDataLen );
253         }
254
255         /* Add link-layer header, if required to do so */
256         if ( net_protocol != NULL ) {
257
258                 /* Calculate destination address */
259                 if ( undi_transmit->XmitFlag == XMT_DESTADDR ) {
260                         copy_from_real ( destaddr,
261                                          undi_transmit->DestAddr.segment,
262                                          undi_transmit->DestAddr.offset,
263                                          ll_protocol->ll_addr_len );
264                         ll_dest = destaddr;
265                 } else {
266                         DBG ( " BCAST" );
267                         ll_dest = ll_protocol->ll_broadcast;
268                 }
269
270                 /* Add link-layer header */
271                 if ( ( rc = ll_protocol->push ( iobuf, ll_dest,
272                                                 pxe_netdev->ll_addr,
273                                                 net_protocol->net_proto ))!=0){
274                         free_iob ( iobuf );
275                         undi_transmit->Status = PXENV_STATUS ( rc );
276                         return PXENV_EXIT_FAILURE;
277                 }
278         }
279
280         /* Transmit packet */
281         if ( ( rc = netdev_tx ( pxe_netdev, iobuf ) ) != 0 ) {
282                 undi_transmit->Status = PXENV_STATUS ( rc );
283                 return PXENV_EXIT_FAILURE;
284         }
285
286         /* Flag transmission as in-progress */
287         undi_tx_count++;
288
289         undi_transmit->Status = PXENV_STATUS_SUCCESS;
290         return PXENV_EXIT_SUCCESS;
291 }
292
293 /* PXENV_UNDI_SET_MCAST_ADDRESS
294  *
295  * Status: stub (no PXE multicast support)
296  */
297 PXENV_EXIT_t
298 pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS
299                                *undi_set_mcast_address ) {
300         DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" );
301
302         undi_set_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
303         return PXENV_EXIT_FAILURE;
304 }
305
306 /* PXENV_UNDI_SET_STATION_ADDRESS
307  *
308  * Status: working
309  */
310 PXENV_EXIT_t 
311 pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS
312                                  *undi_set_station_address ) {
313
314         DBG ( "PXENV_UNDI_SET_STATION_ADDRESS" );
315
316         /* If adapter is open, the change will have no effect; return
317          * an error
318          */
319         if ( pxe_netdev->state & NETDEV_OPEN ) {
320                 undi_set_station_address->Status =
321                         PXENV_STATUS_UNDI_INVALID_STATE;
322                 return PXENV_EXIT_FAILURE;
323         }
324
325         /* Update MAC address */
326         memcpy ( pxe_netdev->ll_addr,
327                  &undi_set_station_address->StationAddress,
328                  pxe_netdev->ll_protocol->ll_addr_len );
329
330         undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
331         return PXENV_EXIT_SUCCESS;
332 }
333
334 /* PXENV_UNDI_SET_PACKET_FILTER
335  *
336  * Status: won't implement (would require driver API changes for no
337  * real benefit)
338  */
339 PXENV_EXIT_t
340 pxenv_undi_set_packet_filter ( struct s_PXENV_UNDI_SET_PACKET_FILTER
341                                *undi_set_packet_filter ) {
342         DBG ( "PXENV_UNDI_SET_PACKET_FILTER" );
343
344         undi_set_packet_filter->Status = PXENV_STATUS_UNSUPPORTED;
345         return PXENV_EXIT_FAILURE;
346 }
347
348 /* PXENV_UNDI_GET_INFORMATION
349  *
350  * Status: working
351  */
352 PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
353                                           *undi_get_information ) {
354         struct device *dev = pxe_netdev->dev;
355         struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
356
357         DBG ( "PXENV_UNDI_GET_INFORMATION" );
358
359         undi_get_information->BaseIo = dev->desc.ioaddr;
360         undi_get_information->IntNumber = dev->desc.irq;
361         /* Cheat: assume all cards can cope with this */
362         undi_get_information->MaxTranUnit = ETH_MAX_MTU;
363         undi_get_information->HwType = ntohs ( ll_protocol->ll_proto );
364         undi_get_information->HwAddrLen = ll_protocol->ll_addr_len;
365         /* Cheat: assume card is always configured with its permanent
366          * node address.  This is a valid assumption within Etherboot
367          * at the time of writing.
368          */
369         memcpy ( &undi_get_information->CurrentNodeAddress,
370                  pxe_netdev->ll_addr,
371                  sizeof ( undi_get_information->CurrentNodeAddress ) );
372         memcpy ( &undi_get_information->PermNodeAddress,
373                  pxe_netdev->ll_addr,
374                  sizeof ( undi_get_information->PermNodeAddress ) );
375         undi_get_information->ROMAddress = 0;
376                 /* nic.rom_info->rom_segment; */
377         /* We only provide the ability to receive or transmit a single
378          * packet at a time.  This is a bootloader, not an OS.
379          */
380         undi_get_information->RxBufCt = 1;
381         undi_get_information->TxBufCt = 1;
382
383         undi_get_information->Status = PXENV_STATUS_SUCCESS;
384         return PXENV_EXIT_SUCCESS;
385 }
386
387 /* PXENV_UNDI_GET_STATISTICS
388  *
389  * Status: working
390  */
391 PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
392                                          *undi_get_statistics ) {
393         DBG ( "PXENV_UNDI_GET_STATISTICS" );
394
395         undi_get_statistics->XmtGoodFrames = pxe_netdev->stats.tx_ok;
396         undi_get_statistics->RcvGoodFrames = pxe_netdev->stats.rx_ok;
397         undi_get_statistics->RcvCRCErrors = pxe_netdev->stats.rx_err;
398         undi_get_statistics->RcvResourceErrors = pxe_netdev->stats.rx_err;
399
400         undi_get_statistics->Status = PXENV_STATUS_SUCCESS;
401         return PXENV_EXIT_SUCCESS;
402 }
403
404 /* PXENV_UNDI_CLEAR_STATISTICS
405  *
406  * Status: working
407  */
408 PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
409                                            *undi_clear_statistics ) {
410         DBG ( "PXENV_UNDI_CLEAR_STATISTICS" );
411
412         memset ( &pxe_netdev->stats, 0, sizeof ( pxe_netdev->stats ) );
413
414         undi_clear_statistics->Status = PXENV_STATUS_SUCCESS;
415         return PXENV_EXIT_SUCCESS;
416 }
417
418 /* PXENV_UNDI_INITIATE_DIAGS
419  *
420  * Status: won't implement (would require driver API changes for no
421  * real benefit)
422  */
423 PXENV_EXIT_t pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
424                                          *undi_initiate_diags ) {
425         DBG ( "PXENV_UNDI_INITIATE_DIAGS" );
426
427         undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
428         return PXENV_EXIT_FAILURE;
429 }
430
431 /* PXENV_UNDI_FORCE_INTERRUPT
432  *
433  * Status: won't implement (would require driver API changes for no
434  * perceptible benefit)
435  */
436 PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
437                                           *undi_force_interrupt ) {
438         DBG ( "PXENV_UNDI_FORCE_INTERRUPT" );
439
440         undi_force_interrupt->Status = PXENV_STATUS_UNSUPPORTED;
441         return PXENV_EXIT_FAILURE;
442 }
443
444 /* PXENV_UNDI_GET_MCAST_ADDRESS
445  *
446  * Status: stub (no PXE multicast support)
447  */
448 PXENV_EXIT_t
449 pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS
450                                *undi_get_mcast_address ) {
451         DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS" );
452
453         undi_get_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
454         return PXENV_EXIT_FAILURE;
455 }
456
457 /* PXENV_UNDI_GET_NIC_TYPE
458  *
459  * Status: working
460  */
461 PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE
462                                        *undi_get_nic_type ) {
463         struct device *dev = pxe_netdev->dev;
464
465         DBG ( "PXENV_UNDI_GET_NIC_TYPE" );
466
467         memset ( &undi_get_nic_type->info, 0,
468                  sizeof ( undi_get_nic_type->info ) );
469
470         switch ( dev->desc.bus_type ) {
471         case BUS_TYPE_PCI: {
472                 struct pci_nic_info *info = &undi_get_nic_type->info.pci;
473
474                 undi_get_nic_type->NicType = PCI_NIC;
475                 info->Vendor_ID = dev->desc.vendor;
476                 info->Dev_ID = dev->desc.device;
477                 info->Base_Class = PCI_BASE_CLASS ( dev->desc.class );
478                 info->Sub_Class = PCI_SUB_CLASS ( dev->desc.class );
479                 info->Prog_Intf = PCI_PROG_INTF ( dev->desc.class );
480                 info->BusDevFunc = dev->desc.location;
481                 /* Cheat: remaining fields are probably unnecessary,
482                  * and would require adding extra code to pci.c.
483                  */
484                 undi_get_nic_type->info.pci.SubVendor_ID = 0xffff;
485                 undi_get_nic_type->info.pci.SubDevice_ID = 0xffff;
486                 break; }
487         case BUS_TYPE_ISAPNP: {
488                 struct pnp_nic_info *info = &undi_get_nic_type->info.pnp;
489
490                 undi_get_nic_type->NicType = PnP_NIC;
491                 info->EISA_Dev_ID = ( ( dev->desc.vendor << 16 ) |
492                                       dev->desc.device );
493                 info->CardSelNum = dev->desc.location;
494                 /* Cheat: remaining fields are probably unnecessary,
495                  * and would require adding extra code to isapnp.c.
496                  */
497                 break; }
498         default:
499                 undi_get_nic_type->Status = PXENV_STATUS_FAILURE;
500                 return PXENV_EXIT_FAILURE;
501         }
502
503         undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
504         return PXENV_EXIT_SUCCESS;
505 }
506
507 /* PXENV_UNDI_GET_IFACE_INFO
508  *
509  * Status: working
510  */
511 PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO
512                                          *undi_get_iface_info ) {
513         DBG ( "PXENV_UNDI_GET_IFACE_INFO" );
514
515         /* Just hand back some info, doesn't really matter what it is.
516          * Most PXE stacks seem to take this approach.
517          */
518         snprintf ( ( char * ) undi_get_iface_info->IfaceType,
519                    sizeof ( undi_get_iface_info->IfaceType ), "gPXE" );
520         undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
521         undi_get_iface_info->ServiceFlags = 0;
522         memset ( undi_get_iface_info->Reserved, 0,
523                  sizeof(undi_get_iface_info->Reserved) );
524
525         undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
526         return PXENV_EXIT_SUCCESS;
527 }
528
529 /* PXENV_UNDI_GET_STATE
530  *
531  * Status: impossible
532  */
533 PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE
534                                     *undi_get_state ) {
535         DBG ( "PXENV_UNDI_GET_STATE" );
536
537         undi_get_state->Status = PXENV_STATUS_UNSUPPORTED;
538         return PXENV_EXIT_FAILURE;
539 };
540
541 /* PXENV_UNDI_ISR
542  *
543  * Status: working
544  */
545 PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
546         struct io_buffer *iobuf;
547         size_t len;
548         struct ll_protocol *ll_protocol;
549         const void *ll_dest;
550         const void *ll_source;
551         uint16_t net_proto;
552         size_t ll_hlen;
553         struct net_protocol *net_protocol;
554         unsigned int prottype;
555         int rc;
556
557         DBG ( "PXENV_UNDI_ISR" );
558
559         /* Just in case some idiot actually looks at these fields when
560          * we weren't meant to fill them in...
561          */
562         undi_isr->BufferLength = 0;
563         undi_isr->FrameLength = 0;
564         undi_isr->FrameHeaderLength = 0;
565         undi_isr->ProtType = 0;
566         undi_isr->PktType = 0;
567
568         switch ( undi_isr->FuncFlag ) {
569         case PXENV_UNDI_ISR_IN_START :
570                 DBG ( " START" );
571
572                 /* Call poll().  This should acknowledge the device
573                  * interrupt and queue up any received packet.
574                  */
575                 netdev_poll ( pxe_netdev );
576
577                 /* Disable interrupts to avoid interrupt storm */
578                 netdev_irq ( pxe_netdev, 0 );
579
580                 /* Always say it was ours for the sake of simplicity */
581                 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
582                 break;
583         case PXENV_UNDI_ISR_IN_PROCESS :
584                 DBG ( " PROCESS" );
585                 /* Fall through */
586         case PXENV_UNDI_ISR_IN_GET_NEXT :
587                 DBG ( " GET_NEXT" );
588
589                 /* Some dumb NBPs (e.g. emBoot's winBoot/i) never call
590                  * PXENV_UNDI_ISR with FuncFlag=PXENV_UNDI_ISR_START;
591                  * they just sit in a tight polling loop merrily
592                  * violating the PXE spec with repeated calls to
593                  * PXENV_UNDI_ISR_IN_PROCESS.  Force extra polls to
594                  * cope with these out-of-spec clients.
595                  */
596                 netdev_poll ( pxe_netdev );
597
598                 /* If we have not yet marked a TX as complete, and the
599                  * netdev TX queue is empty, report the TX completion.
600                  */
601                 if ( undi_tx_count && list_empty ( &pxe_netdev->tx_queue ) ) {
602                         DBG ( " TXC" );
603                         undi_tx_count--;
604                         undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_TRANSMIT;
605                         break;
606                 }
607
608                 /* Remove first packet from netdev RX queue */
609                 iobuf = netdev_rx_dequeue ( pxe_netdev );
610                 if ( ! iobuf ) {
611                         DBG ( " DONE" );
612                         /* No more packets remaining */
613                         undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
614                         /* Re-enable interrupts */
615                         netdev_irq ( pxe_netdev, 1 );
616                         break;
617                 }
618
619                 /* Copy packet to base memory buffer */
620                 len = iob_len ( iobuf );
621                 DBG ( " RX %zd", len );
622                 if ( len > sizeof ( basemem_packet ) ) {
623                         /* Should never happen */
624                         len = sizeof ( basemem_packet );
625                 }
626                 memcpy ( basemem_packet, iobuf->data, len );
627
628                 /* Strip link-layer header */
629                 ll_protocol = pxe_netdev->ll_protocol;
630                 if ( ( rc = ll_protocol->pull ( iobuf, &ll_dest, &ll_source,
631                                                 &net_proto ) ) != 0 ) {
632                         /* Assume unknown net_proto and no ll_source */
633                         net_proto = 0;
634                         ll_source = NULL;
635                 }
636                 ll_hlen = ( len - iob_len ( iobuf ) );
637
638                 /* Determine network-layer protocol */
639                 switch ( net_proto ) {
640                 case htons ( ETH_P_IP ):
641                         net_protocol = &ipv4_protocol;
642                         prottype = P_IP;
643                         break;
644                 case htons ( ETH_P_ARP ):
645                         net_protocol = &arp_protocol;
646                         prottype = P_ARP;
647                         break;
648                 case htons ( ETH_P_RARP ):
649                         net_protocol = &rarp_protocol;
650                         prottype = P_RARP;
651                         break;
652                 default:
653                         net_protocol = NULL;
654                         prottype = P_UNKNOWN;
655                         break;
656                 }
657                 DBG ( " %s", ( net_protocol ? net_protocol->name : "RAW" ) );
658
659                 /* Fill in UNDI_ISR structure */
660                 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
661                 undi_isr->BufferLength = len;
662                 undi_isr->FrameLength = len;
663                 undi_isr->FrameHeaderLength = ll_hlen;
664                 undi_isr->Frame.segment = rm_ds;
665                 undi_isr->Frame.offset = __from_data16 ( basemem_packet );
666                 undi_isr->ProtType = prottype;
667                 undi_isr->PktType = XMT_DESTADDR;
668
669                 /* Free packet */
670                 free_iob ( iobuf );
671                 break;
672         default :
673                 DBG ( " INVALID(%04x)", undi_isr->FuncFlag );
674
675                 /* Should never happen */
676                 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
677                 undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
678                 return PXENV_EXIT_FAILURE;
679         }
680
681         undi_isr->Status = PXENV_STATUS_SUCCESS;
682         return PXENV_EXIT_SUCCESS;
683 }