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., 675 Mass Ave, Cambridge, MA 02139, USA.
00023  */
00024 
00025 FILE_LICENCE ( GPL2_OR_LATER );
00026 
00027 #include <stdint.h>
00028 #include <stdio.h>
00029 #include <string.h>
00030 #include <byteswap.h>
00031 #include <basemem_packet.h>
00032 #include <gpxe/netdevice.h>
00033 #include <gpxe/iobuf.h>
00034 #include <gpxe/device.h>
00035 #include <gpxe/pci.h>
00036 #include <gpxe/if_ether.h>
00037 #include <gpxe/ip.h>
00038 #include <gpxe/arp.h>
00039 #include <gpxe/rarp.h>
00040 #include "pxe.h"
00041 
00042 /**
00043  * Count of outstanding transmitted packets
00044  *
00045  * This is incremented each time PXENV_UNDI_TRANSMIT is called, and
00046  * decremented each time that PXENV_UNDI_ISR is called with the TX
00047  * queue empty, stopping when the count reaches zero.  This allows us
00048  * to provide a pessimistic approximation of TX completion events to
00049  * the PXE NBP simply by monitoring the netdev's TX queue.
00050  */
00051 static int undi_tx_count = 0;
00052 
00053 struct net_device *pxe_netdev = NULL;
00054 
00055 /**
00056  * Set network device as current PXE network device
00057  *
00058  * @v netdev            Network device, or NULL
00059  */
00060 void pxe_set_netdev ( struct net_device *netdev ) {
00061         if ( pxe_netdev )
00062                 netdev_put ( pxe_netdev );
00063         pxe_netdev = NULL;
00064         if ( netdev )
00065                 pxe_netdev = netdev_get ( netdev );
00066 }
00067 
00068 /**
00069  * Open PXE network device
00070  *
00071  * @ret rc              Return status code
00072  */
00073 static int pxe_netdev_open ( void ) {
00074         int rc;
00075 
00076         if ( ( rc = netdev_open ( pxe_netdev ) ) != 0 )
00077                 return rc;
00078 
00079         netdev_irq ( pxe_netdev, 1 );
00080         return 0;
00081 }
00082 
00083 /**
00084  * Close PXE network device
00085  *
00086  */
00087 static void pxe_netdev_close ( void ) {
00088         netdev_irq ( pxe_netdev, 0 );
00089         netdev_close ( pxe_netdev );
00090         undi_tx_count = 0;
00091 }
00092 
00093 /**
00094  * Dump multicast address list
00095  *
00096  * @v mcast             PXE multicast address list
00097  */
00098 static void pxe_dump_mcast_list ( struct s_PXENV_UNDI_MCAST_ADDRESS *mcast ) {
00099         struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
00100         unsigned int i;
00101 
00102         for ( i = 0 ; i < mcast->MCastAddrCount ; i++ ) {
00103                 DBG ( " %s", ll_protocol->ntoa ( mcast->McastAddr[i] ) );
00104         }
00105 }
00106 
00107 /* PXENV_UNDI_STARTUP
00108  *
00109  * Status: working
00110  */
00111 PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
00112         DBG ( "PXENV_UNDI_STARTUP\n" );
00113 
00114         undi_startup->Status = PXENV_STATUS_SUCCESS;
00115         return PXENV_EXIT_SUCCESS;
00116 }
00117 
00118 /* PXENV_UNDI_CLEANUP
00119  *
00120  * Status: working
00121  */
00122 PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) {
00123         DBG ( "PXENV_UNDI_CLEANUP\n" );
00124 
00125         pxe_netdev_close();
00126 
00127         undi_cleanup->Status = PXENV_STATUS_SUCCESS;
00128         return PXENV_EXIT_SUCCESS;
00129 }
00130 
00131 /* PXENV_UNDI_INITIALIZE
00132  *
00133  * Status: working
00134  */
00135 PXENV_EXIT_t pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE
00136                                      *undi_initialize ) {
00137         DBG ( "PXENV_UNDI_INITIALIZE protocolini %08x\n",
00138               undi_initialize->ProtocolIni );
00139 
00140         undi_initialize->Status = PXENV_STATUS_SUCCESS;
00141         return PXENV_EXIT_SUCCESS;
00142 }
00143 
00144 /* PXENV_UNDI_RESET_ADAPTER
00145  *
00146  * Status: working
00147  */
00148 PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET
00149                                         *undi_reset_adapter ) {
00150         int rc;
00151 
00152         DBG ( "PXENV_UNDI_RESET_ADAPTER" );
00153         pxe_dump_mcast_list ( &undi_reset_adapter->R_Mcast_Buf );
00154         DBG ( "\n" );
00155 
00156         pxe_netdev_close();
00157         if ( ( rc = pxe_netdev_open() ) != 0 ) {
00158                 DBG ( "PXENV_UNDI_RESET_ADAPTER could not reopen %s: %s\n",
00159                       pxe_netdev->name, strerror ( rc ) );
00160                 undi_reset_adapter->Status = PXENV_STATUS ( rc );
00161                 return PXENV_EXIT_FAILURE;
00162         }
00163 
00164         undi_reset_adapter->Status = PXENV_STATUS_SUCCESS;
00165         return PXENV_EXIT_SUCCESS;
00166 }
00167 
00168 /* PXENV_UNDI_SHUTDOWN
00169  *
00170  * Status: working
00171  */
00172 PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN
00173                                    *undi_shutdown ) {
00174         DBG ( "PXENV_UNDI_SHUTDOWN\n" );
00175 
00176         pxe_netdev_close();
00177 
00178         undi_shutdown->Status = PXENV_STATUS_SUCCESS;
00179         return PXENV_EXIT_SUCCESS;
00180 }
00181 
00182 /* PXENV_UNDI_OPEN
00183  *
00184  * Status: working
00185  */
00186 PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) {
00187         int rc;
00188 
00189         DBG ( "PXENV_UNDI_OPEN flag %04x filter %04x",
00190               undi_open->OpenFlag, undi_open->PktFilter );
00191         pxe_dump_mcast_list ( &undi_open->R_Mcast_Buf );
00192         DBG ( "\n" );
00193 
00194         if ( ( rc = pxe_netdev_open() ) != 0 ) {
00195                 DBG ( "PXENV_UNDI_OPEN could not open %s: %s\n",
00196                       pxe_netdev->name, strerror ( rc ) );
00197                 undi_open->Status = PXENV_STATUS ( rc );
00198                 return PXENV_EXIT_FAILURE;
00199         }
00200 
00201         undi_open->Status = PXENV_STATUS_SUCCESS;
00202         return PXENV_EXIT_SUCCESS;
00203 }
00204 
00205 /* PXENV_UNDI_CLOSE
00206  *
00207  * Status: working
00208  */
00209 PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) {
00210         DBG ( "PXENV_UNDI_CLOSE\n" );
00211 
00212         pxe_netdev_close();
00213 
00214         undi_close->Status = PXENV_STATUS_SUCCESS;
00215         return PXENV_EXIT_SUCCESS;
00216 }
00217 
00218 /* PXENV_UNDI_TRANSMIT
00219  *
00220  * Status: working
00221  */
00222 PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT
00223                                    *undi_transmit ) {
00224         struct s_PXENV_UNDI_TBD tbd;
00225         struct DataBlk *datablk;
00226         struct io_buffer *iobuf;
00227         struct net_protocol *net_protocol;
00228         struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
00229         char destaddr[MAX_LL_ADDR_LEN];
00230         const void *ll_dest;
00231         size_t ll_hlen = ll_protocol->ll_header_len;
00232         size_t len;
00233         unsigned int i;
00234         int rc;
00235 
00236         DBG2 ( "PXENV_UNDI_TRANSMIT" );
00237 
00238         /* Forcibly enable interrupts at this point, to work around
00239          * callers that never call PXENV_UNDI_OPEN before attempting
00240          * to use the UNDI API.
00241          */
00242         netdev_irq ( pxe_netdev, 1 );
00243 
00244         /* Identify network-layer protocol */
00245         switch ( undi_transmit->Protocol ) {
00246         case P_IP:      net_protocol = &ipv4_protocol;  break;
00247         case P_ARP:     net_protocol = &arp_protocol;   break;
00248         case P_RARP:    net_protocol = &rarp_protocol;  break;
00249         case P_UNKNOWN:
00250                 net_protocol = NULL;
00251                 ll_hlen = 0;
00252                 break;
00253         default:
00254                 DBG2 ( " %02x invalid protocol\n", undi_transmit->Protocol );
00255                 undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
00256                 return PXENV_EXIT_FAILURE;
00257         }
00258         DBG2 ( " %s", ( net_protocol ? net_protocol->name : "RAW" ) );
00259 
00260         /* Calculate total packet length */
00261         copy_from_real ( &tbd, undi_transmit->TBD.segment,
00262                          undi_transmit->TBD.offset, sizeof ( tbd ) );
00263         len = tbd.ImmedLength;
00264         DBG2 ( " %04x:%04x+%x", tbd.Xmit.segment, tbd.Xmit.offset,
00265                tbd.ImmedLength );
00266         for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
00267                 datablk = &tbd.DataBlock[i];
00268                 len += datablk->TDDataLen;
00269                 DBG2 ( " %04x:%04x+%x", datablk->TDDataPtr.segment,
00270                        datablk->TDDataPtr.offset, datablk->TDDataLen );
00271         }
00272 
00273         /* Allocate and fill I/O buffer */
00274         iobuf = alloc_iob ( ll_hlen + len );
00275         if ( ! iobuf ) {
00276                 DBG2 ( " could not allocate iobuf\n" );
00277                 undi_transmit->Status = PXENV_STATUS_OUT_OF_RESOURCES;
00278                 return PXENV_EXIT_FAILURE;
00279         }
00280         iob_reserve ( iobuf, ll_hlen );
00281         copy_from_real ( iob_put ( iobuf, tbd.ImmedLength ), tbd.Xmit.segment,
00282                          tbd.Xmit.offset, tbd.ImmedLength );
00283         for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
00284                 datablk = &tbd.DataBlock[i];
00285                 copy_from_real ( iob_put ( iobuf, datablk->TDDataLen ),
00286                                  datablk->TDDataPtr.segment,
00287                                  datablk->TDDataPtr.offset,
00288                                  datablk->TDDataLen );
00289         }
00290 
00291         /* Add link-layer header, if required to do so */
00292         if ( net_protocol != NULL ) {
00293 
00294                 /* Calculate destination address */
00295                 if ( undi_transmit->XmitFlag == XMT_DESTADDR ) {
00296                         copy_from_real ( destaddr,
00297                                          undi_transmit->DestAddr.segment,
00298                                          undi_transmit->DestAddr.offset,
00299                                          ll_protocol->ll_addr_len );
00300                         ll_dest = destaddr;
00301                         DBG2 ( " DEST %s", ll_protocol->ntoa ( ll_dest ) );
00302                 } else {
00303                         ll_dest = pxe_netdev->ll_broadcast;
00304                         DBG2 ( " BCAST" );
00305                 }
00306 
00307                 /* Add link-layer header */
00308                 if ( ( rc = ll_protocol->push ( pxe_netdev, iobuf, ll_dest,
00309                                                 pxe_netdev->ll_addr,
00310                                                 net_protocol->net_proto ))!=0){
00311                         DBG2 ( " could not add link-layer header: %s\n",
00312                                strerror ( rc ) );
00313                         free_iob ( iobuf );
00314                         undi_transmit->Status = PXENV_STATUS ( rc );
00315                         return PXENV_EXIT_FAILURE;
00316                 }
00317         }
00318 
00319         /* Flag transmission as in-progress.  Do this before starting
00320          * to transmit the packet, because the ISR may trigger before
00321          * we return from netdev_tx().
00322          */
00323         undi_tx_count++;
00324 
00325         /* Transmit packet */
00326         DBG2 ( "\n" );
00327         if ( ( rc = netdev_tx ( pxe_netdev, iobuf ) ) != 0 ) {
00328                 DBG2 ( "PXENV_UNDI_TRANSMIT could not transmit: %s\n",
00329                        strerror ( rc ) );
00330                 undi_tx_count--;
00331                 undi_transmit->Status = PXENV_STATUS ( rc );
00332                 return PXENV_EXIT_FAILURE;
00333         }
00334 
00335         undi_transmit->Status = PXENV_STATUS_SUCCESS;
00336         return PXENV_EXIT_SUCCESS;
00337 }
00338 
00339 /* PXENV_UNDI_SET_MCAST_ADDRESS
00340  *
00341  * Status: working (for NICs that support receive-all-multicast)
00342  */
00343 PXENV_EXIT_t
00344 pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS
00345                                *undi_set_mcast_address ) {
00346         DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" );
00347         pxe_dump_mcast_list ( &undi_set_mcast_address->R_Mcast_Buf );
00348         DBG ( "\n" );
00349 
00350         undi_set_mcast_address->Status = PXENV_STATUS_SUCCESS;
00351         return PXENV_EXIT_SUCCESS;
00352 }
00353 
00354 /* PXENV_UNDI_SET_STATION_ADDRESS
00355  *
00356  * Status: working
00357  */
00358 PXENV_EXIT_t 
00359 pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS
00360                                  *undi_set_station_address ) {
00361         struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
00362 
00363         DBG ( "PXENV_UNDI_SET_STATION_ADDRESS %s",
00364               ll_protocol->ntoa ( undi_set_station_address->StationAddress ) );
00365 
00366         /* If adapter is open, the change will have no effect; return
00367          * an error
00368          */
00369         if ( netdev_is_open ( pxe_netdev ) ) {
00370                 DBG ( " failed: netdev is open\n" );
00371                 undi_set_station_address->Status =
00372                         PXENV_STATUS_UNDI_INVALID_STATE;
00373                 return PXENV_EXIT_FAILURE;
00374         }
00375 
00376         /* Update MAC address */
00377         memcpy ( pxe_netdev->ll_addr,
00378                  &undi_set_station_address->StationAddress,
00379                  ll_protocol->ll_addr_len );
00380 
00381         DBG ( "\n" );
00382         undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
00383         return PXENV_EXIT_SUCCESS;
00384 }
00385 
00386 /* PXENV_UNDI_SET_PACKET_FILTER
00387  *
00388  * Status: won't implement (would require driver API changes for no
00389  * real benefit)
00390  */
00391 PXENV_EXIT_t
00392 pxenv_undi_set_packet_filter ( struct s_PXENV_UNDI_SET_PACKET_FILTER
00393                                *undi_set_packet_filter ) {
00394 
00395         DBG ( "PXENV_UNDI_SET_PACKET_FILTER %02x\n",
00396               undi_set_packet_filter->filter );
00397 
00398         /* Pretend that we succeeded, otherwise the 3Com DOS UNDI
00399          * driver refuses to load.  (We ignore the filter value in the
00400          * PXENV_UNDI_OPEN call anyway.)
00401          */
00402         undi_set_packet_filter->Status = PXENV_STATUS_SUCCESS;
00403 
00404         return PXENV_EXIT_SUCCESS;
00405 }
00406 
00407 /* PXENV_UNDI_GET_INFORMATION
00408  *
00409  * Status: working
00410  */
00411 PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
00412                                           *undi_get_information ) {
00413         struct device *dev = pxe_netdev->dev;
00414         struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
00415         size_t ll_addr_len = ll_protocol->ll_addr_len;
00416 
00417         DBG ( "PXENV_UNDI_GET_INFORMATION" );
00418 
00419         undi_get_information->BaseIo = dev->desc.ioaddr;
00420         undi_get_information->IntNumber = dev->desc.irq;
00421         /* Cheat: assume all cards can cope with this */
00422         undi_get_information->MaxTranUnit = ETH_MAX_MTU;
00423         undi_get_information->HwType = ntohs ( ll_protocol->ll_proto );
00424         undi_get_information->HwAddrLen = ll_addr_len;
00425         assert ( ll_addr_len <=
00426                  sizeof ( undi_get_information->CurrentNodeAddress ) );
00427         memcpy ( &undi_get_information->CurrentNodeAddress,
00428                  pxe_netdev->ll_addr,
00429                  sizeof ( undi_get_information->CurrentNodeAddress ) );
00430         ll_protocol->init_addr ( pxe_netdev->hw_addr,
00431                                  &undi_get_information->PermNodeAddress );
00432         undi_get_information->ROMAddress = 0;
00433                 /* nic.rom_info->rom_segment; */
00434         /* We only provide the ability to receive or transmit a single
00435          * packet at a time.  This is a bootloader, not an OS.
00436          */
00437         undi_get_information->RxBufCt = 1;
00438         undi_get_information->TxBufCt = 1;
00439 
00440         DBG ( " io %04x irq %d mtu %d %s %s\n",
00441               undi_get_information->BaseIo, undi_get_information->IntNumber,
00442               undi_get_information->MaxTranUnit, ll_protocol->name,
00443               ll_protocol->ntoa ( &undi_get_information->CurrentNodeAddress ));
00444         undi_get_information->Status = PXENV_STATUS_SUCCESS;
00445         return PXENV_EXIT_SUCCESS;
00446 }
00447 
00448 /* PXENV_UNDI_GET_STATISTICS
00449  *
00450  * Status: working
00451  */
00452 PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
00453                                          *undi_get_statistics ) {
00454         DBG ( "PXENV_UNDI_GET_STATISTICS" );
00455 
00456         undi_get_statistics->XmtGoodFrames = pxe_netdev->tx_stats.good;
00457         undi_get_statistics->RcvGoodFrames = pxe_netdev->rx_stats.good;
00458         undi_get_statistics->RcvCRCErrors = pxe_netdev->rx_stats.bad;
00459         undi_get_statistics->RcvResourceErrors = pxe_netdev->rx_stats.bad;
00460 
00461         DBG ( " txok %d rxok %d rxcrc %d rxrsrc %d\n",
00462               undi_get_statistics->XmtGoodFrames,
00463               undi_get_statistics->RcvGoodFrames,
00464               undi_get_statistics->RcvCRCErrors,
00465               undi_get_statistics->RcvResourceErrors );
00466         undi_get_statistics->Status = PXENV_STATUS_SUCCESS;
00467         return PXENV_EXIT_SUCCESS;
00468 }
00469 
00470 /* PXENV_UNDI_CLEAR_STATISTICS
00471  *
00472  * Status: working
00473  */
00474 PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
00475                                            *undi_clear_statistics ) {
00476         DBG ( "PXENV_UNDI_CLEAR_STATISTICS\n" );
00477 
00478         memset ( &pxe_netdev->tx_stats, 0, sizeof ( pxe_netdev->tx_stats ) );
00479         memset ( &pxe_netdev->rx_stats, 0, sizeof ( pxe_netdev->rx_stats ) );
00480 
00481         undi_clear_statistics->Status = PXENV_STATUS_SUCCESS;
00482         return PXENV_EXIT_SUCCESS;
00483 }
00484 
00485 /* PXENV_UNDI_INITIATE_DIAGS
00486  *
00487  * Status: won't implement (would require driver API changes for no
00488  * real benefit)
00489  */
00490 PXENV_EXIT_t pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
00491                                          *undi_initiate_diags ) {
00492         DBG ( "PXENV_UNDI_INITIATE_DIAGS failed: unsupported\n" );
00493 
00494         undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
00495         return PXENV_EXIT_FAILURE;
00496 }
00497 
00498 /* PXENV_UNDI_FORCE_INTERRUPT
00499  *
00500  * Status: won't implement (would require driver API changes for no
00501  * perceptible benefit)
00502  */
00503 PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
00504                                           *undi_force_interrupt ) {
00505         DBG ( "PXENV_UNDI_FORCE_INTERRUPT failed: unsupported\n" );
00506 
00507         undi_force_interrupt->Status = PXENV_STATUS_UNSUPPORTED;
00508         return PXENV_EXIT_FAILURE;
00509 }
00510 
00511 /* PXENV_UNDI_GET_MCAST_ADDRESS
00512  *
00513  * Status: working
00514  */
00515 PXENV_EXIT_t
00516 pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS
00517                                *undi_get_mcast_address ) {
00518         struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
00519         struct in_addr ip = { .s_addr = undi_get_mcast_address->InetAddr };
00520         int rc;
00521 
00522         DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS %s", inet_ntoa ( ip ) );
00523 
00524         if ( ( rc = ll_protocol->mc_hash ( AF_INET, &ip,
00525                                       undi_get_mcast_address->MediaAddr ))!=0){
00526                 DBG ( " failed: %s\n", strerror ( rc ) );
00527                 undi_get_mcast_address->Status = PXENV_STATUS ( rc );
00528                 return PXENV_EXIT_FAILURE;
00529         }
00530         DBG ( "=>%s\n",
00531               ll_protocol->ntoa ( undi_get_mcast_address->MediaAddr ) );
00532 
00533         undi_get_mcast_address->Status = PXENV_STATUS_SUCCESS;
00534         return PXENV_EXIT_SUCCESS;
00535 }
00536 
00537 /* PXENV_UNDI_GET_NIC_TYPE
00538  *
00539  * Status: working
00540  */
00541 PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE
00542                                        *undi_get_nic_type ) {
00543         struct device *dev = pxe_netdev->dev;
00544 
00545         DBG ( "PXENV_UNDI_GET_NIC_TYPE" );
00546 
00547         memset ( &undi_get_nic_type->info, 0,
00548                  sizeof ( undi_get_nic_type->info ) );
00549 
00550         switch ( dev->desc.bus_type ) {
00551         case BUS_TYPE_PCI: {
00552                 struct pci_nic_info *info = &undi_get_nic_type->info.pci;
00553 
00554                 undi_get_nic_type->NicType = PCI_NIC;
00555                 info->Vendor_ID = dev->desc.vendor;
00556                 info->Dev_ID = dev->desc.device;
00557                 info->Base_Class = PCI_BASE_CLASS ( dev->desc.class );
00558                 info->Sub_Class = PCI_SUB_CLASS ( dev->desc.class );
00559                 info->Prog_Intf = PCI_PROG_INTF ( dev->desc.class );
00560                 info->BusDevFunc = dev->desc.location;
00561                 /* Cheat: remaining fields are probably unnecessary,
00562                  * and would require adding extra code to pci.c.
00563                  */
00564                 undi_get_nic_type->info.pci.SubVendor_ID = 0xffff;
00565                 undi_get_nic_type->info.pci.SubDevice_ID = 0xffff;
00566                 DBG ( " PCI %02x:%02x.%x %04x:%04x (%04x:%04x) %02x%02x%02x "
00567                       "rev %02x\n", PCI_BUS ( info->BusDevFunc ),
00568                       PCI_SLOT ( info->BusDevFunc ),
00569                       PCI_FUNC ( info->BusDevFunc ), info->Vendor_ID,
00570                       info->Dev_ID, info->SubVendor_ID, info->SubDevice_ID,
00571                       info->Base_Class, info->Sub_Class, info->Prog_Intf,
00572                       info->Rev );
00573                 break; }
00574         case BUS_TYPE_ISAPNP: {
00575                 struct pnp_nic_info *info = &undi_get_nic_type->info.pnp;
00576 
00577                 undi_get_nic_type->NicType = PnP_NIC;
00578                 info->EISA_Dev_ID = ( ( dev->desc.vendor << 16 ) |
00579                                       dev->desc.device );
00580                 info->CardSelNum = dev->desc.location;
00581                 /* Cheat: remaining fields are probably unnecessary,
00582                  * and would require adding extra code to isapnp.c.
00583                  */
00584                 DBG ( " ISAPnP CSN %04x %08x %02x%02x%02x\n",
00585                       info->CardSelNum, info->EISA_Dev_ID,
00586                       info->Base_Class, info->Sub_Class, info->Prog_Intf );
00587                 break; }
00588         default:
00589                 DBG ( " failed: unknown bus type\n" );
00590                 undi_get_nic_type->Status = PXENV_STATUS_FAILURE;
00591                 return PXENV_EXIT_FAILURE;
00592         }
00593 
00594         undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
00595         return PXENV_EXIT_SUCCESS;
00596 }
00597 
00598 /* PXENV_UNDI_GET_IFACE_INFO
00599  *
00600  * Status: working
00601  */
00602 PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO
00603                                          *undi_get_iface_info ) {
00604         DBG ( "PXENV_UNDI_GET_IFACE_INFO" );
00605 
00606         /* Just hand back some info, doesn't really matter what it is.
00607          * Most PXE stacks seem to take this approach.
00608          */
00609         snprintf ( ( char * ) undi_get_iface_info->IfaceType,
00610                    sizeof ( undi_get_iface_info->IfaceType ), "DIX+802.3" );
00611         undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
00612         undi_get_iface_info->ServiceFlags =
00613                 ( SUPPORTED_BROADCAST | SUPPORTED_MULTICAST |
00614                   SUPPORTED_SET_STATION_ADDRESS | SUPPORTED_RESET |
00615                   SUPPORTED_OPEN_CLOSE | SUPPORTED_IRQ );
00616         memset ( undi_get_iface_info->Reserved, 0,
00617                  sizeof(undi_get_iface_info->Reserved) );
00618 
00619         DBG ( " %s %dbps flags %08x\n", undi_get_iface_info->IfaceType,
00620               undi_get_iface_info->LinkSpeed,
00621               undi_get_iface_info->ServiceFlags );
00622         undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
00623         return PXENV_EXIT_SUCCESS;
00624 }
00625 
00626 /* PXENV_UNDI_GET_STATE
00627  *
00628  * Status: impossible
00629  */
00630 PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE
00631                                     *undi_get_state ) {
00632         DBG ( "PXENV_UNDI_GET_STATE failed: unsupported\n" );
00633 
00634         undi_get_state->Status = PXENV_STATUS_UNSUPPORTED;
00635         return PXENV_EXIT_FAILURE;
00636 };
00637 
00638 /* PXENV_UNDI_ISR
00639  *
00640  * Status: working
00641  */
00642 PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
00643         struct io_buffer *iobuf;
00644         size_t len;
00645         struct ll_protocol *ll_protocol;
00646         const void *ll_dest;
00647         const void *ll_source;
00648         uint16_t net_proto;
00649         size_t ll_hlen;
00650         struct net_protocol *net_protocol;
00651         unsigned int prottype;
00652         int rc;
00653 
00654         /* Use coloured debug, since UNDI ISR messages are likely to
00655          * be interspersed amongst other UNDI messages.
00656          */
00657         DBGC2 ( &pxenv_undi_isr, "PXENV_UNDI_ISR" );
00658 
00659         /* Just in case some idiot actually looks at these fields when
00660          * we weren't meant to fill them in...
00661          */
00662         undi_isr->BufferLength = 0;
00663         undi_isr->FrameLength = 0;
00664         undi_isr->FrameHeaderLength = 0;
00665         undi_isr->ProtType = 0;
00666         undi_isr->PktType = 0;
00667 
00668         switch ( undi_isr->FuncFlag ) {
00669         case PXENV_UNDI_ISR_IN_START :
00670                 DBGC2 ( &pxenv_undi_isr, " START" );
00671 
00672                 /* Call poll().  This should acknowledge the device
00673                  * interrupt and queue up any received packet.
00674                  */
00675                 netdev_poll ( pxe_netdev );
00676 
00677                 /* A 100% accurate determination of "OURS" vs "NOT
00678                  * OURS" is difficult to achieve without invasive and
00679                  * unpleasant changes to the driver model.  We settle
00680                  * for always returning "OURS" if interrupts are
00681                  * currently enabled.
00682                  *
00683                  * Returning "NOT OURS" when interrupts are disabled
00684                  * allows us to avoid a potential interrupt storm when
00685                  * we are on a shared interrupt line; if we were to
00686                  * always return "OURS" then the other device's ISR
00687                  * may never be called.
00688                  */
00689                 if ( netdev_irq_enabled ( pxe_netdev ) ) {
00690                         DBGC2 ( &pxenv_undi_isr, " OURS" );
00691                         undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
00692                 } else {
00693                         DBGC2 ( &pxenv_undi_isr, " NOT OURS" );
00694                         undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_NOT_OURS;
00695                 }
00696 
00697                 /* Disable interrupts */
00698                 netdev_irq ( pxe_netdev, 0 );
00699 
00700                 break;
00701         case PXENV_UNDI_ISR_IN_PROCESS :
00702         case PXENV_UNDI_ISR_IN_GET_NEXT :
00703                 DBGC2 ( &pxenv_undi_isr, " %s",
00704                         ( ( undi_isr->FuncFlag == PXENV_UNDI_ISR_IN_PROCESS ) ?
00705                           "PROCESS" : "GET_NEXT" ) );
00706 
00707                 /* Some dumb NBPs (e.g. emBoot's winBoot/i) never call
00708                  * PXENV_UNDI_ISR with FuncFlag=PXENV_UNDI_ISR_START;
00709                  * they just sit in a tight polling loop merrily
00710                  * violating the PXE spec with repeated calls to
00711                  * PXENV_UNDI_ISR_IN_PROCESS.  Force extra polls to
00712                  * cope with these out-of-spec clients.
00713                  */
00714                 netdev_poll ( pxe_netdev );
00715 
00716                 /* If we have not yet marked a TX as complete, and the
00717                  * netdev TX queue is empty, report the TX completion.
00718                  */
00719                 if ( undi_tx_count && list_empty ( &pxe_netdev->tx_queue ) ) {
00720                         DBGC2 ( &pxenv_undi_isr, " TXC" );
00721                         undi_tx_count--;
00722                         undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_TRANSMIT;
00723                         break;
00724                 }
00725 
00726                 /* Remove first packet from netdev RX queue */
00727                 iobuf = netdev_rx_dequeue ( pxe_netdev );
00728                 if ( ! iobuf ) {
00729                         DBGC2 ( &pxenv_undi_isr, " DONE" );
00730                         /* No more packets remaining */
00731                         undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
00732                         /* Re-enable interrupts */
00733                         netdev_irq ( pxe_netdev, 1 );
00734                         break;
00735                 }
00736 
00737                 /* Copy packet to base memory buffer */
00738                 len = iob_len ( iobuf );
00739                 DBGC2 ( &pxenv_undi_isr, " RX" );
00740                 if ( len > sizeof ( basemem_packet ) ) {
00741                         /* Should never happen */
00742                         DBGC2 ( &pxenv_undi_isr, " overlength (%zx)", len );
00743                         len = sizeof ( basemem_packet );
00744                 }
00745                 memcpy ( basemem_packet, iobuf->data, len );
00746 
00747                 /* Strip link-layer header */
00748                 ll_protocol = pxe_netdev->ll_protocol;
00749                 if ( ( rc = ll_protocol->pull ( pxe_netdev, iobuf, &ll_dest,
00750                                                 &ll_source, &net_proto )) !=0){
00751                         /* Assume unknown net_proto and no ll_source */
00752                         net_proto = 0;
00753                         ll_source = NULL;
00754                 }
00755                 ll_hlen = ( len - iob_len ( iobuf ) );
00756 
00757                 /* Determine network-layer protocol */
00758                 switch ( net_proto ) {
00759                 case htons ( ETH_P_IP ):
00760                         net_protocol = &ipv4_protocol;
00761                         prottype = P_IP;
00762                         break;
00763                 case htons ( ETH_P_ARP ):
00764                         net_protocol = &arp_protocol;
00765                         prottype = P_ARP;
00766                         break;
00767                 case htons ( ETH_P_RARP ):
00768                         net_protocol = &rarp_protocol;
00769                         prottype = P_RARP;
00770                         break;
00771                 default:
00772                         net_protocol = NULL;
00773                         prottype = P_UNKNOWN;
00774                         break;
00775                 }
00776 
00777                 /* Fill in UNDI_ISR structure */
00778                 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
00779                 undi_isr->BufferLength = len;
00780                 undi_isr->FrameLength = len;
00781                 undi_isr->FrameHeaderLength = ll_hlen;
00782                 undi_isr->Frame.segment = rm_ds;
00783                 undi_isr->Frame.offset = __from_data16 ( basemem_packet );
00784                 undi_isr->ProtType = prottype;
00785                 undi_isr->PktType = XMT_DESTADDR;
00786                 DBGC2 ( &pxenv_undi_isr, " %04x:%04x+%x(%x) %s hlen %d",
00787                         undi_isr->Frame.segment, undi_isr->Frame.offset,
00788                         undi_isr->BufferLength, undi_isr->FrameLength,
00789                         ( net_protocol ? net_protocol->name : "RAW" ),
00790                         undi_isr->FrameHeaderLength );
00791 
00792                 /* Free packet */
00793                 free_iob ( iobuf );
00794                 break;
00795         default :
00796                 DBGC2 ( &pxenv_undi_isr, " INVALID(%04x)\n",
00797                         undi_isr->FuncFlag );
00798 
00799                 /* Should never happen */
00800                 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
00801                 undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
00802                 return PXENV_EXIT_FAILURE;
00803         }
00804 
00805         DBGC2 ( &pxenv_undi_isr, "\n" );
00806         undi_isr->Status = PXENV_STATUS_SUCCESS;
00807         return PXENV_EXIT_SUCCESS;
00808 }

Generated on Tue Apr 6 20:00:51 2010 for gPXE by  doxygen 1.5.7.1