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