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