iPXE
Functions
undinet.h File Reference

UNDI network device driver. More...

Go to the source code of this file.

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
int undinet_probe (struct undi_device *undi, struct device *dev)
 Probe UNDI device.
void undinet_remove (struct undi_device *undi)
 Remove UNDI device.

Detailed Description

UNDI network device driver.

Definition in file undinet.h.


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
int undinet_probe ( struct undi_device undi,
struct device dev 
)

Probe UNDI device.

Parameters:
undiUNDI device
devUnderlying generic device
Return values:
rcReturn status code

Definition at line 881 of file undinet.c.

References alloc_etherdev(), s_PXENV_START_UNDI::AX, BIOS_SEG, s_PXENV_START_UNDI::BX, s_PXENV_UNDI_GET_INFORMATION::CurrentNodeAddress, DBGC, device::desc, net_device::dev, s_PXENV_START_UNDI::DI, s_PXENV_START_UNDI::DX, ENOMEM, undi_device::entry, s_PXENV_START_UNDI::ES, ETH_ALEN, eth_ntoa(), find_pnp_bios(), undi_device::flags, undi_nic::hacks, net_device::hw_addr, s_PXENV_UNDI_GET_IFACE_INFO::IfaceType, s_PXENV_UNDI_GET_INFORMATION::IntNumber, undi_nic::irq, undi_nic::irq_supported, undi_device::isapnp_csn, undi_device::isapnp_read_port, s_PXENV_UNDI_GET_IFACE_INFO::LinkSpeed, net_device::ll_addr, mdelay(), memcpy(), memset(), netdev, netdev_init(), netdev_link_up(), netdev_nullify(), netdev_put(), NULL, undi_device::pci_busdevfn, s_PXENV_UNDI_GET_INFORMATION::PermNodeAddress, net_device::priv, PXENV_START_UNDI, PXENV_STOP_UNDI, PXENV_UNDI_CLEANUP, PXENV_UNDI_GET_IFACE_INFO, PXENV_UNDI_GET_INFORMATION, PXENV_UNDI_INITIALIZE, PXENV_UNDI_SHUTDOWN, PXENV_UNDI_STARTUP, rc, register_netdev(), s_PXENV_UNDI_GET_IFACE_INFO::ServiceFlags, strncmp(), SUPPORTED_IRQ, UNDI_FL_INITIALIZED, UNDI_FL_STARTED, UNDI_HACK_EB54, UNDI_INITIALIZE_RETRY_DELAY_MS, UNDI_INITIALIZE_RETRY_MAX, undi_set_drvdata(), undinet_call(), undinet_entry_point, and undinet_irq_is_broken().

