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