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