Referenced by undibus_probe(), and undipci_probe().

                                                                   {
        struct net_device *netdev;
        struct undi_nic *undinic;
        struct s_PXENV_START_UNDI start_undi;
        struct s_PXENV_UNDI_STARTUP undi_startup;
        struct s_PXENV_UNDI_INITIALIZE undi_init;
        struct s_PXENV_UNDI_GET_INFORMATION undi_info;
        struct s_PXENV_UNDI_GET_IFACE_INFO undi_iface;
        struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
        struct s_PXENV_UNDI_CLEANUP undi_cleanup;
        struct s_PXENV_STOP_UNDI stop_undi;
        unsigned int retry;
        int rc;

        /* Allocate net device */
        netdev = alloc_etherdev ( sizeof ( *undinic ) );
        if ( ! netdev )
                return -ENOMEM;
        netdev_init ( netdev, &undinet_operations );
        undinic = netdev->priv;
        undi_set_drvdata ( undi, netdev );
        netdev->dev = dev;
        memset ( undinic, 0, sizeof ( *undinic ) );
        undinet_entry_point = undi->entry;
        DBGC ( undinic, "UNDINIC %p using UNDI %p\n", undinic, undi );

        /* Hook in UNDI stack */
        if ( ! ( undi->flags & UNDI_FL_STARTED ) ) {
                memset ( &start_undi, 0, sizeof ( start_undi ) );
                start_undi.AX = undi->pci_busdevfn;
                start_undi.BX = undi->isapnp_csn;
                start_undi.DX = undi->isapnp_read_port;
                start_undi.ES = BIOS_SEG;
                start_undi.DI = find_pnp_bios();
                if ( ( rc = undinet_call ( undinic, PXENV_START_UNDI,
                                           &start_undi,
                                           sizeof ( start_undi ) ) ) != 0 )
                        goto err_start_undi;
        }
        undi->flags |= UNDI_FL_STARTED;

        /* Bring up UNDI stack */
        if ( ! ( undi->flags & UNDI_FL_INITIALIZED ) ) {
                memset ( &undi_startup, 0, sizeof ( undi_startup ) );
                if ( ( rc = undinet_call ( undinic, PXENV_UNDI_STARTUP,
                                           &undi_startup,
                                           sizeof ( undi_startup ) ) ) != 0 )
                        goto err_undi_startup;
                /* On some PXE stacks, PXENV_UNDI_INITIALIZE may fail
                 * due to a transient condition (e.g. media test
                 * failing because the link has only just come out of
                 * reset).  We may therefore need to retry this call
                 * several times.
                 */
                for ( retry = 0 ; ; ) {
                        memset ( &undi_init, 0, sizeof ( undi_init ) );
                        if ( ( rc = undinet_call ( undinic,
                                                   PXENV_UNDI_INITIALIZE,
                                                   &undi_init,
                                                   sizeof ( undi_init ) ) ) ==0)
                                break;
                        if ( ++retry > UNDI_INITIALIZE_RETRY_MAX )
                                goto err_undi_initialize;
                        DBGC ( undinic, "UNDINIC %p retrying "
                               "PXENV_UNDI_INITIALIZE (retry %d)\n",
                               undinic, retry );
                        /* Delay to allow link to settle if necessary */
                        mdelay ( UNDI_INITIALIZE_RETRY_DELAY_MS );
                }
        }
        undi->flags |= UNDI_FL_INITIALIZED;

        /* Get device information */
        memset ( &undi_info, 0, sizeof ( undi_info ) );
        if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_INFORMATION,
                                   &undi_info, sizeof ( undi_info ) ) ) != 0 )
                goto err_undi_get_information;
        memcpy ( netdev->hw_addr, undi_info.PermNodeAddress, ETH_ALEN );
        memcpy ( netdev->ll_addr, undi_info.CurrentNodeAddress, ETH_ALEN );
        undinic->irq = undi_info.IntNumber;
        if ( undinic->irq > IRQ_MAX ) {
                DBGC ( undinic, "UNDINIC %p ignoring invalid IRQ %d\n",
                       undinic, undinic->irq );
                undinic->irq = 0;
        }
        DBGC ( undinic, "UNDINIC %p has MAC address %s and IRQ %d\n",
               undinic, eth_ntoa ( netdev->hw_addr ), undinic->irq );

        /* Get interface information */
        memset ( &undi_iface, 0, sizeof ( undi_iface ) );
        if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_IFACE_INFO,
                                   &undi_iface, sizeof ( undi_iface ) ) ) != 0 )
                goto err_undi_get_iface_info;
        DBGC ( undinic, "UNDINIC %p has type %s, speed %d, flags %08x\n",
               undinic, undi_iface.IfaceType, undi_iface.LinkSpeed,
               undi_iface.ServiceFlags );
        if ( ( undi_iface.ServiceFlags & SUPPORTED_IRQ ) &&
             ( undinic->irq != 0 ) ) {
                undinic->irq_supported = 1;
        }
        DBGC ( undinic, "UNDINIC %p using %s mode\n", undinic,
               ( undinic->irq_supported ? "interrupt" : "polling" ) );
        if ( strncmp ( ( ( char * ) undi_iface.IfaceType ), "Etherboot",
                       sizeof ( undi_iface.IfaceType ) ) == 0 ) {
                DBGC ( undinic, "UNDINIC %p Etherboot 5.4 workaround enabled\n",
                       undinic );
                undinic->hacks |= UNDI_HACK_EB54;
        }
        if ( undinet_irq_is_broken ( &dev->desc ) ) {
                DBGC ( undinic, "UNDINIC %p forcing polling mode due to "
                       "broken interrupts\n", undinic );
                undinic->irq_supported = 0;
        }

        /* Register network device */
        if ( ( rc = register_netdev ( netdev ) ) != 0 )
                goto err_register;

        /* Mark as link up; we don't handle link state */
        netdev_link_up ( netdev );

        DBGC ( undinic, "UNDINIC %p added\n", undinic );
        return 0;

 err_register:
 err_undi_get_iface_info:
 err_undi_get_information:
 err_undi_initialize:
        /* Shut down UNDI stack */
        memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
        undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
                       sizeof ( undi_shutdown ) );
        memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
        undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup,
                       sizeof ( undi_cleanup ) );
        undi->flags &= ~UNDI_FL_INITIALIZED;
 err_undi_startup:
        /* Unhook UNDI stack */
        memset ( &stop_undi, 0, sizeof ( stop_undi ) );
        undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
                       sizeof ( stop_undi ) );
        undi->flags &= ~UNDI_FL_STARTED;
 err_start_undi:
        netdev_nullify ( netdev );
        netdev_put ( netdev );
        undi_set_drvdata ( undi, NULL );
        return rc;
}
void undinet_remove ( struct undi_device undi)

Remove UNDI device.

Parameters:
undiUNDI device

Definition at line 1035 of file undinet.c.

References DBGC, undi_device::flags, memset(), netdev, netdev_nullify(), netdev_put(), net_device::priv, PXENV_STOP_UNDI, PXENV_UNDI_CLEANUP, PXENV_UNDI_SHUTDOWN, UNDI_FL_INITIALIZED, UNDI_FL_KEEP_ALL, UNDI_FL_STARTED, undi_get_drvdata(), undinet_call(), undinet_entry_point, and unregister_netdev().

Referenced by undibus_remove(), and undipci_remove().

                                                 {
        struct net_device *netdev = undi_get_drvdata ( undi );
        struct undi_nic *undinic = netdev->priv;
        struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
        struct s_PXENV_UNDI_CLEANUP undi_cleanup;
        struct s_PXENV_STOP_UNDI stop_undi;

        /* Unregister net device */
        unregister_netdev ( netdev );

        /* If we are preparing for an OS boot, or if we cannot exit
         * via the PXE stack, then shut down the PXE stack.
         */
        if ( ! ( undi->flags & UNDI_FL_KEEP_ALL ) ) {

                /* Shut down UNDI stack */
                memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
                undinet_call ( undinic, PXENV_UNDI_SHUTDOWN,
                               &undi_shutdown, sizeof ( undi_shutdown ) );
                memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
                undinet_call ( undinic, PXENV_UNDI_CLEANUP,
                               &undi_cleanup, sizeof ( undi_cleanup ) );
                undi->flags &= ~UNDI_FL_INITIALIZED;

                /* Unhook UNDI stack */
                memset ( &stop_undi, 0, sizeof ( stop_undi ) );
                undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
                               sizeof ( stop_undi ) );
                undi->flags &= ~UNDI_FL_STARTED;
        }

        /* Clear entry point */
        memset ( &undinet_entry_point, 0, sizeof ( undinet_entry_point ) );

        /* Free network device */
        netdev_nullify ( netdev );
        netdev_put ( netdev );

        DBGC ( undinic, "UNDINIC %p removed\n", undinic );
}