summaryrefslogtreecommitdiffstats
path: root/src/arch/i386/drivers/net/undinet.c
diff options
context:
space:
mode:
authorMichael Brown2010-11-19 19:48:00 +0100
committerMichael Brown2010-11-19 19:52:20 +0100
commit006d9f1f60594d6ffd147bea1f08bc753051a50d (patch)
treecb6d09c6717b00d2bb6cd8bb1aec76594c3ac9b9 /src/arch/i386/drivers/net/undinet.c
parent[tcp] Remove obsolete constants (diff)
downloadipxe-006d9f1f60594d6ffd147bea1f08bc753051a50d.tar.gz
ipxe-006d9f1f60594d6ffd147bea1f08bc753051a50d.tar.xz
ipxe-006d9f1f60594d6ffd147bea1f08bc753051a50d.zip
[undi] Support underlying UNDI devices that don't support interrupts
Some network cards do not generate interrupts when operated via the UNDI API. Allow for this by waiting for the ISR to be triggered only if the PXE stack advertises that it supports interrupts. When the PXE stack does not advertise interrupt support, we skip the call to PXENV_UNDI_ISR_IN_START and just poll the device using PXENV_UNDI_ISR_IN_PROCESS. This matches the observed behaviour of at least one other PXE NBP (emBoot's winBoot/i), so there is a reasonable chance of this working. Originally-implemented-by: Muralidhar Appalla <Muralidhar.Appalla@emulex.com> 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.c48
1 files changed, 31 insertions, 17 deletions
diff --git a/src/arch/i386/drivers/net/undinet.c b/src/arch/i386/drivers/net/undinet.c
index e6c08464..7571574d 100644
--- a/src/arch/i386/drivers/net/undinet.c
+++ b/src/arch/i386/drivers/net/undinet.c
@@ -43,6 +43,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
/** An UNDI NIC */
struct undi_nic {
+ /** Device supports IRQs */
+ int irq_supported;
/** Assigned IRQ number */
unsigned int irq;
/** Currently processing ISR */
@@ -251,8 +253,10 @@ static void undinet_poll ( struct net_device *netdev ) {
int rc;
if ( ! undinic->isr_processing ) {
- /* Do nothing unless ISR has been triggered */
- if ( ! undinet_isr_triggered() ) {
+ /* If interrupts are supported, then do nothing unless
+ * the ISR has been triggered.
+ */
+ if ( undinic->irq_supported && ( ! undinet_isr_triggered() ) ){
/* Allow interrupt to occur */
__asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
"nop\n\t"
@@ -357,10 +361,12 @@ static int undinet_open ( struct net_device *netdev ) {
struct s_PXENV_UNDI_OPEN undi_open;
int rc;
- /* Hook interrupt service routine and enable interrupt */
- undinet_hook_isr ( undinic->irq );
- enable_irq ( undinic->irq );
- send_eoi ( undinic->irq );
+ /* Hook interrupt service routine and enable interrupt if supported */
+ if ( undinic->irq_supported ) {
+ undinet_hook_isr ( undinic->irq );
+ enable_irq ( undinic->irq );
+ send_eoi ( undinic->irq );
+ }
/* Set station address. Required for some PXE stacks; will
* spuriously fail on others. Ignore failures. We only ever
@@ -425,9 +431,11 @@ static void undinet_close ( struct net_device *netdev ) {
pxeparent_call ( undinet_entry, PXENV_UNDI_CLOSE,
&undi_close, sizeof ( undi_close ) );
- /* Disable interrupt and unhook ISR */
- disable_irq ( undinic->irq );
- undinet_unhook_isr ( undinic->irq );
+ /* Disable interrupt and unhook ISR if supported */
+ if ( undinic->irq_supported ) {
+ disable_irq ( undinic->irq );
+ undinet_unhook_isr ( undinic->irq );
+ }
DBGC ( undinic, "UNDINIC %p closed\n", undinic );
}
@@ -524,13 +532,8 @@ int undinet_probe ( struct undi_device *undi ) {
goto err_undi_get_information;
memcpy ( netdev->hw_addr, undi_info.PermNodeAddress, ETH_ALEN );
undinic->irq = undi_info.IntNumber;
- if ( undinic->irq > IRQ_MAX ) {
- DBGC ( undinic, "UNDINIC %p invalid IRQ %d\n",
- undinic, undinic->irq );
- goto err_bad_irq;
- }
- DBGC ( undinic, "UNDINIC %p is %s on IRQ %d\n",
- undinic, eth_ntoa ( netdev->hw_addr ), undinic->irq );
+ DBGC ( undinic, "UNDINIC %p has MAC address %s\n",
+ undinic, eth_ntoa ( netdev->hw_addr ) );
/* Get interface information */
memset ( &undi_iface, 0, sizeof ( undi_iface ) );
@@ -541,6 +544,17 @@ int undinet_probe ( struct undi_device *undi ) {
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 ) {
+ if ( undinic->irq > IRQ_MAX ) {
+ DBGC ( undinic, "UNDINIC %p has invalid IRQ %d\n",
+ undinic, undinic->irq );
+ rc = -EINVAL;
+ goto err_bad_irq;
+ }
+ undinic->irq_supported = 1;
+ DBGC ( undinic, "UNDINIC %p uses IRQ %d\n",
+ undinic, undinic->irq );
+ }
if ( strncmp ( ( ( char * ) undi_iface.IfaceType ), "Etherboot",
sizeof ( undi_iface.IfaceType ) ) == 0 ) {
DBGC ( undinic, "UNDINIC %p Etherboot 5.4 workaround enabled\n",
@@ -559,8 +573,8 @@ int undinet_probe ( struct undi_device *undi ) {
return 0;
err_register:
- err_undi_get_iface_info:
err_bad_irq:
+ err_undi_get_iface_info:
err_undi_get_information:
err_undi_initialize:
/* Shut down UNDI stack */