iPXE
pxe_undi.c
Go to the documentation of this file.
00001 /** @file
00002  *
00003  * PXE UNDI API
00004  *
00005  */
00006 
00007 /*
00008  * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
00009  *
00010  * This program is free software; you can redistribute it and/or
00011  * modify it under the terms of the GNU General Public License as
00012  * published by the Free Software Foundation; either version 2 of the
00013  * License, or any later version.
00014  *
00015  * This program is distributed in the hope that it will be useful, but
00016  * WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018  * General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00023  * 02110-1301, USA.
00024  *
00025  * You can also choose to distribute this program under the terms of
00026  * the Unmodified Binary Distribution Licence (as given in the file
00027  * COPYING.UBDL), provided that you have satisfied its requirements.
00028  */
00029 
00030 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00031 
00032 #include <stdint.h>
00033 #include <stdio.h>
00034 #include <string.h>
00035 #include <byteswap.h>
00036 #include <basemem_packet.h>
00037 #include <ipxe/netdevice.h>
00038 #include <ipxe/iobuf.h>
00039 #include <ipxe/device.h>
00040 #include <ipxe/pci.h>
00041 #include <ipxe/if_ether.h>
00042 #include <ipxe/ip.h>
00043 #include <ipxe/arp.h>
00044 #include <ipxe/rarp.h>
00045 #include <ipxe/profile.h>
00046 #include "pxe.h"
00047 
00048 /**
00049  * Count of outstanding transmitted packets
00050  *
00051  * This is incremented each time PXENV_UNDI_TRANSMIT is called, and
00052  * decremented each time that PXENV_UNDI_ISR is called with the TX
00053  * queue empty, stopping when the count reaches zero.  This allows us
00054  * to provide a pessimistic approximation of TX completion events to
00055  * the PXE NBP simply by monitoring the netdev's TX queue.
00056  */
00057 static int undi_tx_count = 0;
00058 
00059 struct net_device *pxe_netdev = NULL;
00060 
00061 /** Transmit profiler */
00062 static struct profiler undi_tx_profiler __profiler = { .name = "undi.tx" };
00063 
00064 /**
00065  * Set network device as current PXE network device
00066  *
00067  * @v netdev            Network device, or NULL
00068  */
00069 void pxe_set_netdev ( struct net_device *netdev ) {
00070 
00071         if ( pxe_netdev ) {
00072                 netdev_rx_unfreeze ( pxe_netdev );
00073                 netdev_put ( pxe_netdev );
00074         }
00075 
00076         pxe_netdev = NULL;
00077 
00078         if ( netdev )
00079                 pxe_netdev = netdev_get ( netdev );
00080 }
00081 
00082 /**
00083  * Open PXE network device
00084  *
00085  * @ret rc              Return status code
00086  */
00087 static int pxe_netdev_open ( void ) {
00088         int rc;
00089 
00090         assert ( pxe_netdev != NULL );
00091 
00092         if ( ( rc = netdev_open ( pxe_netdev ) ) != 0 )
00093                 return rc;
00094 
00095         netdev_rx_freeze ( pxe_netdev );
00096         netdev_irq ( pxe_netdev, 1 );
00097 
00098         return 0;
00099 }
00100 
00101 /**
00102  * Close PXE network device
00103  *
00104  */
00105 static void pxe_netdev_close ( void ) {
00106 
00107         assert ( pxe_netdev != NULL );
00108         netdev_rx_unfreeze ( pxe_netdev );
00109         netdev_irq ( pxe_netdev, 0 );
00110         netdev_close ( pxe_netdev );
00111         undi_tx_count = 0;
00112 }
00113 
00114 /**
00115  * Dump multicast address list
00116  *
00117  * @v mcast             PXE multicast address list
00118  */
00119 static void pxe_dump_mcast_list ( struct s_PXENV_UNDI_MCAST_ADDRESS *mcast ) {
00120         struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
00121         unsigned int i;
00122 
00123         for ( i = 0 ; i < mcast->MCastAddrCount ; i++ ) {
00124                 DBGC ( &pxe_netdev, " %s",
00125                        ll_protocol->ntoa ( mcast->McastAddr[i] ) );
00126         }
00127 }
00128 
00129 /* PXENV_UNDI_STARTUP
00130  *
00131  * Status: working
00132  */
00133 static PXENV_EXIT_t
00134 pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
00135         DBGC ( &pxe_netdev, "PXENV_UNDI_STARTUP\n" );
00136 
00137         /* Sanity check */
00138         if ( ! pxe_netdev ) {
00139                 DBGC ( &pxe_netdev, "PXENV_UNDI_STARTUP called with no "
00140                        "network device\n" );
00141                 undi_startup->Status = PXENV_STATUS_UNDI_INVALID_STATE;
00142                 return PXENV_EXIT_FAILURE;
00143         }
00144 
00145         undi_startup->Status = PXENV_STATUS_SUCCESS;
00146         return PXENV_EXIT_SUCCESS;
00147 }
00148 
00149 /* PXENV_UNDI_CLEANUP
00150  *
00151  * Status: working
00152  */
00153 static PXENV_EXIT_t
00154 pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) {
00155         DBGC ( &pxe_netdev, "PXENV_UNDI_CLEANUP\n" );
00156 
00157         /* Sanity check */
00158         if ( ! pxe_netdev ) {
00159                 DBGC ( &pxe_netdev, "PXENV_UNDI_CLEANUP called with no "
00160                        "network device\n" );
00161                 undi_cleanup->Status = PXENV_STATUS_UNDI_INVALID_STATE;
00162                 return PXENV_EXIT_FAILURE;
00163         }
00164 
00165         /* Close network device */
00166         pxe_netdev_close();
00167 
00168         undi_cleanup->Status = PXENV_STATUS_SUCCESS;
00169         return PXENV_EXIT_SUCCESS;
00170 }
00171 
00172 /* PXENV_UNDI_INITIALIZE
00173  *
00174  * Status: working
00175  */
00176 static PXENV_EXIT_t
00177 pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE *undi_initialize ) {
00178         DBGC ( &pxe_netdev, "PXENV_UNDI_INITIALIZE protocolini %08x\n",
00179                undi_initialize->ProtocolIni );
00180 
00181         /* Sanity check */
00182         if ( ! pxe_netdev ) {
00183                 DBGC ( &pxe_netdev, "PXENV_UNDI_INITIALIZE called with no "
00184                        "network device\n" );
00185                 undi_initialize->Status = PXENV_STATUS_UNDI_INVALID_STATE;
00186                 return PXENV_EXIT_FAILURE;
00187         }
00188 
00189         undi_initialize->Status = PXENV_STATUS_SUCCESS;
00190         return PXENV_EXIT_SUCCESS;
00191 }
00192 
00193 /* PXENV_UNDI_RESET_ADAPTER
00194  *
00195  * Status: working
00196  */
00197 static PXENV_EXIT_t
00198 pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET *undi_reset_adapter ) {
00199         int rc;
00200 
00201         DBGC ( &pxe_netdev, "PXENV_UNDI_RESET_ADAPTER" );
00202         pxe_dump_mcast_list ( &undi_reset_adapter->R_Mcast_Buf );
00203         DBGC ( &pxe_netdev, "\n" );
00204 
00205         /* Sanity check */
00206         if ( ! pxe_netdev ) {
00207                 DBGC ( &pxe_netdev, "PXENV_UNDI_RESET_ADAPTER called with no "
00208                        "network device\n" );
00209                 undi_reset_adapter->Status = PXENV_STATUS_UNDI_INVALID_STATE;
00210                 return PXENV_EXIT_FAILURE;
00211         }
00212 
00213         /* Close and reopen network device */
00214         pxe_netdev_close();
00215         if ( ( rc = pxe_netdev_open() ) != 0 ) {
00216                 DBGC ( &pxe_netdev, "PXENV_UNDI_RESET_ADAPTER could not "
00217                        "reopen %s: %s\n", pxe_netdev->name, strerror ( rc ) );
00218                 undi_reset_adapter->Status = PXENV_STATUS ( rc );
00219                 return PXENV_EXIT_FAILURE;
00220         }
00221 
00222         undi_reset_adapter->Status = PXENV_STATUS_SUCCESS;
00223         return PXENV_EXIT_SUCCESS;
00224 }
00225 
00226 /* PXENV_UNDI_SHUTDOWN
00227  *
00228  * Status: working
00229  */
00230 static PXENV_EXIT_t
00231 pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN *undi_shutdown ) {
00232         DBGC ( &pxe_netdev, "PXENV_UNDI_SHUTDOWN\n" );
00233 
00234         /* Sanity check */
00235         if ( ! pxe_netdev ) {
00236                 DBGC ( &pxe_netdev, "PXENV_UNDI_SHUTDOWN called with no "
00237                        "network device\n" );
00238                 undi_shutdown->Status = PXENV_STATUS_UNDI_INVALID_STATE;
00239                 return PXENV_EXIT_FAILURE;
00240         }
00241 
00242         /* Close network device */
00243         pxe_netdev_close();
00244 
00245         undi_shutdown->Status = PXENV_STATUS_SUCCESS;
00246         return PXENV_EXIT_SUCCESS;
00247 }
00248 
00249 /* PXENV_UNDI_OPEN
00250  *
00251  * Status: working
00252  */
00253 static PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) {
00254         int rc;
00255 
00256         DBGC ( &pxe_netdev, "PXENV_UNDI_OPEN flag %04x filter %04x",
00257                undi_open->OpenFlag, undi_open->PktFilter );
00258         pxe_dump_mcast_list ( &undi_open->R_Mcast_Buf );
00259         DBGC ( &pxe_netdev, "\n" );
00260 
00261         /* Sanity check */
00262         if ( ! pxe_netdev ) {
00263                 DBGC ( &pxe_netdev, "PXENV_UNDI_OPEN called with no "
00264                        "network device\n" );
00265                 undi_open->Status = PXENV_STATUS_UNDI_INVALID_STATE;
00266                 return PXENV_EXIT_FAILURE;
00267         }
00268 
00269         /* Open network device */
00270         if ( ( rc = pxe_netdev_open() ) != 0 ) {
00271                 DBGC ( &pxe_netdev, "PXENV_UNDI_OPEN could not open %s: %s\n",
00272                        pxe_netdev->name, strerror ( rc ) );
00273                 undi_open->Status = PXENV_STATUS ( rc );
00274                 return PXENV_EXIT_FAILURE;
00275         }
00276 
00277         undi_open->Status = PXENV_STATUS_SUCCESS;
00278         return PXENV_EXIT_SUCCESS;
00279 }
00280 
00281 /* PXENV_UNDI_CLOSE
00282  *
00283  * Status: working
00284  */
00285 static PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) {
00286         DBGC ( &pxe_netdev, "PXENV_UNDI_CLOSE\n" );
00287 
00288         /* Sanity check */
00289         if ( ! pxe_netdev ) {
00290                 DBGC ( &pxe_netdev, "PXENV_UNDI_CLOSE called with no "
00291                        "network device\n" );
00292                 undi_close->Status = PXENV_STATUS_UNDI_INVALID_STATE;
00293                 return PXENV_EXIT_FAILURE;
00294         }
00295 
00296         /* Close network device */
00297         pxe_netdev_close();
00298 
00299         undi_close->Status = PXENV_STATUS_SUCCESS;
00300         return PXENV_EXIT_SUCCESS;
00301 }
00302 
00303 /* PXENV_UNDI_TRANSMIT
00304  *
00305  * Status: working
00306  */
00307 static PXENV_EXIT_t
00308 pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT *undi_transmit ) {
00309         struct s_PXENV_UNDI_TBD tbd;
00310         struct DataBlk *datablk;
00311         struct io_buffer *iobuf;
00312         struct net_protocol *net_protocol;
00313         struct ll_protocol *ll_protocol;
00314         char destaddr[MAX_LL_ADDR_LEN];
00315         const void *ll_dest;
00316         size_t len;
00317         unsigned int i;
00318         int rc;
00319 
00320         /* Start profiling */
00321         profile_start ( &undi_tx_profiler );
00322 
00323         /* Sanity check */
00324         if ( ! pxe_netdev ) {
00325                 DBGC ( &pxe_netdev, "PXENV_UNDI_TRANSMIT called with no "
00326                        "network device\n" );
00327                 undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_STATE;
00328                 return PXENV_EXIT_FAILURE;
00329         }
00330 
00331         DBGC2 ( &pxe_netdev, "PXENV_UNDI_TRANSMIT" );
00332 
00333         /* Forcibly enable interrupts and freeze receive queue
00334          * processing at this point, to work around callers that never
00335          * call PXENV_UNDI_OPEN before attempting to use the UNDI API.
00336          */
00337         if ( ! netdev_rx_frozen ( pxe_netdev ) ) {
00338                 netdev_rx_freeze ( pxe_netdev );
00339                 netdev_irq ( pxe_netdev, 1 );
00340         }
00341 
00342         /* Identify network-layer protocol */
00343         switch ( undi_transmit->Protocol ) {
00344         case P_IP:      net_protocol = &ipv4_protocol;  break;
00345         case P_ARP:     net_protocol = &arp_protocol;   break;
00346         case P_RARP:    net_protocol = &rarp_protocol;  break;
00347         case P_UNKNOWN:
00348                 net_protocol = NULL;
00349                 break;
00350         default:
00351                 DBGC2 ( &pxe_netdev, " %02x invalid protocol\n",
00352                         undi_transmit->Protocol );
00353                 undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
00354                 return PXENV_EXIT_FAILURE;
00355         }
00356         DBGC2 ( &pxe_netdev, " %s",
00357                 ( net_protocol ? net_protocol->name : "RAW" ) );
00358 
00359         /* Calculate total packet length */
00360         copy_from_real ( &tbd, undi_transmit->TBD.segment,
00361                          undi_transmit->TBD.offset, sizeof ( tbd ) );
00362         len = tbd.ImmedLength;
00363         DBGC2 ( &pxe_netdev, " %04x:%04x+%x", tbd.Xmit.segment, tbd.Xmit.offset,
00364                 tbd.ImmedLength );
00365         for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
00366                 datablk = &tbd.DataBlock[i];
00367                 len += datablk->TDDataLen;
00368                 DBGC2 ( &pxe_netdev, " %04x:%04x+%x",
00369                         datablk->TDDataPtr.segment, datablk->TDDataPtr.offset,
00370                         datablk->TDDataLen );
00371         }
00372 
00373         /* Allocate and fill I/O buffer */
00374         iobuf = alloc_iob ( MAX_LL_HEADER_LEN +
00375                             ( ( len > IOB_ZLEN ) ? len : IOB_ZLEN ) );
00376         if ( ! iobuf ) {
00377                 DBGC2 ( &pxe_netdev, " could not allocate iobuf\n" );
00378                 undi_transmit->Status = PXENV_STATUS_OUT_OF_RESOURCES;
00379                 return PXENV_EXIT_FAILURE;
00380         }
00381         iob_reserve ( iobuf, MAX_LL_HEADER_LEN );
00382         copy_from_real ( iob_put ( iobuf, tbd.ImmedLength ), tbd.Xmit.segment,
00383                          tbd.Xmit.offset, tbd.ImmedLength );
00384         for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
00385                 datablk = &tbd.DataBlock[i];
00386                 copy_from_real ( iob_put ( iobuf, datablk->TDDataLen ),
00387                                  datablk->TDDataPtr.segment,
00388                                  datablk->TDDataPtr.offset,
00389                                  datablk->TDDataLen );
00390         }
00391 
00392         /* Add link-layer header, if required to do so */
00393         if ( net_protocol != NULL ) {
00394 
00395                 /* Calculate destination address */
00396                 ll_protocol = pxe_netdev->ll_protocol;
00397                 if ( undi_transmit->XmitFlag == XMT_DESTADDR ) {
00398                         copy_from_real ( destaddr,
00399                                          undi_transmit->DestAddr.segment,
00400                                          undi_transmit->DestAddr.offset,
00401                                          ll_protocol->ll_addr_len );
00402                         ll_dest = destaddr;
00403                         DBGC2 ( &pxe_netdev, " DEST %s",
00404                                 ll_protocol->ntoa ( ll_dest ) );
00405                 } else {
00406                         ll_dest = pxe_netdev->ll_broadcast;
00407                         DBGC2 ( &pxe_netdev, " BCAST" );
00408                 }
00409 
00410                 /* Add link-layer header */
00411                 if ( ( rc = ll_protocol->push ( pxe_netdev, iobuf, ll_dest,
00412                                                 pxe_netdev->ll_addr,
00413                                                 net_protocol->net_proto ))!=0){
00414                         DBGC2 ( &pxe_netdev, " could not add link-layer "
00415                                 "header: %s\n", strerror ( rc ) );
00416                         free_iob ( iobuf );
00417                         undi_transmit->Status = PXENV_STATUS ( rc );
00418                         return PXENV_EXIT_FAILURE;
00419                 }
00420         }
00421 
00422         /* Flag transmission as in-progress.  Do this before starting
00423          * to transmit the packet, because the ISR may trigger before
00424          * we return from netdev_tx().
00425          */
00426         undi_tx_count++;
00427 
00428         /* Transmit packet */
00429         DBGC2 ( &pxe_netdev, "\n" );
00430         if ( ( rc = netdev_tx ( pxe_netdev, iobuf ) ) != 0 ) {
00431                 DBGC2 ( &pxe_netdev, "PXENV_UNDI_TRANSMIT could not transmit: "
00432                         "%s\n", strerror ( rc ) );
00433                 undi_tx_count--;
00434                 undi_transmit->Status = PXENV_STATUS ( rc );
00435                 return PXENV_EXIT_FAILURE;
00436         }
00437 
00438         profile_stop ( &undi_tx_profiler );
00439         undi_transmit->Status = PXENV_STATUS_SUCCESS;
00440         return PXENV_EXIT_SUCCESS;
00441 }
00442 
00443 /* PXENV_UNDI_SET_MCAST_ADDRESS
00444  *
00445  * Status: working (for NICs that support receive-all-multicast)
00446  */
00447 static PXENV_EXIT_t
00448 pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS
00449                                *undi_set_mcast_address ) {
00450         DBGC ( &pxe_netdev, "PXENV_UNDI_SET_MCAST_ADDRESS" );
00451         pxe_dump_mcast_list ( &undi_set_mcast_address->R_Mcast_Buf );
00452         DBGC ( &pxe_netdev, "\n" );
00453 
00454         /* Sanity check */
00455         if ( ! pxe_netdev ) {
00456                 DBGC ( &pxe_netdev, "PXENV_UNDI_SET_MCAST_ADDRESS called with "
00457                        "no network device\n" );
00458                 undi_set_mcast_address->Status =
00459                         PXENV_STATUS_UNDI_INVALID_STATE;
00460                 return PXENV_EXIT_FAILURE;
00461         }
00462 
00463         undi_set_mcast_address->Status = PXENV_STATUS_SUCCESS;
00464         return PXENV_EXIT_SUCCESS;
00465 }
00466 
00467 /* PXENV_UNDI_SET_STATION_ADDRESS
00468  *
00469  * Status: working
00470  */
00471 static PXENV_EXIT_t
00472 pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS
00473                                  *undi_set_station_address ) {
00474         struct ll_protocol *ll_protocol;
00475 
00476         /* Sanity check */
00477         if ( ! pxe_netdev ) {
00478                 DBGC ( &pxe_netdev, "PXENV_UNDI_SET_STATION_ADDRESS called "
00479                        "with no network device\n" );
00480                 undi_set_station_address->Status =
00481                         PXENV_STATUS_UNDI_INVALID_STATE;
00482                 return PXENV_EXIT_FAILURE;
00483         }
00484 
00485         ll_protocol = pxe_netdev->ll_protocol;
00486         DBGC ( &pxe_netdev, "PXENV_UNDI_SET_STATION_ADDRESS %s",
00487                ll_protocol->ntoa ( undi_set_station_address->StationAddress ) );
00488 
00489         /* If adapter is open, the change will have no effect; return
00490          * an error
00491          */
00492         if ( netdev_is_open ( pxe_netdev ) ) {
00493                 DBGC ( &pxe_netdev, " failed: netdev is open\n" );
00494                 undi_set_station_address->Status =
00495                         PXENV_STATUS_UNDI_INVALID_STATE;
00496                 return PXENV_EXIT_FAILURE;
00497         }
00498 
00499         /* Update MAC address */
00500         memcpy ( pxe_netdev->ll_addr,
00501                  &undi_set_station_address->StationAddress,
00502                  ll_protocol->ll_addr_len );
00503 
00504         DBGC ( &pxe_netdev, "\n" );
00505         undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
00506         return PXENV_EXIT_SUCCESS;
00507 }
00508 
00509 /* PXENV_UNDI_SET_PACKET_FILTER
00510  *
00511  * Status: won't implement (would require driver API changes for no
00512  * real benefit)
00513  */
00514 static PXENV_EXIT_t
00515 pxenv_undi_set_packet_filter ( struct s_PXENV_UNDI_SET_PACKET_FILTER
00516                                *undi_set_packet_filter ) {
00517 
00518         DBGC ( &pxe_netdev, "PXENV_UNDI_SET_PACKET_FILTER %02x\n",
00519                undi_set_packet_filter->filter );
00520 
00521         /* Sanity check */
00522         if ( ! pxe_netdev ) {
00523                 DBGC ( &pxe_netdev, "PXENV_UNDI_SET_PACKET_FILTER called with "
00524                        "no network device\n" );
00525                 undi_set_packet_filter->Status =
00526                         PXENV_STATUS_UNDI_INVALID_STATE;
00527                 return PXENV_EXIT_FAILURE;
00528         }
00529 
00530         /* Pretend that we succeeded, otherwise the 3Com DOS UNDI
00531          * driver refuses to load.  (We ignore the filter value in the
00532          * PXENV_UNDI_OPEN call anyway.)
00533          */
00534         undi_set_packet_filter->Status = PXENV_STATUS_SUCCESS;
00535 
00536         return PXENV_EXIT_SUCCESS;
00537 }
00538 
00539 /* PXENV_UNDI_GET_INFORMATION
00540  *
00541  * Status: working
00542  */
00543 static PXENV_EXIT_t
00544 pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
00545                              *undi_get_information ) {
00546         struct device *dev;
00547         struct ll_protocol *ll_protocol;
00548 
00549         /* Sanity check */
00550         if ( ! pxe_netdev ) {
00551                 DBGC ( &pxe_netdev, "PXENV_UNDI_GET_INFORMATION called with no "
00552                        "network device\n" );
00553                 undi_get_information->Status = PXENV_STATUS_UNDI_INVALID_STATE;
00554                 return PXENV_EXIT_FAILURE;
00555         }
00556 
00557         DBGC ( &pxe_netdev, "PXENV_UNDI_GET_INFORMATION" );
00558 
00559         /* Fill in information */
00560         dev = pxe_netdev->dev;
00561         ll_protocol = pxe_netdev->ll_protocol;
00562         undi_get_information->BaseIo = dev->desc.ioaddr;
00563         undi_get_information->IntNumber =
00564                 ( netdev_irq_supported ( pxe_netdev ) ? dev->desc.irq : 0 );
00565         /* Cheat: assume all cards can cope with this */
00566         undi_get_information->MaxTranUnit = ETH_MAX_MTU;
00567         undi_get_information->HwType = ntohs ( ll_protocol->ll_proto );
00568         undi_get_information->HwAddrLen = ll_protocol->ll_addr_len;
00569         assert ( ll_protocol->ll_addr_len <=
00570                  sizeof ( undi_get_information->CurrentNodeAddress ) );
00571         memcpy ( &undi_get_information->CurrentNodeAddress,
00572                  pxe_netdev->ll_addr,
00573                  sizeof ( undi_get_information->CurrentNodeAddress ) );
00574         ll_protocol->init_addr ( pxe_netdev->hw_addr,
00575                                  &undi_get_information->PermNodeAddress );
00576         undi_get_information->ROMAddress = 0;
00577                 /* nic.rom_info->rom_segment; */
00578         /* We only provide the ability to receive or transmit a single
00579          * packet at a time.  This is a bootloader, not an OS.
00580          */
00581         undi_get_information->RxBufCt = 1;
00582         undi_get_information->TxBufCt = 1;
00583 
00584         DBGC ( &pxe_netdev, " io %04x irq %d mtu %d %s %s\n",
00585                undi_get_information->BaseIo, undi_get_information->IntNumber,
00586                undi_get_information->MaxTranUnit, ll_protocol->name,
00587                ll_protocol->ntoa ( &undi_get_information->CurrentNodeAddress ));
00588         undi_get_information->Status = PXENV_STATUS_SUCCESS;
00589         return PXENV_EXIT_SUCCESS;
00590 }
00591 
00592 /* PXENV_UNDI_GET_STATISTICS
00593  *
00594  * Status: working
00595  */
00596 static PXENV_EXIT_t
00597 pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
00598                             *undi_get_statistics ) {
00599 
00600         /* Sanity check */
00601         if ( ! pxe_netdev ) {
00602                 DBGC ( &pxe_netdev, "PXENV_UNDI_GET_STATISTICS called with no "
00603                        "network device\n" );
00604                 undi_get_statistics->Status = PXENV_STATUS_UNDI_INVALID_STATE;
00605                 return PXENV_EXIT_FAILURE;
00606         }
00607 
00608         DBGC ( &pxe_netdev, "PXENV_UNDI_GET_STATISTICS" );
00609 
00610         /* Report statistics */
00611         undi_get_statistics->XmtGoodFrames = pxe_netdev->tx_stats.good;
00612         undi_get_statistics->RcvGoodFrames = pxe_netdev->rx_stats.good;
00613         undi_get_statistics->RcvCRCErrors = pxe_netdev->rx_stats.bad;
00614         undi_get_statistics->RcvResourceErrors = pxe_netdev->rx_stats.bad;
00615         DBGC ( &pxe_netdev, " txok %d rxok %d rxcrc %d rxrsrc %d\n",
00616                undi_get_statistics->XmtGoodFrames,
00617                undi_get_statistics->RcvGoodFrames,
00618                undi_get_statistics->RcvCRCErrors,
00619                undi_get_statistics->RcvResourceErrors );
00620 
00621         undi_get_statistics->Status = PXENV_STATUS_SUCCESS;
00622         return PXENV_EXIT_SUCCESS;
00623 }
00624 
00625 /* PXENV_UNDI_CLEAR_STATISTICS
00626  *
00627  * Status: working
00628  */
00629 static PXENV_EXIT_t
00630 pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
00631                               *undi_clear_statistics ) {
00632         DBGC ( &pxe_netdev, "PXENV_UNDI_CLEAR_STATISTICS\n" );
00633 
00634         /* Sanity check */
00635         if ( ! pxe_netdev ) {
00636                 DBGC ( &pxe_netdev, "PXENV_UNDI_CLEAR_STATISTICS called with "
00637                        "no network device\n" );
00638                 undi_clear_statistics->Status = PXENV_STATUS_UNDI_INVALID_STATE;
00639                 return PXENV_EXIT_FAILURE;
00640         }
00641 
00642         /* Clear statistics */
00643         memset ( &pxe_netdev->tx_stats, 0, sizeof ( pxe_netdev->tx_stats ) );
00644         memset ( &pxe_netdev->rx_stats, 0, sizeof ( pxe_netdev->rx_stats ) );
00645 
00646         undi_clear_statistics->Status = PXENV_STATUS_SUCCESS;
00647         return PXENV_EXIT_SUCCESS;
00648 }
00649 
00650 /* PXENV_UNDI_INITIATE_DIAGS
00651  *
00652  * Status: won't implement (would require driver API changes for no
00653  * real benefit)
00654  */
00655 static PXENV_EXIT_t
00656 pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
00657                             *undi_initiate_diags ) {
00658         DBGC ( &pxe_netdev, "PXENV_UNDI_INITIATE_DIAGS failed: unsupported\n" );
00659 
00660         /* Sanity check */
00661         if ( ! pxe_netdev ) {
00662                 DBGC ( &pxe_netdev, "PXENV_UNDI_INITIATE_DIAGS called with no "
00663                        "network device\n" );
00664                 undi_initiate_diags->Status = PXENV_STATUS_UNDI_INVALID_STATE;
00665                 return PXENV_EXIT_FAILURE;
00666         }
00667 
00668         undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
00669         return PXENV_EXIT_FAILURE;
00670 }
00671 
00672 /* PXENV_UNDI_FORCE_INTERRUPT
00673  *
00674  * Status: won't implement (would require driver API changes for no
00675  * perceptible benefit)
00676  */
00677 static PXENV_EXIT_t
00678 pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
00679                              *undi_force_interrupt ) {
00680         DBGC ( &pxe_netdev,
00681                "PXENV_UNDI_FORCE_INTERRUPT failed: unsupported\n" );
00682 
00683         /* Sanity check */
00684         if ( ! pxe_netdev ) {
00685                 DBGC ( &pxe_netdev, "PXENV_UNDI_FORCE_INTERRUPT called with no "
00686                        "network device\n" );
00687                 undi_force_interrupt->Status = PXENV_STATUS_UNDI_INVALID_STATE;
00688                 return PXENV_EXIT_FAILURE;
00689         }
00690 
00691         undi_force_interrupt->Status = PXENV_STATUS_UNSUPPORTED;
00692         return PXENV_EXIT_FAILURE;
00693 }
00694 
00695 /* PXENV_UNDI_GET_MCAST_ADDRESS
00696  *
00697  * Status: working
00698  */
00699 static PXENV_EXIT_t
00700 pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS
00701                                *undi_get_mcast_address ) {
00702         struct ll_protocol *ll_protocol;
00703         struct in_addr ip = { .s_addr = undi_get_mcast_address->InetAddr };
00704         int rc;
00705 
00706         /* Sanity check */
00707         if ( ! pxe_netdev ) {
00708                 DBGC ( &pxe_netdev, "PXENV_UNDI_GET_MCAST_ADDRESS called with "
00709                        "no network device\n" );
00710                 undi_get_mcast_address->Status =
00711                         PXENV_STATUS_UNDI_INVALID_STATE;
00712                 return PXENV_EXIT_FAILURE;
00713         }
00714 
00715         DBGC ( &pxe_netdev, "PXENV_UNDI_GET_MCAST_ADDRESS %s",
00716                inet_ntoa ( ip ) );
00717 
00718         /* Hash address using the network device's link-layer protocol */
00719         ll_protocol = pxe_netdev->ll_protocol;
00720         if ( ( rc = ll_protocol->mc_hash ( AF_INET, &ip,
00721                                       undi_get_mcast_address->MediaAddr ))!=0){
00722                 DBGC ( &pxe_netdev, " failed: %s\n", strerror ( rc ) );
00723                 undi_get_mcast_address->Status = PXENV_STATUS ( rc );
00724                 return PXENV_EXIT_FAILURE;
00725         }
00726         DBGC ( &pxe_netdev, "=>%s\n",
00727                ll_protocol->ntoa ( undi_get_mcast_address->MediaAddr ) );
00728 
00729         undi_get_mcast_address->Status = PXENV_STATUS_SUCCESS;
00730         return PXENV_EXIT_SUCCESS;
00731 }
00732 
00733 /* PXENV_UNDI_GET_NIC_TYPE
00734  *
00735  * Status: working
00736  */
00737 static PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE
00738                                               *undi_get_nic_type ) {
00739         struct device *dev;
00740 
00741         /* Sanity check */
00742         if ( ! pxe_netdev ) {
00743                 DBGC ( &pxe_netdev, "PXENV_UNDI_GET_NIC_TYPE called with "
00744                        "no network device\n" );
00745                 undi_get_nic_type->Status = PXENV_STATUS_UNDI_INVALID_STATE;
00746                 return PXENV_EXIT_FAILURE;
00747         }
00748 
00749         DBGC ( &pxe_netdev, "PXENV_UNDI_GET_NIC_TYPE" );
00750 
00751         /* Fill in information */
00752         memset ( &undi_get_nic_type->info, 0,
00753                  sizeof ( undi_get_nic_type->info ) );
00754         dev = pxe_netdev->dev;
00755         switch ( dev->desc.bus_type ) {
00756         case BUS_TYPE_PCI: {
00757                 struct pci_nic_info *info = &undi_get_nic_type->info.pci;
00758 
00759                 undi_get_nic_type->NicType = PCI_NIC;
00760                 info->Vendor_ID = dev->desc.vendor;
00761                 info->Dev_ID = dev->desc.device;
00762                 info->Base_Class = PCI_BASE_CLASS ( dev->desc.class );
00763                 info->Sub_Class = PCI_SUB_CLASS ( dev->desc.class );
00764                 info->Prog_Intf = PCI_PROG_INTF ( dev->desc.class );
00765                 info->BusDevFunc = dev->desc.location;
00766                 /* Earlier versions of the PXE specification do not
00767                  * have the SubVendor_ID and SubDevice_ID fields.  It
00768                  * is possible that some NBPs will not provide space
00769                  * for them, and so we must not fill them in.
00770                  */
00771                 DBGC ( &pxe_netdev, " PCI %02x:%02x.%x %04x:%04x "
00772                        "('%04x:%04x') %02x%02x%02x rev %02x\n",
00773                        PCI_BUS ( info->BusDevFunc ),
00774                        PCI_SLOT ( info->BusDevFunc ),
00775                        PCI_FUNC ( info->BusDevFunc ), info->Vendor_ID,
00776                        info->Dev_ID, info->SubVendor_ID, info->SubDevice_ID,
00777                        info->Base_Class, info->Sub_Class, info->Prog_Intf,
00778                        info->Rev );
00779                 break; }
00780         case BUS_TYPE_ISAPNP: {
00781                 struct pnp_nic_info *info = &undi_get_nic_type->info.pnp;
00782 
00783                 undi_get_nic_type->NicType = PnP_NIC;
00784                 info->EISA_Dev_ID = ( ( dev->desc.vendor << 16 ) |
00785                                       dev->desc.device );
00786                 info->CardSelNum = dev->desc.location;
00787                 /* Cheat: remaining fields are probably unnecessary,
00788                  * and would require adding extra code to isapnp.c.
00789                  */
00790                 DBGC ( &pxe_netdev, " ISAPnP CSN %04x %08x %02x%02x%02x\n",
00791                        info->CardSelNum, info->EISA_Dev_ID,
00792                        info->Base_Class, info->Sub_Class, info->Prog_Intf );
00793                 break; }
00794         default:
00795                 DBGC ( &pxe_netdev, " failed: unknown bus type\n" );
00796                 undi_get_nic_type->Status = PXENV_STATUS_FAILURE;
00797                 return PXENV_EXIT_FAILURE;
00798         }
00799 
00800         undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
00801         return PXENV_EXIT_SUCCESS;
00802 }
00803 
00804 /* PXENV_UNDI_GET_IFACE_INFO
00805  *
00806  * Status: working
00807  */
00808 static PXENV_EXIT_t
00809 pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO
00810                             *undi_get_iface_info ) {
00811 
00812         /* Sanity check */
00813         if ( ! pxe_netdev ) {
00814                 DBGC ( &pxe_netdev, "PXENV_UNDI_GET_IFACE_INFO called with "
00815                        "no network device\n" );
00816                 undi_get_iface_info->Status = PXENV_STATUS_UNDI_INVALID_STATE;
00817                 return PXENV_EXIT_FAILURE;
00818         }
00819 
00820         DBGC ( &pxe_netdev, "PXENV_UNDI_GET_IFACE_INFO" );
00821 
00822         /* Just hand back some info, doesn't really matter what it is.
00823          * Most PXE stacks seem to take this approach.
00824          */
00825         snprintf ( ( char * ) undi_get_iface_info->IfaceType,
00826                    sizeof ( undi_get_iface_info->IfaceType ), "DIX+802.3" );
00827         undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
00828         undi_get_iface_info->ServiceFlags =
00829                 ( SUPPORTED_BROADCAST | SUPPORTED_MULTICAST |
00830                   SUPPORTED_SET_STATION_ADDRESS | SUPPORTED_RESET |
00831                   SUPPORTED_OPEN_CLOSE );
00832         if ( netdev_irq_supported ( pxe_netdev ) )
00833                 undi_get_iface_info->ServiceFlags |= SUPPORTED_IRQ;
00834         memset ( undi_get_iface_info->Reserved, 0,
00835                  sizeof(undi_get_iface_info->Reserved) );
00836 
00837         DBGC ( &pxe_netdev, " %s %dbps flags %08x\n",
00838                undi_get_iface_info->IfaceType, undi_get_iface_info->LinkSpeed,
00839                undi_get_iface_info->ServiceFlags );
00840         undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
00841         return PXENV_EXIT_SUCCESS;
00842 }
00843 
00844 /* PXENV_UNDI_GET_STATE
00845  *
00846  * Status: impossible due to opcode collision
00847  */
00848 
00849 /* PXENV_UNDI_ISR
00850  *
00851  * Status: working
00852  */
00853 static PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
00854         struct io_buffer *iobuf;
00855         size_t len;
00856         struct ll_protocol *ll_protocol;
00857         const void *ll_dest;
00858         const void *ll_source;
00859         uint16_t net_proto;
00860         unsigned int flags;
00861         size_t ll_hlen;
00862         struct net_protocol *net_protocol;
00863         unsigned int prottype;
00864         int rc;
00865 
00866         /* Use a different debug colour, since UNDI ISR messages are
00867          * likely to be interspersed amongst other UNDI messages.
00868          */
00869 
00870         /* Sanity check */
00871         if ( ! pxe_netdev ) {
00872                 DBGC ( &pxenv_undi_isr, "PXENV_UNDI_ISR called with "
00873                        "no network device\n" );
00874                 undi_isr->Status = PXENV_STATUS_UNDI_INVALID_STATE;
00875                 return PXENV_EXIT_FAILURE;
00876         }
00877 
00878         DBGC2 ( &pxenv_undi_isr, "PXENV_UNDI_ISR" );
00879 
00880         /* Just in case some idiot actually looks at these fields when
00881          * we weren't meant to fill them in...
00882          */
00883         undi_isr->BufferLength = 0;
00884         undi_isr->FrameLength = 0;
00885         undi_isr->FrameHeaderLength = 0;
00886         undi_isr->ProtType = 0;
00887         undi_isr->PktType = 0;
00888 
00889         switch ( undi_isr->FuncFlag ) {
00890         case PXENV_UNDI_ISR_IN_START :
00891                 DBGC2 ( &pxenv_undi_isr, " START" );
00892 
00893                 /* Call poll().  This should acknowledge the device
00894                  * interrupt and queue up any received packet.
00895                  */
00896                 net_poll();
00897 
00898                 /* A 100% accurate determination of "OURS" vs "NOT
00899                  * OURS" is difficult to achieve without invasive and
00900                  * unpleasant changes to the driver model.  We settle
00901                  * for always returning "OURS" if interrupts are
00902                  * currently enabled.
00903                  *
00904                  * Returning "NOT OURS" when interrupts are disabled
00905                  * allows us to avoid a potential interrupt storm when
00906                  * we are on a shared interrupt line; if we were to
00907                  * always return "OURS" then the other device's ISR
00908                  * may never be called.
00909                  */
00910                 if ( netdev_irq_enabled ( pxe_netdev ) ) {
00911                         DBGC2 ( &pxenv_undi_isr, " OURS" );
00912                         undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
00913                 } else {
00914                         DBGC2 ( &pxenv_undi_isr, " NOT OURS" );
00915                         undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_NOT_OURS;
00916                 }
00917 
00918                 /* Disable interrupts */
00919                 netdev_irq ( pxe_netdev, 0 );
00920 
00921                 break;
00922         case PXENV_UNDI_ISR_IN_PROCESS :
00923         case PXENV_UNDI_ISR_IN_GET_NEXT :
00924                 DBGC2 ( &pxenv_undi_isr, " %s",
00925                         ( ( undi_isr->FuncFlag == PXENV_UNDI_ISR_IN_PROCESS ) ?
00926                           "PROCESS" : "GET_NEXT" ) );
00927 
00928                 /* Some dumb NBPs (e.g. emBoot's winBoot/i) never call
00929                  * PXENV_UNDI_ISR with FuncFlag=PXENV_UNDI_ISR_START;
00930                  * they just sit in a tight polling loop merrily
00931                  * violating the PXE spec with repeated calls to
00932                  * PXENV_UNDI_ISR_IN_PROCESS.  Force extra polls to
00933                  * cope with these out-of-spec clients.
00934                  */
00935                 net_poll();
00936 
00937                 /* If we have not yet marked a TX as complete, and the
00938                  * netdev TX queue is empty, report the TX completion.
00939                  */
00940                 if ( undi_tx_count && list_empty ( &pxe_netdev->tx_queue ) ) {
00941                         DBGC2 ( &pxenv_undi_isr, " TXC" );
00942                         undi_tx_count--;
00943                         undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_TRANSMIT;
00944                         break;
00945                 }
00946 
00947                 /* Remove first packet from netdev RX queue */
00948                 iobuf = netdev_rx_dequeue ( pxe_netdev );
00949                 if ( ! iobuf ) {
00950                         DBGC2 ( &pxenv_undi_isr, " DONE" );
00951                         /* No more packets remaining */
00952                         undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
00953                         /* Re-enable interrupts */
00954                         netdev_irq ( pxe_netdev, 1 );
00955                         break;
00956                 }
00957 
00958                 /* Copy packet to base memory buffer */
00959                 len = iob_len ( iobuf );
00960                 DBGC2 ( &pxenv_undi_isr, " RX" );
00961                 if ( len > sizeof ( basemem_packet ) ) {
00962                         /* Should never happen */
00963                         DBGC2 ( &pxenv_undi_isr, " overlength (%zx)", len );
00964                         len = sizeof ( basemem_packet );
00965                 }
00966                 memcpy ( basemem_packet, iobuf->data, len );
00967 
00968                 /* Strip link-layer header */
00969                 ll_protocol = pxe_netdev->ll_protocol;
00970                 if ( ( rc = ll_protocol->pull ( pxe_netdev, iobuf, &ll_dest,
00971                                                 &ll_source, &net_proto,
00972                                                 &flags ) ) != 0 ) {
00973                         /* Assume unknown net_proto and no ll_source */
00974                         net_proto = 0;
00975                         ll_source = NULL;
00976                 }
00977                 ll_hlen = ( len - iob_len ( iobuf ) );
00978 
00979                 /* Determine network-layer protocol */
00980                 switch ( net_proto ) {
00981                 case htons ( ETH_P_IP ):
00982                         net_protocol = &ipv4_protocol;
00983                         prottype = P_IP;
00984                         break;
00985                 case htons ( ETH_P_ARP ):
00986                         net_protocol = &arp_protocol;
00987                         prottype = P_ARP;
00988                         break;
00989                 case htons ( ETH_P_RARP ):
00990                         net_protocol = &rarp_protocol;
00991                         prottype = P_RARP;
00992                         break;
00993                 default:
00994                         net_protocol = NULL;
00995                         prottype = P_UNKNOWN;
00996                         break;
00997                 }
00998 
00999                 /* Fill in UNDI_ISR structure */
01000                 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
01001                 undi_isr->BufferLength = len;
01002                 undi_isr->FrameLength = len;
01003                 undi_isr->FrameHeaderLength = ll_hlen;
01004                 undi_isr->Frame.segment = rm_ds;
01005                 undi_isr->Frame.offset = __from_data16 ( basemem_packet );
01006                 undi_isr->ProtType = prottype;
01007                 if ( flags & LL_BROADCAST ) {
01008                         undi_isr->PktType = P_BROADCAST;
01009                 } else if ( flags & LL_MULTICAST ) {
01010                         undi_isr->PktType = P_MULTICAST;
01011                 } else {
01012                         undi_isr->PktType = P_DIRECTED;
01013                 }
01014                 DBGC2 ( &pxenv_undi_isr, " %04x:%04x+%x(%x) %s hlen %d",
01015                         undi_isr->Frame.segment, undi_isr->Frame.offset,
01016                         undi_isr->BufferLength, undi_isr->FrameLength,
01017                         ( net_protocol ? net_protocol->name : "RAW" ),
01018                         undi_isr->FrameHeaderLength );
01019 
01020                 /* Free packet */
01021                 free_iob ( iobuf );
01022                 break;
01023         default :
01024                 DBGC2 ( &pxenv_undi_isr, " INVALID(%04x)\n",
01025                         undi_isr->FuncFlag );
01026 
01027                 /* Should never happen */
01028                 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
01029                 undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
01030                 return PXENV_EXIT_FAILURE;
01031         }
01032 
01033         DBGC2 ( &pxenv_undi_isr, "\n" );
01034         undi_isr->Status = PXENV_STATUS_SUCCESS;
01035         return PXENV_EXIT_SUCCESS;
01036 }
01037 
01038 /** PXE UNDI API */
01039 struct pxe_api_call pxe_undi_api[] __pxe_api_call = {
01040         PXE_API_CALL ( PXENV_UNDI_STARTUP, pxenv_undi_startup,
01041                        struct s_PXENV_UNDI_STARTUP ),
01042         PXE_API_CALL ( PXENV_UNDI_CLEANUP, pxenv_undi_cleanup,
01043                        struct s_PXENV_UNDI_CLEANUP ),
01044         PXE_API_CALL ( PXENV_UNDI_INITIALIZE, pxenv_undi_initialize,
01045                        struct s_PXENV_UNDI_INITIALIZE ),
01046         PXE_API_CALL ( PXENV_UNDI_RESET_ADAPTER, pxenv_undi_reset_adapter,
01047                        struct s_PXENV_UNDI_RESET ),
01048         PXE_API_CALL ( PXENV_UNDI_SHUTDOWN, pxenv_undi_shutdown,
01049                        struct s_PXENV_UNDI_SHUTDOWN ),
01050         PXE_API_CALL ( PXENV_UNDI_OPEN, pxenv_undi_open,
01051                        struct s_PXENV_UNDI_OPEN ),
01052         PXE_API_CALL ( PXENV_UNDI_CLOSE, pxenv_undi_close,
01053                        struct s_PXENV_UNDI_CLOSE ),
01054         PXE_API_CALL ( PXENV_UNDI_TRANSMIT, pxenv_undi_transmit,
01055                        struct s_PXENV_UNDI_TRANSMIT ),
01056         PXE_API_CALL ( PXENV_UNDI_SET_MCAST_ADDRESS,
01057                        pxenv_undi_set_mcast_address,
01058                        struct s_PXENV_UNDI_SET_MCAST_ADDRESS ),
01059         PXE_API_CALL ( PXENV_UNDI_SET_STATION_ADDRESS,
01060                        pxenv_undi_set_station_address,
01061                        struct s_PXENV_UNDI_SET_STATION_ADDRESS ),
01062         PXE_API_CALL ( PXENV_UNDI_SET_PACKET_FILTER,
01063                        pxenv_undi_set_packet_filter,
01064                        struct s_PXENV_UNDI_SET_PACKET_FILTER ),
01065         PXE_API_CALL ( PXENV_UNDI_GET_INFORMATION, pxenv_undi_get_information,
01066                        struct s_PXENV_UNDI_GET_INFORMATION ),
01067         PXE_API_CALL ( PXENV_UNDI_GET_STATISTICS, pxenv_undi_get_statistics,
01068                        struct s_PXENV_UNDI_GET_STATISTICS ),
01069         PXE_API_CALL ( PXENV_UNDI_CLEAR_STATISTICS, pxenv_undi_clear_statistics,
01070                        struct s_PXENV_UNDI_CLEAR_STATISTICS ),
01071         PXE_API_CALL ( PXENV_UNDI_INITIATE_DIAGS, pxenv_undi_initiate_diags,
01072                        struct s_PXENV_UNDI_INITIATE_DIAGS ),
01073         PXE_API_CALL ( PXENV_UNDI_FORCE_INTERRUPT, pxenv_undi_force_interrupt,
01074                        struct s_PXENV_UNDI_FORCE_INTERRUPT ),
01075         PXE_API_CALL ( PXENV_UNDI_GET_MCAST_ADDRESS,
01076                        pxenv_undi_get_mcast_address,
01077                        struct s_PXENV_UNDI_GET_MCAST_ADDRESS ),
01078         PXE_API_CALL ( PXENV_UNDI_GET_NIC_TYPE, pxenv_undi_get_nic_type,
01079                        struct s_PXENV_UNDI_GET_NIC_TYPE ),
01080         PXE_API_CALL ( PXENV_UNDI_GET_IFACE_INFO, pxenv_undi_get_iface_info,
01081                        struct s_PXENV_UNDI_GET_IFACE_INFO ),
01082         PXE_API_CALL ( PXENV_UNDI_ISR, pxenv_undi_isr,
01083                        struct s_PXENV_UNDI_ISR ),
01084 };