mirror of https://github.com/ipxe/ipxe.git
[undi] Cope with devices that erroneously claim not to use interrupts
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>pull/1/head
parent
8482451812
commit
b6ca3aa01f
|
@ -253,15 +253,23 @@ 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" ) : : );
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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 ) );
|
||||
|
@ -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",
|
||||
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue