undinet.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017  */
00018 
00019 FILE_LICENCE ( GPL2_OR_LATER );
00020 
00021 #include <string.h>
00022 #include <pxe.h>
00023 #include <realmode.h>
00024 #include <pic8259.h>
00025 #include <biosint.h>
00026 #include <pnpbios.h>
00027 #include <basemem_packet.h>
00028 #include <gpxe/io.h>
00029 #include <gpxe/iobuf.h>
00030 #include <gpxe/netdevice.h>
00031 #include <gpxe/if_ether.h>
00032 #include <gpxe/ethernet.h>
00033 #include <undi.h>
00034 #include <undinet.h>
00035 #include <pxeparent.h>
00036 
00037 
00038 /** @file
00039  *
00040  * UNDI network device driver
00041  *
00042  */
00043 
00044 /** An UNDI NIC */
00045 struct undi_nic {
00046         /** Assigned IRQ number */
00047         unsigned int irq;
00048         /** Currently processing ISR */
00049         int isr_processing;
00050         /** Bug workarounds */
00051         int hacks;
00052 };
00053 
00054 /**
00055  * @defgroup undi_hacks UNDI workarounds
00056  * @{
00057  */
00058 
00059 /** Work around Etherboot 5.4 bugs */
00060 #define UNDI_HACK_EB54          0x0001
00061 
00062 /** @} */
00063 
00064 static void undinet_close ( struct net_device *netdev );
00065 
00066 /** Address of UNDI entry point */
00067 static SEGOFF16_t undinet_entry;
00068 
00069 /*****************************************************************************
00070  *
00071  * UNDI interrupt service routine
00072  *
00073  *****************************************************************************
00074  */
00075 
00076 /**
00077  * UNDI interrupt service routine
00078  *
00079  * The UNDI ISR increments a counter (@c trigger_count) and exits.
00080  */
00081 extern void undiisr ( void );
00082 
00083 /** IRQ number */
00084 uint8_t __data16 ( undiisr_irq );
00085 #define undiisr_irq __use_data16 ( undiisr_irq )
00086 
00087 /** IRQ chain vector */
00088 struct segoff __data16 ( undiisr_next_handler );
00089 #define undiisr_next_handler __use_data16 ( undiisr_next_handler )
00090 
00091 /** IRQ trigger count */
00092 volatile uint8_t __data16 ( undiisr_trigger_count ) = 0;
00093 #define undiisr_trigger_count __use_data16 ( undiisr_trigger_count )
00094 
00095 /** Last observed trigger count */
00096 static unsigned int last_trigger_count = 0;
00097 
00098 /**
00099  * Hook UNDI interrupt service routine
00100  *
00101  * @v irq               IRQ number
00102  */
00103 static void undinet_hook_isr ( unsigned int irq ) {
00104 
00105         assert ( irq <= IRQ_MAX );
00106         assert ( undiisr_irq == 0 );
00107 
00108         undiisr_irq = irq;
00109         hook_bios_interrupt ( IRQ_INT ( irq ),
00110                               ( ( unsigned int ) undiisr ),
00111                               &undiisr_next_handler );
00112 }
00113 
00114 /**
00115  * Unhook UNDI interrupt service routine
00116  *
00117  * @v irq               IRQ number
00118  */
00119 static void undinet_unhook_isr ( unsigned int irq ) {
00120 
00121         assert ( irq <= IRQ_MAX );
00122 
00123         unhook_bios_interrupt ( IRQ_INT ( irq ),
00124                                 ( ( unsigned int ) undiisr ),
00125                                 &undiisr_next_handler );
00126         undiisr_irq = 0;
00127 }
00128 
00129 /**
00130  * Test to see if UNDI ISR has been triggered
00131  *
00132  * @ret triggered       ISR has been triggered since last check
00133  */
00134 static int undinet_isr_triggered ( void ) {
00135         unsigned int this_trigger_count;
00136 
00137         /* Read trigger_count.  Do this only once; it is volatile */
00138         this_trigger_count = undiisr_trigger_count;
00139 
00140         if ( this_trigger_count == last_trigger_count ) {
00141                 /* Not triggered */
00142                 return 0;
00143         } else {
00144                 /* Triggered */
00145                 last_trigger_count = this_trigger_count;
00146                 return 1;
00147         }
00148 }
00149 
00150 /*****************************************************************************
00151  *
00152  * UNDI network device interface
00153  *
00154  *****************************************************************************
00155  */
00156 
00157 /** UNDI transmit buffer descriptor */
00158 static struct s_PXENV_UNDI_TBD __data16 ( undinet_tbd );
00159 #define undinet_tbd __use_data16 ( undinet_tbd )
00160 
00161 /**
00162  * Transmit packet
00163  *
00164  * @v netdev            Network device
00165  * @v iobuf             I/O buffer
00166  * @ret rc              Return status code
00167  */
00168 static int undinet_transmit ( struct net_device *netdev,
00169                               struct io_buffer *iobuf ) {
00170         struct s_PXENV_UNDI_TRANSMIT undi_transmit;
00171         size_t len = iob_len ( iobuf );
00172         int rc;
00173 
00174         /* Technically, we ought to make sure that the previous
00175          * transmission has completed before we re-use the buffer.
00176          * However, many PXE stacks (including at least some Intel PXE
00177          * stacks and Etherboot 5.4) fail to generate TX completions.
00178          * In practice this won't be a problem, since our TX datapath
00179          * has a very low packet volume and we can get away with
00180          * assuming that a TX will be complete by the time we want to
00181          * transmit the next packet.
00182          */
00183 
00184         /* Copy packet to UNDI I/O buffer */
00185         if ( len > sizeof ( basemem_packet ) )
00186                 len = sizeof ( basemem_packet );
00187         memcpy ( &basemem_packet, iobuf->data, len );
00188 
00189         /* Create PXENV_UNDI_TRANSMIT data structure */
00190         memset ( &undi_transmit, 0, sizeof ( undi_transmit ) );
00191         undi_transmit.DestAddr.segment = rm_ds;
00192         undi_transmit.DestAddr.offset = __from_data16 ( &undinet_tbd );
00193         undi_transmit.TBD.segment = rm_ds;
00194         undi_transmit.TBD.offset = __from_data16 ( &undinet_tbd );
00195 
00196         /* Create PXENV_UNDI_TBD data structure */
00197         undinet_tbd.ImmedLength = len;
00198         undinet_tbd.Xmit.segment = rm_ds;
00199         undinet_tbd.Xmit.offset = __from_data16 ( basemem_packet );
00200 
00201         /* Issue PXE API call */
00202         if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_TRANSMIT,
00203                                      &undi_transmit,
00204                                      sizeof ( undi_transmit ) ) ) != 0 )
00205                 goto done;
00206 
00207         /* Free I/O buffer */
00208         netdev_tx_complete ( netdev, iobuf );
00209 
00210  done:
00211         return rc;
00212 }
00213 
00214 /** 
00215  * Poll for received packets
00216  *
00217  * @v netdev            Network device
00218  *
00219  * Fun, fun, fun.  UNDI drivers don't use polling; they use
00220  * interrupts.  We therefore cheat and pretend that an interrupt has
00221  * occurred every time undinet_poll() is called.  This isn't too much
00222  * of a hack; PCI devices share IRQs and so the first thing that a
00223  * proper ISR should do is call PXENV_UNDI_ISR to determine whether or
00224  * not the UNDI NIC generated the interrupt; there is no harm done by
00225  * spurious calls to PXENV_UNDI_ISR.  Similarly, we wouldn't be
00226  * handling them any more rapidly than the usual rate of
00227  * undinet_poll() being called even if we did implement a full ISR.
00228  * So it should work.  Ha!
00229  *
00230  * Addendum (21/10/03).  Some cards don't play nicely with this trick,
00231  * so instead of doing it the easy way we have to go to all the hassle
00232  * of installing a genuine interrupt service routine and dealing with
00233  * the wonderful 8259 Programmable Interrupt Controller.  Joy.
00234  *
00235  * Addendum (10/07/07).  When doing things such as iSCSI boot, in
00236  * which we have to co-operate with a running OS, we can't get away
00237  * with the "ISR-just-increments-a-counter-and-returns" trick at all,
00238  * because it involves tying up the PIC for far too long, and other
00239  * interrupt-dependent components (e.g. local disks) start breaking.
00240  * We therefore implement a "proper" ISR which calls PXENV_UNDI_ISR
00241  * from within interrupt context in order to deassert the device
00242  * interrupt, and sends EOI if applicable.
00243  */
00244 static void undinet_poll ( struct net_device *netdev ) {
00245         struct undi_nic *undinic = netdev->priv;
00246         struct s_PXENV_UNDI_ISR undi_isr;
00247         struct io_buffer *iobuf = NULL;
00248         size_t len;
00249         size_t frag_len;
00250         size_t max_frag_len;
00251         int rc;
00252 
00253         if ( ! undinic->isr_processing ) {
00254                 /* Do nothing unless ISR has been triggered */
00255                 if ( ! undinet_isr_triggered() ) {
00256                         /* Allow interrupt to occur */
00257                         __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
00258                                                            "nop\n\t"
00259                                                            "nop\n\t"
00260                                                            "cli\n\t" ) : : );
00261                         return;
00262                 }
00263 
00264                 /* Start ISR processing */
00265                 undinic->isr_processing = 1;
00266                 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;
00267         } else {
00268                 /* Continue ISR processing */
00269                 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
00270         }
00271 
00272         /* Run through the ISR loop */
00273         while ( 1 ) {
00274                 if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_ISR,
00275                                              &undi_isr,
00276                                              sizeof ( undi_isr ) ) ) != 0 )
00277                         break;
00278                 switch ( undi_isr.FuncFlag ) {
00279                 case PXENV_UNDI_ISR_OUT_TRANSMIT:
00280                         /* We don't care about transmit completions */
00281                         break;
00282                 case PXENV_UNDI_ISR_OUT_RECEIVE:
00283                         /* Packet fragment received */
00284                         len = undi_isr.FrameLength;
00285                         frag_len = undi_isr.BufferLength;
00286                         if ( ( len == 0 ) || ( len < frag_len ) ) {
00287                                 /* Don't laugh.  VMWare does it. */
00288                                 DBGC ( undinic, "UNDINIC %p reported insane "
00289                                        "fragment (%zd of %zd bytes)\n",
00290                                        undinic, frag_len, len );
00291                                 netdev_rx_err ( netdev, NULL, -EINVAL );
00292                                 break;
00293                         }
00294                         if ( ! iobuf )
00295                                 iobuf = alloc_iob ( len );
00296                         if ( ! iobuf ) {
00297                                 DBGC ( undinic, "UNDINIC %p could not "
00298                                        "allocate %zd bytes for RX buffer\n",
00299                                        undinic, len );
00300                                 /* Fragment will be dropped */
00301                                 netdev_rx_err ( netdev, NULL, -ENOMEM );
00302                                 goto done;
00303                         }
00304                         max_frag_len = iob_tailroom ( iobuf );
00305                         if ( frag_len > max_frag_len ) {
00306                                 DBGC ( undinic, "UNDINIC %p fragment too big "
00307                                        "(%zd+%zd does not fit into %zd)\n",
00308                                        undinic, iob_len ( iobuf ), frag_len,
00309                                        ( iob_len ( iobuf ) + max_frag_len ) );
00310                                 frag_len = max_frag_len;
00311                         }
00312                         copy_from_real ( iob_put ( iobuf, frag_len ),
00313                                          undi_isr.Frame.segment,
00314                                          undi_isr.Frame.offset, frag_len );
00315                         if ( iob_len ( iobuf ) == len ) {
00316                                 /* Whole packet received; deliver it */
00317                                 netdev_rx ( netdev, iob_disown ( iobuf ) );
00318                                 /* Etherboot 5.4 fails to return all packets
00319                                  * under mild load; pretend it retriggered.
00320                                  */
00321                                 if ( undinic->hacks & UNDI_HACK_EB54 )
00322                                         --last_trigger_count;
00323                         }
00324                         break;
00325                 case PXENV_UNDI_ISR_OUT_DONE:
00326                         /* Processing complete */
00327                         undinic->isr_processing = 0;
00328                         goto done;
00329                 default:
00330                         /* Should never happen.  VMWare does it routinely. */
00331                         DBGC ( undinic, "UNDINIC %p ISR returned invalid "
00332                                "FuncFlag %04x\n", undinic, undi_isr.FuncFlag );
00333                         undinic->isr_processing = 0;
00334                         goto done;
00335                 }
00336                 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
00337         }
00338 
00339  done:
00340         if ( iobuf ) {
00341                 DBGC ( undinic, "UNDINIC %p returned incomplete packet "
00342                        "(%zd of %zd)\n", undinic, iob_len ( iobuf ),
00343                        ( iob_len ( iobuf ) + iob_tailroom ( iobuf ) ) );
00344                 netdev_rx_err ( netdev, iobuf, -EINVAL );
00345         }
00346 }
00347 
00348 /**
00349  * Open NIC
00350  *
00351  * @v netdev            Net device
00352  * @ret rc              Return status code
00353  */
00354 static int undinet_open ( struct net_device *netdev ) {
00355         struct undi_nic *undinic = netdev->priv;
00356         struct s_PXENV_UNDI_SET_STATION_ADDRESS undi_set_address;
00357         struct s_PXENV_UNDI_OPEN undi_open;
00358         int rc;
00359 
00360         /* Hook interrupt service routine and enable interrupt */
00361         undinet_hook_isr ( undinic->irq );
00362         enable_irq ( undinic->irq );
00363         send_eoi ( undinic->irq );
00364 
00365         /* Set station address.  Required for some PXE stacks; will
00366          * spuriously fail on others.  Ignore failures.  We only ever
00367          * use it to set the MAC address to the card's permanent value
00368          * anyway.
00369          */
00370         memcpy ( undi_set_address.StationAddress, netdev->ll_addr,
00371                  sizeof ( undi_set_address.StationAddress ) );
00372         pxeparent_call ( undinet_entry, PXENV_UNDI_SET_STATION_ADDRESS,
00373                          &undi_set_address, sizeof ( undi_set_address ) );
00374 
00375         /* Open NIC.  We ask for promiscuous operation, since it's the
00376          * only way to ask for all multicast addresses.  On any
00377          * switched network, it shouldn't really make a difference to
00378          * performance.
00379          */
00380         memset ( &undi_open, 0, sizeof ( undi_open ) );
00381         undi_open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST | FLTR_PRMSCS );
00382         if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_OPEN,
00383                                      &undi_open, sizeof ( undi_open ) ) ) != 0 )
00384                 goto err;
00385 
00386         DBGC ( undinic, "UNDINIC %p opened\n", undinic );
00387         return 0;
00388 
00389  err:
00390         undinet_close ( netdev );
00391         return rc;
00392 }
00393 
00394 /**
00395  * Close NIC
00396  *
00397  * @v netdev            Net device
00398  */
00399 static void undinet_close ( struct net_device *netdev ) {
00400         struct undi_nic *undinic = netdev->priv;
00401         struct s_PXENV_UNDI_ISR undi_isr;
00402         struct s_PXENV_UNDI_CLOSE undi_close;
00403         int rc;
00404 
00405         /* Ensure ISR has exited cleanly */
00406         while ( undinic->isr_processing ) {
00407                 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
00408                 if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_ISR,
00409                                              &undi_isr,
00410                                              sizeof ( undi_isr ) ) ) != 0 )
00411                         break;
00412                 switch ( undi_isr.FuncFlag ) {
00413                 case PXENV_UNDI_ISR_OUT_TRANSMIT:
00414                 case PXENV_UNDI_ISR_OUT_RECEIVE:
00415                         /* Continue draining */
00416                         break;
00417                 default:
00418                         /* Stop processing */
00419                         undinic->isr_processing = 0;
00420                         break;
00421                 }
00422         }
00423 
00424         /* Close NIC */
00425         pxeparent_call ( undinet_entry, PXENV_UNDI_CLOSE,
00426                          &undi_close, sizeof ( undi_close ) );
00427 
00428         /* Disable interrupt and unhook ISR */
00429         disable_irq ( undinic->irq );
00430         undinet_unhook_isr ( undinic->irq );
00431 
00432         DBGC ( undinic, "UNDINIC %p closed\n", undinic );
00433 }
00434 
00435 /**
00436  * Enable/disable interrupts
00437  *
00438  * @v netdev            Net device
00439  * @v enable            Interrupts should be enabled
00440  */
00441 static void undinet_irq ( struct net_device *netdev, int enable ) {
00442         struct undi_nic *undinic = netdev->priv;
00443 
00444         /* Cannot support interrupts yet */
00445         DBGC ( undinic, "UNDINIC %p cannot %s interrupts\n",
00446                undinic, ( enable ? "enable" : "disable" ) );
00447 }
00448 
00449 /** UNDI network device operations */
00450 static struct net_device_operations undinet_operations = {
00451         .open           = undinet_open,
00452         .close          = undinet_close,
00453         .transmit       = undinet_transmit,
00454         .poll           = undinet_poll,
00455         .irq            = undinet_irq,
00456 };
00457 
00458 /**
00459  * Probe UNDI device
00460  *
00461  * @v undi              UNDI device
00462  * @ret rc              Return status code
00463  */
00464 int undinet_probe ( struct undi_device *undi ) {
00465         struct net_device *netdev;
00466         struct undi_nic *undinic;
00467         struct s_PXENV_START_UNDI start_undi;
00468         struct s_PXENV_UNDI_STARTUP undi_startup;
00469         struct s_PXENV_UNDI_INITIALIZE undi_initialize;
00470         struct s_PXENV_UNDI_GET_INFORMATION undi_info;
00471         struct s_PXENV_UNDI_GET_IFACE_INFO undi_iface;
00472         struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
00473         struct s_PXENV_UNDI_CLEANUP undi_cleanup;
00474         struct s_PXENV_STOP_UNDI stop_undi;
00475         int rc;
00476 
00477         /* Allocate net device */
00478         netdev = alloc_etherdev ( sizeof ( *undinic ) );
00479         if ( ! netdev )
00480                 return -ENOMEM;
00481         netdev_init ( netdev, &undinet_operations );
00482         undinic = netdev->priv;
00483         undi_set_drvdata ( undi, netdev );
00484         netdev->dev = &undi->dev;
00485         memset ( undinic, 0, sizeof ( *undinic ) );
00486         undinet_entry = undi->entry;
00487         DBGC ( undinic, "UNDINIC %p using UNDI %p\n", undinic, undi );
00488 
00489         /* Hook in UNDI stack */
00490         if ( ! ( undi->flags & UNDI_FL_STARTED ) ) {
00491                 memset ( &start_undi, 0, sizeof ( start_undi ) );
00492                 start_undi.AX = undi->pci_busdevfn;
00493                 start_undi.BX = undi->isapnp_csn;
00494                 start_undi.DX = undi->isapnp_read_port;
00495                 start_undi.ES = BIOS_SEG;
00496                 start_undi.DI = find_pnp_bios();
00497                 if ( ( rc = pxeparent_call ( undinet_entry, PXENV_START_UNDI,
00498                                              &start_undi,
00499                                              sizeof ( start_undi ) ) ) != 0 )
00500                         goto err_start_undi;
00501         }
00502         undi->flags |= UNDI_FL_STARTED;
00503 
00504         /* Bring up UNDI stack */
00505         if ( ! ( undi->flags & UNDI_FL_INITIALIZED ) ) {
00506                 memset ( &undi_startup, 0, sizeof ( undi_startup ) );
00507                 if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_STARTUP,
00508                                              &undi_startup,
00509                                              sizeof ( undi_startup ) ) ) != 0 )
00510                         goto err_undi_startup;
00511                 memset ( &undi_initialize, 0, sizeof ( undi_initialize ) );
00512                 if ( ( rc = pxeparent_call ( undinet_entry,
00513                                              PXENV_UNDI_INITIALIZE,
00514                                              &undi_initialize,
00515                                              sizeof ( undi_initialize ))) != 0 )
00516                         goto err_undi_initialize;
00517         }
00518         undi->flags |= UNDI_FL_INITIALIZED;
00519 
00520         /* Get device information */
00521         memset ( &undi_info, 0, sizeof ( undi_info ) );
00522         if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_GET_INFORMATION,
00523                                      &undi_info, sizeof ( undi_info ) ) ) != 0 )
00524                 goto err_undi_get_information;
00525         memcpy ( netdev->hw_addr, undi_info.PermNodeAddress, ETH_ALEN );
00526         undinic->irq = undi_info.IntNumber;
00527         if ( undinic->irq > IRQ_MAX ) {
00528                 DBGC ( undinic, "UNDINIC %p invalid IRQ %d\n",
00529                        undinic, undinic->irq );
00530                 goto err_bad_irq;
00531         }
00532         DBGC ( undinic, "UNDINIC %p is %s on IRQ %d\n",
00533                undinic, eth_ntoa ( netdev->hw_addr ), undinic->irq );
00534 
00535         /* Get interface information */
00536         memset ( &undi_iface, 0, sizeof ( undi_iface ) );
00537         if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_GET_IFACE_INFO,
00538                                      &undi_iface,
00539                                      sizeof ( undi_iface ) ) ) != 0 )
00540                 goto err_undi_get_iface_info;
00541         DBGC ( undinic, "UNDINIC %p has type %s, speed %d, flags %08x\n",
00542                undinic, undi_iface.IfaceType, undi_iface.LinkSpeed,
00543                undi_iface.ServiceFlags );
00544         if ( strncmp ( ( ( char * ) undi_iface.IfaceType ), "Etherboot",
00545                        sizeof ( undi_iface.IfaceType ) ) == 0 ) {
00546                 DBGC ( undinic, "UNDINIC %p Etherboot 5.4 workaround enabled\n",
00547                        undinic );
00548                 undinic->hacks |= UNDI_HACK_EB54;
00549         }
00550 
00551         /* Mark as link up; we don't handle link state */
00552         netdev_link_up ( netdev );
00553 
00554         /* Register network device */
00555         if ( ( rc = register_netdev ( netdev ) ) != 0 )
00556                 goto err_register;
00557 
00558         DBGC ( undinic, "UNDINIC %p added\n", undinic );
00559         return 0;
00560 
00561  err_register:
00562  err_undi_get_iface_info:
00563  err_bad_irq:
00564  err_undi_get_information:
00565  err_undi_initialize:
00566         /* Shut down UNDI stack */
00567         memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
00568         pxeparent_call ( undinet_entry, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
00569                          sizeof ( undi_shutdown ) );
00570         memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
00571         pxeparent_call ( undinet_entry, PXENV_UNDI_CLEANUP, &undi_cleanup,
00572                          sizeof ( undi_cleanup ) );
00573         undi->flags &= ~UNDI_FL_INITIALIZED;
00574  err_undi_startup:
00575         /* Unhook UNDI stack */
00576         memset ( &stop_undi, 0, sizeof ( stop_undi ) );
00577         pxeparent_call ( undinet_entry, PXENV_STOP_UNDI, &stop_undi,
00578                          sizeof ( stop_undi ) );
00579         undi->flags &= ~UNDI_FL_STARTED;
00580  err_start_undi:
00581         netdev_nullify ( netdev );
00582         netdev_put ( netdev );
00583         undi_set_drvdata ( undi, NULL );
00584         return rc;
00585 }
00586 
00587 /**
00588  * Remove UNDI device
00589  *
00590  * @v undi              UNDI device
00591  */
00592 void undinet_remove ( struct undi_device *undi ) {
00593         struct net_device *netdev = undi_get_drvdata ( undi );
00594         struct undi_nic *undinic = netdev->priv;
00595         struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
00596         struct s_PXENV_UNDI_CLEANUP undi_cleanup;
00597         struct s_PXENV_STOP_UNDI stop_undi;
00598 
00599         /* Unregister net device */
00600         unregister_netdev ( netdev );
00601 
00602         /* If we are preparing for an OS boot, or if we cannot exit
00603          * via the PXE stack, then shut down the PXE stack.
00604          */
00605         if ( ! ( undi->flags & UNDI_FL_KEEP_ALL ) ) {
00606 
00607                 /* Shut down UNDI stack */
00608                 memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
00609                 pxeparent_call ( undinet_entry, PXENV_UNDI_SHUTDOWN,
00610                                  &undi_shutdown, sizeof ( undi_shutdown ) );
00611                 memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
00612                 pxeparent_call ( undinet_entry, PXENV_UNDI_CLEANUP,
00613                                  &undi_cleanup, sizeof ( undi_cleanup ) );
00614                 undi->flags &= ~UNDI_FL_INITIALIZED;
00615 
00616                 /* Unhook UNDI stack */
00617                 memset ( &stop_undi, 0, sizeof ( stop_undi ) );
00618                 pxeparent_call ( undinet_entry, PXENV_STOP_UNDI, &stop_undi,
00619                                  sizeof ( stop_undi ) );
00620                 undi->flags &= ~UNDI_FL_STARTED;
00621         }
00622 
00623         /* Clear entry point */
00624         memset ( &undinet_entry, 0, sizeof ( undinet_entry ) );
00625 
00626         /* Free network device */
00627         netdev_nullify ( netdev );
00628         netdev_put ( netdev );
00629 
00630         DBGC ( undinic, "UNDINIC %p removed\n", undinic );
00631 }

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