[pxe] Avoid potential interrupt storms when using shared interrupts
[people/peper/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 /**
94  * Dump multicast address list
95  *
96  * @v mcast             PXE multicast address list
97  */
98 static void pxe_dump_mcast_list ( struct s_PXENV_UNDI_MCAST_ADDRESS *mcast ) {
99         struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
100         unsigned int i;
101
102         for ( i = 0 ; i < mcast->MCastAddrCount ; i++ ) {
103                 DBG ( " %s", ll_protocol->ntoa ( mcast->McastAddr[i] ) );
104         }
105 }
106
107 /* PXENV_UNDI_STARTUP
108  *
109  * Status: working
110  */
111 PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
112         DBG ( "PXENV_UNDI_STARTUP\n" );
113
114         undi_startup->Status = PXENV_STATUS_SUCCESS;
115         return PXENV_EXIT_SUCCESS;
116 }
117
118 /* PXENV_UNDI_CLEANUP
119  *
120  * Status: working
121  */
122 PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) {
123         DBG ( "PXENV_UNDI_CLEANUP\n" );
124
125         pxe_netdev_close();
126
127         undi_cleanup->Status = PXENV_STATUS_SUCCESS;
128         return PXENV_EXIT_SUCCESS;
129 }
130
131 /* PXENV_UNDI_INITIALIZE
132  *
133  * Status: working
134  */
135 PXENV_EXIT_t pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE
136                                      *undi_initialize ) {
137         DBG ( "PXENV_UNDI_INITIALIZE protocolini %08x\n",
138               undi_initialize->ProtocolIni );
139
140         undi_initialize->Status = PXENV_STATUS_SUCCESS;
141         return PXENV_EXIT_SUCCESS;
142 }
143
144 /* PXENV_UNDI_RESET_ADAPTER
145  *
146  * Status: working
147  */
148 PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET
149                                         *undi_reset_adapter ) {
150         int rc;
151
152         DBG ( "PXENV_UNDI_RESET_ADAPTER" );
153         pxe_dump_mcast_list ( &undi_reset_adapter->R_Mcast_Buf );
154         DBG ( "\n" );
155
156         pxe_netdev_close();
157         if ( ( rc = pxe_netdev_open() ) != 0 ) {
158                 DBG ( "PXENV_UNDI_RESET_ADAPTER could not reopen %s: %s\n",
159                       pxe_netdev->name, strerror ( rc ) );
160                 undi_reset_adapter->Status = PXENV_STATUS ( rc );
161                 return PXENV_EXIT_FAILURE;
162         }
163
164         undi_reset_adapter->Status = PXENV_STATUS_SUCCESS;
165         return PXENV_EXIT_SUCCESS;
166 }
167
168 /* PXENV_UNDI_SHUTDOWN
169  *
170  * Status: working
171  */
172 PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN
173                                    *undi_shutdown ) {
174         DBG ( "PXENV_UNDI_SHUTDOWN\n" );
175
176         pxe_netdev_close();
177
178         undi_shutdown->Status = PXENV_STATUS_SUCCESS;
179         return PXENV_EXIT_SUCCESS;
180 }
181
182 /* PXENV_UNDI_OPEN
183  *
184  * Status: working
185  */
186 PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) {
187         int rc;
188
189         DBG ( "PXENV_UNDI_OPEN flag %04x filter %04x",
190               undi_open->OpenFlag, undi_open->PktFilter );
191         pxe_dump_mcast_list ( &undi_open->R_Mcast_Buf );
192         DBG ( "\n" );
193
194         if ( ( rc = pxe_netdev_open() ) != 0 ) {
195                 DBG ( "PXENV_UNDI_OPEN could not open %s: %s\n",
196                       pxe_netdev->name, strerror ( rc ) );
197                 undi_open->Status = PXENV_STATUS ( rc );
198                 return PXENV_EXIT_FAILURE;
199         }
200
201         undi_open->Status = PXENV_STATUS_SUCCESS;
202         return PXENV_EXIT_SUCCESS;
203 }
204
205 /* PXENV_UNDI_CLOSE
206  *
207  * Status: working
208  */
209 PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) {
210         DBG ( "PXENV_UNDI_CLOSE\n" );
211
212         pxe_netdev_close();
213
214         undi_close->Status = PXENV_STATUS_SUCCESS;
215         return PXENV_EXIT_SUCCESS;
216 }
217
218 /* PXENV_UNDI_TRANSMIT
219  *
220  * Status: working
221  */
222 PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT
223                                    *undi_transmit ) {
224         struct s_PXENV_UNDI_TBD tbd;
225         struct DataBlk *datablk;
226         struct io_buffer *iobuf;
227         struct net_protocol *net_protocol;
228         struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
229         char destaddr[MAX_LL_ADDR_LEN];
230         const void *ll_dest;
231         size_t ll_hlen = ll_protocol->ll_header_len;
232         size_t len;
233         unsigned int i;
234         int rc;
235
236         DBG2 ( "PXENV_UNDI_TRANSMIT" );
237
238         /* Forcibly enable interrupts at this point, to work around
239          * callers that never call PXENV_UNDI_OPEN before attempting
240          * to use the UNDI API.
241          */
242         netdev_irq ( pxe_netdev, 1 );
243
244         /* Identify network-layer protocol */
245         switch ( undi_transmit->Protocol ) {
246         case P_IP:      net_protocol = &ipv4_protocol;  break;
247         case P_ARP:     net_protocol = &arp_protocol;   break;
248         case P_RARP:    net_protocol = &rarp_protocol;  break;
249         case P_UNKNOWN:
250                 net_protocol = NULL;
251                 ll_hlen = 0;
252                 break;
253         default:
254                 DBG2 ( " %02x invalid protocol\n", undi_transmit->Protocol );
255                 undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
256                 return PXENV_EXIT_FAILURE;
257         }
258         DBG2 ( " %s", ( net_protocol ? net_protocol->name : "RAW" ) );
259
260         /* Calculate total packet length */
261         copy_from_real ( &tbd, undi_transmit->TBD.segment,
262                          undi_transmit->TBD.offset, sizeof ( tbd ) );
263         len = tbd.ImmedLength;
264         DBG2 ( " %04x:%04x+%x", tbd.Xmit.segment, tbd.Xmit.offset,
265                tbd.ImmedLength );
266         for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
267                 datablk = &tbd.DataBlock[i];
268                 len += datablk->TDDataLen;
269                 DBG2 ( " %04x:%04x+%x", datablk->TDDataPtr.segment,
270                        datablk->TDDataPtr.offset, datablk->TDDataLen );
271         }
272
273         /* Allocate and fill I/O buffer */
274         iobuf = alloc_iob ( ll_hlen + len );
275         if ( ! iobuf ) {
276                 DBG2 ( " could not allocate iobuf\n" );
277                 undi_transmit->Status = PXENV_STATUS_OUT_OF_RESOURCES;
278                 return PXENV_EXIT_FAILURE;
279         }
280         iob_reserve ( iobuf, ll_hlen );
281         copy_from_real ( iob_put ( iobuf, tbd.ImmedLength ), tbd.Xmit.segment,
282                          tbd.Xmit.offset, tbd.ImmedLength );
283         for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
284                 datablk = &tbd.DataBlock[i];
285                 copy_from_real ( iob_put ( iobuf, datablk->TDDataLen ),
286                                  datablk->TDDataPtr.segment,
287                                  datablk->TDDataPtr.offset,
288                                  datablk->TDDataLen );
289         }
290
291         /* Add link-layer header, if required to do so */
292         if ( net_protocol != NULL ) {
293
294                 /* Calculate destination address */
295                 if ( undi_transmit->XmitFlag == XMT_DESTADDR ) {
296                         copy_from_real ( destaddr,
297                                          undi_transmit->DestAddr.segment,
298                                          undi_transmit->DestAddr.offset,
299                                          ll_protocol->ll_addr_len );
300                         ll_dest = destaddr;
301                         DBG2 ( " DEST %s", ll_protocol->ntoa ( ll_dest ) );
302                 } else {
303                         ll_dest = pxe_netdev->ll_broadcast;
304                         DBG2 ( " BCAST" );
305                 }
306
307                 /* Add link-layer header */
308                 if ( ( rc = ll_protocol->push ( pxe_netdev, iobuf, ll_dest,
309                                                 pxe_netdev->ll_addr,
310                                                 net_protocol->net_proto ))!=0){
311                         DBG2 ( " could not add link-layer header: %s\n",
312                                strerror ( rc ) );
313                         free_iob ( iobuf );
314                         undi_transmit->Status = PXENV_STATUS ( rc );
315                         return PXENV_EXIT_FAILURE;
316                 }
317         }
318
319         /* Flag transmission as in-progress.  Do this before starting
320          * to transmit the packet, because the ISR may trigger before
321          * we return from netdev_tx().
322          */
323         undi_tx_count++;
324
325         /* Transmit packet */
326         DBG2 ( "\n" );
327         if ( ( rc = netdev_tx ( pxe_netdev, iobuf ) ) != 0 ) {
328                 DBG2 ( "PXENV_UNDI_TRANSMIT could not transmit: %s\n",
329                        strerror ( rc ) );
330                 undi_tx_count--;
331                 undi_transmit->Status = PXENV_STATUS ( rc );
332                 return PXENV_EXIT_FAILURE;
333         }
334
335         undi_transmit->Status = PXENV_STATUS_SUCCESS;
336         return PXENV_EXIT_SUCCESS;
337 }
338
339 /* PXENV_UNDI_SET_MCAST_ADDRESS
340  *
341  * Status: working (for NICs that support receive-all-multicast)
342  */
343 PXENV_EXIT_t
344 pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS
345                                *undi_set_mcast_address ) {
346         DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" );
347         pxe_dump_mcast_list ( &undi_set_mcast_address->R_Mcast_Buf );
348         DBG ( "\n" );
349
350         undi_set_mcast_address->Status = PXENV_STATUS_SUCCESS;
351         return PXENV_EXIT_SUCCESS;
352 }
353
354 /* PXENV_UNDI_SET_STATION_ADDRESS
355  *
356  * Status: working
357  */
358 PXENV_EXIT_t 
359 pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS
360                                  *undi_set_station_address ) {
361         struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
362
363         DBG ( "PXENV_UNDI_SET_STATION_ADDRESS %s",
364               ll_protocol->ntoa ( undi_set_station_address->StationAddress ) );
365
366         /* If adapter is open, the change will have no effect; return
367          * an error
368          */
369         if ( netdev_is_open ( pxe_netdev ) ) {
370                 DBG ( " failed: netdev is open\n" );
371                 undi_set_station_address->Status =
372                         PXENV_STATUS_UNDI_INVALID_STATE;
373                 return PXENV_EXIT_FAILURE;
374         }
375
376         /* Update MAC address */
377         memcpy ( pxe_netdev->ll_addr,
378                  &undi_set_station_address->StationAddress,
379                  ll_protocol->ll_addr_len );
380
381         DBG ( "\n" );
382         undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
383         return PXENV_EXIT_SUCCESS;
384 }
385
386 /* PXENV_UNDI_SET_PACKET_FILTER
387  *
388  * Status: won't implement (would require driver API changes for no
389  * real benefit)
390  */
391 PXENV_EXIT_t
392 pxenv_undi_set_packet_filter ( struct s_PXENV_UNDI_SET_PACKET_FILTER
393                                *undi_set_packet_filter ) {
394
395         DBG ( "PXENV_UNDI_SET_PACKET_FILTER %02x\n",
396               undi_set_packet_filter->filter );
397
398         /* Pretend that we succeeded, otherwise the 3Com DOS UNDI
399          * driver refuses to load.  (We ignore the filter value in the
400          * PXENV_UNDI_OPEN call anyway.)
401          */
402         undi_set_packet_filter->Status = PXENV_STATUS_SUCCESS;
403
404         return PXENV_EXIT_SUCCESS;
405 }
406
407 /* PXENV_UNDI_GET_INFORMATION
408  *
409  * Status: working
410  */
411 PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
412                                           *undi_get_information ) {
413         struct device *dev = pxe_netdev->dev;
414         struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
415         size_t ll_addr_len = ll_protocol->ll_addr_len;
416
417         DBG ( "PXENV_UNDI_GET_INFORMATION" );
418
419         undi_get_information->BaseIo = dev->desc.ioaddr;
420         undi_get_information->IntNumber = dev->desc.irq;
421         /* Cheat: assume all cards can cope with this */
422         undi_get_information->MaxTranUnit = ETH_MAX_MTU;
423         undi_get_information->HwType = ntohs ( ll_protocol->ll_proto );
424         undi_get_information->HwAddrLen = ll_addr_len;
425         assert ( ll_addr_len <=
426                  sizeof ( undi_get_information->CurrentNodeAddress ) );
427         memcpy ( &undi_get_information->CurrentNodeAddress,
428                  pxe_netdev->ll_addr,
429                  sizeof ( undi_get_information->CurrentNodeAddress ) );
430         ll_protocol->init_addr ( pxe_netdev->hw_addr,
431                                  &undi_get_information->PermNodeAddress );
432         undi_get_information->ROMAddress = 0;
433                 /* nic.rom_info->rom_segment; */
434         /* We only provide the ability to receive or transmit a single
435          * packet at a time.  This is a bootloader, not an OS.
436          */
437         undi_get_information->RxBufCt = 1;
438         undi_get_information->TxBufCt = 1;
439
440         DBG ( " io %04x irq %d mtu %d %s %s\n",
441               undi_get_information->BaseIo, undi_get_information->IntNumber,
442               undi_get_information->MaxTranUnit, ll_protocol->name,
443               ll_protocol->ntoa ( &undi_get_information->CurrentNodeAddress ));
444         undi_get_information->Status = PXENV_STATUS_SUCCESS;
445         return PXENV_EXIT_SUCCESS;
446 }
447
448 /* PXENV_UNDI_GET_STATISTICS
449  *
450  * Status: working
451  */
452 PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
453                                          *undi_get_statistics ) {
454         DBG ( "PXENV_UNDI_GET_STATISTICS" );
455
456         undi_get_statistics->XmtGoodFrames = pxe_netdev->tx_stats.good;
457         undi_get_statistics->RcvGoodFrames = pxe_netdev->rx_stats.good;
458         undi_get_statistics->RcvCRCErrors = pxe_netdev->rx_stats.bad;
459         undi_get_statistics->RcvResourceErrors = pxe_netdev->rx_stats.bad;
460
461         DBG ( " txok %d rxok %d rxcrc %d rxrsrc %d\n",
462               undi_get_statistics->XmtGoodFrames,
463               undi_get_statistics->RcvGoodFrames,
464               undi_get_statistics->RcvCRCErrors,
465               undi_get_statistics->RcvResourceErrors );
466         undi_get_statistics->Status = PXENV_STATUS_SUCCESS;
467         return PXENV_EXIT_SUCCESS;
468 }
469
470 /* PXENV_UNDI_CLEAR_STATISTICS
471  *
472  * Status: working
473  */
474 PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
475                                            *undi_clear_statistics ) {
476         DBG ( "PXENV_UNDI_CLEAR_STATISTICS\n" );
477
478         memset ( &pxe_netdev->tx_stats, 0, sizeof ( pxe_netdev->tx_stats ) );
479         memset ( &pxe_netdev->rx_stats, 0, sizeof ( pxe_netdev->rx_stats ) );
480
481         undi_clear_statistics->Status = PXENV_STATUS_SUCCESS;
482         return PXENV_EXIT_SUCCESS;
483 }
484
485 /* PXENV_UNDI_INITIATE_DIAGS
486  *
487  * Status: won't implement (would require driver API changes for no
488  * real benefit)
489  */
490 PXENV_EXIT_t pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
491                                          *undi_initiate_diags ) {
492         DBG ( "PXENV_UNDI_INITIATE_DIAGS failed: unsupported\n" );
493
494         undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
495         return PXENV_EXIT_FAILURE;
496 }
497
498 /* PXENV_UNDI_FORCE_INTERRUPT
499  *
500  * Status: won't implement (would require driver API changes for no
501  * perceptible benefit)
502  */
503 PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
504                                           *undi_force_interrupt ) {
505         DBG ( "PXENV_UNDI_FORCE_INTERRUPT failed: unsupported\n" );
506
507         undi_force_interrupt->Status = PXENV_STATUS_UNSUPPORTED;
508         return PXENV_EXIT_FAILURE;
509 }
510
511 /* PXENV_UNDI_GET_MCAST_ADDRESS
512  *
513  * Status: working
514  */
515 PXENV_EXIT_t
516 pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS
517                                *undi_get_mcast_address ) {
518         struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
519         struct in_addr ip = { .s_addr = undi_get_mcast_address->InetAddr };
520         int rc;
521
522         DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS %s", inet_ntoa ( ip ) );
523
524         if ( ( rc = ll_protocol->mc_hash ( AF_INET, &ip,
525                                       undi_get_mcast_address->MediaAddr ))!=0){
526                 DBG ( " failed: %s\n", strerror ( rc ) );
527                 undi_get_mcast_address->Status = PXENV_STATUS ( rc );
528                 return PXENV_EXIT_FAILURE;
529         }
530         DBG ( "=>%s\n",
531               ll_protocol->ntoa ( undi_get_mcast_address->MediaAddr ) );
532
533         undi_get_mcast_address->Status = PXENV_STATUS_SUCCESS;
534         return PXENV_EXIT_SUCCESS;
535 }
536
537 /* PXENV_UNDI_GET_NIC_TYPE
538  *
539  * Status: working
540  */
541 PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE
542                                        *undi_get_nic_type ) {
543         struct device *dev = pxe_netdev->dev;
544
545         DBG ( "PXENV_UNDI_GET_NIC_TYPE" );
546
547         memset ( &undi_get_nic_type->info, 0,
548                  sizeof ( undi_get_nic_type->info ) );
549
550         switch ( dev->desc.bus_type ) {
551         case BUS_TYPE_PCI: {
552                 struct pci_nic_info *info = &undi_get_nic_type->info.pci;
553
554                 undi_get_nic_type->NicType = PCI_NIC;
555                 info->Vendor_ID = dev->desc.vendor;
556                 info->Dev_ID = dev->desc.device;
557                 info->Base_Class = PCI_BASE_CLASS ( dev->desc.class );
558                 info->Sub_Class = PCI_SUB_CLASS ( dev->desc.class );
559                 info->Prog_Intf = PCI_PROG_INTF ( dev->desc.class );
560                 info->BusDevFunc = dev->desc.location;
561                 /* Cheat: remaining fields are probably unnecessary,
562                  * and would require adding extra code to pci.c.
563                  */
564                 undi_get_nic_type->info.pci.SubVendor_ID = 0xffff;
565                 undi_get_nic_type->info.pci.SubDevice_ID = 0xffff;
566                 DBG ( " PCI %02x:%02x.%x %04x:%04x (%04x:%04x) %02x%02x%02x "
567                       "rev %02x\n", PCI_BUS ( info->BusDevFunc ),
568                       PCI_SLOT ( info->BusDevFunc ),
569                       PCI_FUNC ( info->BusDevFunc ), info->Vendor_ID,
570                       info->Dev_ID, info->SubVendor_ID, info->SubDevice_ID,
571                       info->Base_Class, info->Sub_Class, info->Prog_Intf,
572                       info->Rev );
573                 break; }
574         case BUS_TYPE_ISAPNP: {
575                 struct pnp_nic_info *info = &undi_get_nic_type->info.pnp;
576
577                 undi_get_nic_type->NicType = PnP_NIC;
578                 info->EISA_Dev_ID = ( ( dev->desc.vendor << 16 ) |
579                                       dev->desc.device );
580                 info->CardSelNum = dev->desc.location;
581                 /* Cheat: remaining fields are probably unnecessary,
582                  * and would require adding extra code to isapnp.c.
583                  */
584                 DBG ( " ISAPnP CSN %04x %08x %02x%02x%02x\n",
585                       info->CardSelNum, info->EISA_Dev_ID,
586                       info->Base_Class, info->Sub_Class, info->Prog_Intf );
587                 break; }
588         default:
589                 DBG ( " failed: unknown bus type\n" );
590                 undi_get_nic_type->Status = PXENV_STATUS_FAILURE;
591                 return PXENV_EXIT_FAILURE;
592         }
593
594         undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
595         return PXENV_EXIT_SUCCESS;
596 }
597
598 /* PXENV_UNDI_GET_IFACE_INFO
599  *
600  * Status: working
601  */
602 PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO
603                                          *undi_get_iface_info ) {
604         DBG ( "PXENV_UNDI_GET_IFACE_INFO" );
605
606         /* Just hand back some info, doesn't really matter what it is.
607          * Most PXE stacks seem to take this approach.
608          */
609         snprintf ( ( char * ) undi_get_iface_info->IfaceType,
610                    sizeof ( undi_get_iface_info->IfaceType ), "DIX+802.3" );
611         undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
612         undi_get_iface_info->ServiceFlags =
613                 ( SUPPORTED_BROADCAST | SUPPORTED_MULTICAST |
614                   SUPPORTED_SET_STATION_ADDRESS | SUPPORTED_RESET |
615                   SUPPORTED_OPEN_CLOSE | SUPPORTED_IRQ );
616         memset ( undi_get_iface_info->Reserved, 0,
617                  sizeof(undi_get_iface_info->Reserved) );
618
619         DBG ( " %s %dbps flags %08x\n", undi_get_iface_info->IfaceType,
620               undi_get_iface_info->LinkSpeed,
621               undi_get_iface_info->ServiceFlags );
622         undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
623         return PXENV_EXIT_SUCCESS;
624 }
625
626 /* PXENV_UNDI_GET_STATE
627  *
628  * Status: impossible
629  */
630 PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE
631                                     *undi_get_state ) {
632         DBG ( "PXENV_UNDI_GET_STATE failed: unsupported\n" );
633
634         undi_get_state->Status = PXENV_STATUS_UNSUPPORTED;
635         return PXENV_EXIT_FAILURE;
636 };
637
638 /* PXENV_UNDI_ISR
639  *
640  * Status: working
641  */
642 PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
643         struct io_buffer *iobuf;
644         size_t len;
645         struct ll_protocol *ll_protocol;
646         const void *ll_dest;
647         const void *ll_source;
648         uint16_t net_proto;
649         size_t ll_hlen;
650         struct net_protocol *net_protocol;
651         unsigned int prottype;
652         int rc;
653
654         /* Use coloured debug, since UNDI ISR messages are likely to
655          * be interspersed amongst other UNDI messages.
656          */
657         DBGC2 ( &pxenv_undi_isr, "PXENV_UNDI_ISR" );
658
659         /* Just in case some idiot actually looks at these fields when
660          * we weren't meant to fill them in...
661          */
662         undi_isr->BufferLength = 0;
663         undi_isr->FrameLength = 0;
664         undi_isr->FrameHeaderLength = 0;
665         undi_isr->ProtType = 0;
666         undi_isr->PktType = 0;
667
668         switch ( undi_isr->FuncFlag ) {
669         case PXENV_UNDI_ISR_IN_START :
670                 DBGC2 ( &pxenv_undi_isr, " START" );
671
672                 /* Call poll().  This should acknowledge the device
673                  * interrupt and queue up any received packet.
674                  */
675                 netdev_poll ( pxe_netdev );
676
677                 /* A 100% accurate determination of "OURS" vs "NOT
678                  * OURS" is difficult to achieve without invasive and
679                  * unpleasant changes to the driver model.  We settle
680                  * for always returning "OURS" if interrupts are
681                  * currently enabled.
682                  *
683                  * Returning "NOT OURS" when interrupts are disabled
684                  * allows us to avoid a potential interrupt storm when
685                  * we are on a shared interrupt line; if we were to
686                  * always return "OURS" then the other device's ISR
687                  * may never be called.
688                  */
689                 if ( netdev_irq_enabled ( pxe_netdev ) ) {
690                         DBGC2 ( &pxenv_undi_isr, " OURS" );
691                         undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
692                 } else {
693                         DBGC2 ( &pxenv_undi_isr, " NOT OURS" );
694                         undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_NOT_OURS;
695                 }
696
697                 /* Disable interrupts */
698                 netdev_irq ( pxe_netdev, 0 );
699
700                 break;
701         case PXENV_UNDI_ISR_IN_PROCESS :
702         case PXENV_UNDI_ISR_IN_GET_NEXT :
703                 DBGC2 ( &pxenv_undi_isr, " %s",
704                         ( ( undi_isr->FuncFlag == PXENV_UNDI_ISR_IN_PROCESS ) ?
705                           "PROCESS" : "GET_NEXT" ) );
706
707                 /* Some dumb NBPs (e.g. emBoot's winBoot/i) never call
708                  * PXENV_UNDI_ISR with FuncFlag=PXENV_UNDI_ISR_START;
709                  * they just sit in a tight polling loop merrily
710                  * violating the PXE spec with repeated calls to
711                  * PXENV_UNDI_ISR_IN_PROCESS.  Force extra polls to
712                  * cope with these out-of-spec clients.
713                  */
714                 netdev_poll ( pxe_netdev );
715
716                 /* If we have not yet marked a TX as complete, and the
717                  * netdev TX queue is empty, report the TX completion.
718                  */
719                 if ( undi_tx_count && list_empty ( &pxe_netdev->tx_queue ) ) {
720                         DBGC2 ( &pxenv_undi_isr, " TXC" );
721                         undi_tx_count--;
722                         undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_TRANSMIT;
723                         break;
724                 }
725
726                 /* Remove first packet from netdev RX queue */
727                 iobuf = netdev_rx_dequeue ( pxe_netdev );
728                 if ( ! iobuf ) {
729                         DBGC2 ( &pxenv_undi_isr, " DONE" );
730                         /* No more packets remaining */
731                         undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
732                         /* Re-enable interrupts */
733                         netdev_irq ( pxe_netdev, 1 );
734                         break;
735                 }
736
737                 /* Copy packet to base memory buffer */
738                 len = iob_len ( iobuf );
739                 DBGC2 ( &pxenv_undi_isr, " RX" );
740                 if ( len > sizeof ( basemem_packet ) ) {
741                         /* Should never happen */
742                         DBGC2 ( &pxenv_undi_isr, " overlength (%zx)", len );
743                         len = sizeof ( basemem_packet );
744                 }
745                 memcpy ( basemem_packet, iobuf->data, len );
746
747                 /* Strip link-layer header */
748                 ll_protocol = pxe_netdev->ll_protocol;
749                 if ( ( rc = ll_protocol->pull ( pxe_netdev, iobuf, &ll_dest,
750                                                 &ll_source, &net_proto )) !=0){
751                         /* Assume unknown net_proto and no ll_source */
752                         net_proto = 0;
753                         ll_source = NULL;
754                 }
755                 ll_hlen = ( len - iob_len ( iobuf ) );
756
757                 /* Determine network-layer protocol */
758                 switch ( net_proto ) {
759                 case htons ( ETH_P_IP ):
760                         net_protocol = &ipv4_protocol;
761                         prottype = P_IP;
762                         break;
763                 case htons ( ETH_P_ARP ):
764                         net_protocol = &arp_protocol;
765                         prottype = P_ARP;
766                         break;
767                 case htons ( ETH_P_RARP ):
768                         net_protocol = &rarp_protocol;
769                         prottype = P_RARP;
770                         break;
771                 default:
772                         net_protocol = NULL;
773                         prottype = P_UNKNOWN;
774                         break;
775                 }
776
777                 /* Fill in UNDI_ISR structure */
778                 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
779                 undi_isr->BufferLength = len;
780                 undi_isr->FrameLength = len;
781                 undi_isr->FrameHeaderLength = ll_hlen;
782                 undi_isr->Frame.segment = rm_ds;
783                 undi_isr->Frame.offset = __from_data16 ( basemem_packet );
784                 undi_isr->ProtType = prottype;
785                 undi_isr->PktType = XMT_DESTADDR;
786                 DBGC2 ( &pxenv_undi_isr, " %04x:%04x+%x(%x) %s hlen %d",
787                         undi_isr->Frame.segment, undi_isr->Frame.offset,
788                         undi_isr->BufferLength, undi_isr->FrameLength,
789                         ( net_protocol ? net_protocol->name : "RAW" ),
790                         undi_isr->FrameHeaderLength );
791
792                 /* Free packet */
793                 free_iob ( iobuf );
794                 break;
795         default :
796                 DBGC2 ( &pxenv_undi_isr, " INVALID(%04x)\n",
797                         undi_isr->FuncFlag );
798
799                 /* Should never happen */
800                 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
801                 undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
802                 return PXENV_EXIT_FAILURE;
803         }
804
805         DBGC2 ( &pxenv_undi_isr, "\n" );
806         undi_isr->Status = PXENV_STATUS_SUCCESS;
807         return PXENV_EXIT_SUCCESS;
808 }