summaryrefslogtreecommitdiffstats
path: root/src/arch/i386/drivers/net/undinet.c
diff options
context:
space:
mode:
authorMichael Brown2011-12-08 01:06:53 +0100
committerMichael Brown2011-12-08 01:12:28 +0100
commitfa3ca017acbf70c900a101409f006c37e9d0e994 (patch)
treed2376cb9d8d157994b07ef177ce9dddb47adcd9e /src/arch/i386/drivers/net/undinet.c
parent[dhcp] Add PXE-mandated DHCP options [128,135] to parameter request list (diff)
downloadipxe-fa3ca017acbf70c900a101409f006c37e9d0e994.tar.gz
ipxe-fa3ca017acbf70c900a101409f006c37e9d0e994.tar.xz
ipxe-fa3ca017acbf70c900a101409f006c37e9d0e994.zip
[undi] Retry PXENV_UNDI_INITIALIZE multiple times
On at least one PXE stack (Realtek r8169), PXENV_UNDI_INITIALIZE has been observed to fail intermittently due to a media test failure (PXE error 0x00000061). Retrying the call to PXENV_UNDI_INITIALIZE succeeds, and the NIC is then usable. It is worth noting that this particular Realtek PXE stack is already known to be unreliable: for example, it repeatably fails its own boot-time media test after every warm reboot. Fix by attempting PXENV_UNDI_INITIALIZE multiple times, with a short delay between each attempt to allow the link to settle. Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/arch/i386/drivers/net/undinet.c')
-rw-r--r--src/arch/i386/drivers/net/undinet.c38
1 files changed, 30 insertions, 8 deletions
diff --git a/src/arch/i386/drivers/net/undinet.c b/src/arch/i386/drivers/net/undinet.c
index c391469f..63b37389 100644
--- a/src/arch/i386/drivers/net/undinet.c
+++ b/src/arch/i386/drivers/net/undinet.c
@@ -19,6 +19,7 @@
FILE_LICENCE ( GPL2_OR_LATER );
#include <string.h>
+#include <unistd.h>
#include <pxe.h>
#include <realmode.h>
#include <pic8259.h>
@@ -34,7 +35,6 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <undinet.h>
#include <pxeparent.h>
-
/** @file
*
* UNDI network device driver
@@ -63,6 +63,12 @@ struct undi_nic {
/** @} */
+/** Maximum number of times to retry PXENV_UNDI_INITIALIZE */
+#define UNDI_INITIALIZE_RETRY_MAX 10
+
+/** Delay between retries of PXENV_UNDI_INITIALIZE */
+#define UNDI_INITIALIZE_RETRY_DELAY_MS 200
+
static void undinet_close ( struct net_device *netdev );
/** Address of UNDI entry point */
@@ -482,12 +488,13 @@ int undinet_probe ( struct undi_device *undi ) {
struct undi_nic *undinic;
struct s_PXENV_START_UNDI start_undi;
struct s_PXENV_UNDI_STARTUP undi_startup;
- struct s_PXENV_UNDI_INITIALIZE undi_initialize;
+ 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 */
@@ -524,12 +531,27 @@ int undinet_probe ( struct undi_device *undi ) {
&undi_startup,
sizeof ( undi_startup ) ) ) != 0 )
goto err_undi_startup;
- memset ( &undi_initialize, 0, sizeof ( undi_initialize ) );
- if ( ( rc = pxeparent_call ( undinet_entry,
- PXENV_UNDI_INITIALIZE,
- &undi_initialize,
- sizeof ( undi_initialize ))) != 0 )
- goto err_undi_initialize;
+ /* 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 = pxeparent_call ( undinet_entry,
+ 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;