diff options
author | Michael Brown | 2007-07-03 16:53:29 +0200 |
---|---|---|
committer | Michael Brown | 2007-07-03 16:53:29 +0200 |
commit | 2dc8ed1eb8015c2810fb01f524c8e8c46751ee9b (patch) | |
tree | 8ce6f7b3a3a423a4946e6526352bc8e0a5d061e4 /src/arch/i386/drivers/net/undinet.c | |
parent | Kill off now-redundant _irq() methods. (diff) | |
download | ipxe-2dc8ed1eb8015c2810fb01f524c8e8c46751ee9b.tar.gz ipxe-2dc8ed1eb8015c2810fb01f524c8e8c46751ee9b.tar.xz ipxe-2dc8ed1eb8015c2810fb01f524c8e8c46751ee9b.zip |
Work around Etherboot 5.4 bug when multiple packets are received.
Diffstat (limited to 'src/arch/i386/drivers/net/undinet.c')
-rw-r--r-- | src/arch/i386/drivers/net/undinet.c | 39 |
1 files changed, 38 insertions, 1 deletions
diff --git a/src/arch/i386/drivers/net/undinet.c b/src/arch/i386/drivers/net/undinet.c index dc4ef98f..46a759cc 100644 --- a/src/arch/i386/drivers/net/undinet.c +++ b/src/arch/i386/drivers/net/undinet.c @@ -45,8 +45,20 @@ struct undi_nic { unsigned int irq; /** Currently processing ISR */ int isr_processing; + /** Bug workarounds */ + int hacks; }; +/** + * @defgroup undi_hacks UNDI workarounds + * @{ + */ + +/** Work around Etherboot 5.4 bugs */ +#define UNDI_HACK_EB54 0x0001 + +/** @} */ + static void undinet_close ( struct net_device *netdev ); /***************************************************************************** @@ -245,6 +257,9 @@ static struct segoff prev_handler[ IRQ_MAX + 1 ]; static volatile uint8_t __text16 ( trigger_count ) = 0; #define trigger_count __use_text16 ( trigger_count ) +/** Last observed trigger count */ +static unsigned int last_trigger_count = 0; + /** * Hook UNDI interrupt service routine * @@ -292,7 +307,6 @@ static void undinet_unhook_isr ( unsigned int irq ) { * @ret triggered ISR has been triggered since last check */ static int undinet_isr_triggered ( void ) { - static unsigned int last_trigger_count = 0; unsigned int this_trigger_count; /* Read trigger_count. Do this only once; it is volatile */ @@ -470,9 +484,15 @@ static void undinet_poll ( struct net_device *netdev, unsigned int rx_quota ) { undi_isr.Frame.segment, undi_isr.Frame.offset, frag_len ); if ( iob_len ( iobuf ) == len ) { + /* Whole packet received; deliver it */ netdev_rx ( netdev, iobuf ); iobuf = NULL; --rx_quota; + /* Etherboot 5.4 fails to return all packets + * under mild load; pretend it retriggered. + */ + if ( undinic->hacks & UNDI_HACK_EB54 ) + --last_trigger_count; } break; case PXENV_UNDI_ISR_OUT_DONE: @@ -592,6 +612,7 @@ int undinet_probe ( struct undi_device *undi ) { struct s_PXENV_UNDI_STARTUP undi_startup; struct s_PXENV_UNDI_INITIALIZE undi_initialize; 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; @@ -649,6 +670,21 @@ int undinet_probe ( struct undi_device *undi ) { DBGC ( undinic, "UNDINIC %p is %s on IRQ %d\n", undinic, eth_ntoa ( netdev->ll_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 and link speed %ld\n", + undinic, undi_iface.IfaceType, undi_iface.LinkSpeed ); + 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; + } + /* Point to NIC specific routines */ netdev->open = undinet_open; netdev->close = undinet_close; @@ -663,6 +699,7 @@ int undinet_probe ( struct undi_device *undi ) { return 0; err_register: + err_undi_get_iface_info: err_bad_irq: err_undi_get_information: err_undi_initialize: |