summaryrefslogtreecommitdiffstats
path: root/src/arch/i386/drivers/net/undinet.c
diff options
context:
space:
mode:
authorMichael Brown2007-07-03 16:53:29 +0200
committerMichael Brown2007-07-03 16:53:29 +0200
commit2dc8ed1eb8015c2810fb01f524c8e8c46751ee9b (patch)
tree8ce6f7b3a3a423a4946e6526352bc8e0a5d061e4 /src/arch/i386/drivers/net/undinet.c
parentKill off now-redundant _irq() methods. (diff)
downloadipxe-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.c39
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: