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