Skip to content

Commit

Permalink
[undi] Cope with devices that erroneously claim not to use interrupts
Browse files Browse the repository at this point in the history
Some PXE stacks advertise that interrupts are not supported, despite
requiring the use of interrupts.  Attempt to cope with such cards
without breaking others by always hooking the interrupt, and using the
"interrupts supported" flag only to decide whether or not to wait for
an interrupt before calling PXENV_UNDI_ISR_IN_PROCESS.

The possible combinations are therefore:

1. Card generates interrupts and claims to support interrupts

   iPXE will call PXENV_UNDI_ISR_IN_PROCESS only after an interrupt
   has been observed.  (This is required to avoid lockups in some PXE
   stacks, which spuriously sulk if called before an interrupt has
   been generated.)

   Such a card should work correctly.

2. Card does not generate interrupts and does not claim to support
   interrupts

   iPXE will call PXENV_UNDI_ISR_IN_PROCESS indiscriminately, matching
   the observed behaviour of at least one other PXE NBP (winBoot/i).

   Such a card should work correctly.

3. Card generates interrupts but claims not to support interrupts

   iPXE will call PXENV_UNDI_ISR_IN_PROCESS indiscriminately.  An
   interrupt will still result in a call to PXENV_UNDI_ISR_IN_START.

   Such a card may work correctly.

4. Card does not generate interrupts but claims to support interrupts

   Such a card will not work at all.

Reported-by: Jerry Cheng <jaspers.cheng@msa.hinet.net>
Tested-by: Jerry Cheng <jaspers.cheng@msa.hinet.net>
Reported-by: Mauricio Silveira <mauricio@livreti.com.br>
Tested-by: Mauricio Silveira <mauricio@livreti.com.br>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
  • Loading branch information
mcb30 committed Mar 23, 2011
1 parent 8482451 commit b6ca3aa
Showing 1 changed file with 28 additions and 21 deletions.
49 changes: 28 additions & 21 deletions src/arch/i386/drivers/net/undinet.c
Expand Up @@ -253,16 +253,24 @@ static void undinet_poll ( struct net_device *netdev ) {
int rc;

if ( ! undinic->isr_processing ) {
/* If interrupts are supported, then do nothing unless
* the ISR has been triggered.
/* Allow interrupt to occur. Do this even if
* interrupts are not known to be supported, since
* some cards erroneously report that they do not
* support interrupts.
*/
if ( undinic->irq_supported && ( ! undinet_isr_triggered() ) ){
if ( ! undinet_isr_triggered() ) {
/* Allow interrupt to occur */
__asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
"nop\n\t"
"nop\n\t"
"cli\n\t" ) : : );
return;

/* If interrupts are known to be supported,
* then do nothing on this poll; wait for the
* interrupt to be triggered.
*/
if ( undinic->irq_supported )
return;
}

/* Start ISR processing */
Expand Down Expand Up @@ -361,8 +369,8 @@ static int undinet_open ( struct net_device *netdev ) {
struct s_PXENV_UNDI_OPEN undi_open;
int rc;

/* Hook interrupt service routine and enable interrupt if supported */
if ( undinic->irq_supported ) {
/* Hook interrupt service routine and enable interrupt if applicable */
if ( undinic->irq ) {
undinet_hook_isr ( undinic->irq );
enable_irq ( undinic->irq );
send_eoi ( undinic->irq );
Expand Down Expand Up @@ -431,8 +439,8 @@ 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 if supported */
if ( undinic->irq_supported ) {
/* Disable interrupt and unhook ISR if applicable */
if ( undinic->irq ) {
disable_irq ( undinic->irq );
undinet_unhook_isr ( undinic->irq );
}
Expand Down Expand Up @@ -532,8 +540,14 @@ 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;
DBGC ( undinic, "UNDINIC %p has MAC address %s\n",
undinic, eth_ntoa ( netdev->hw_addr ) );
if ( undinic->irq > IRQ_MAX ) {
DBGC ( undinic, "UNDINIC %p has invalid IRQ %d\n",
undinic, undinic->irq );
rc = -EINVAL;
goto err_bad_irq;
}
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 ) );
Expand All @@ -544,17 +558,10 @@ 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;
}
if ( undi_iface.ServiceFlags & SUPPORTED_IRQ )
undinic->irq_supported = 1;
DBGC ( undinic, "UNDINIC %p uses IRQ %d\n",
undinic, undinic->irq );
}
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",
Expand All @@ -573,8 +580,8 @@ int undinet_probe ( struct undi_device *undi ) {
return 0;

err_register:
err_bad_irq:
err_undi_get_iface_info:
err_bad_irq:
err_undi_get_information:
err_undi_initialize:
/* Shut down UNDI stack */
Expand Down

0 comments on commit b6ca3aa

Please sign in to comment.