Browse Source

[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>
tags/v1.20.1
Michael Brown 13 years ago
parent
commit
b6ca3aa01f
1 changed files with 28 additions and 21 deletions
  1. 28
    21
      src/arch/i386/drivers/net/undinet.c

+ 28
- 21
src/arch/i386/drivers/net/undinet.c View File

@@ -253,16 +253,24 @@ static void undinet_poll ( struct net_device *netdev ) {
253 253
 	int rc;
254 254
 
255 255
 	if ( ! undinic->isr_processing ) {
256
-		/* If interrupts are supported, then do nothing unless
257
-		 * the ISR has been triggered.
256
+		/* Allow interrupt to occur.  Do this even if
257
+		 * interrupts are not known to be supported, since
258
+		 * some cards erroneously report that they do not
259
+		 * support interrupts.
258 260
 		 */
259
-		if ( undinic->irq_supported && ( ! undinet_isr_triggered() ) ){
261
+		if ( ! undinet_isr_triggered() ) {
260 262
 			/* Allow interrupt to occur */
261 263
 			__asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
262 264
 							   "nop\n\t"
263 265
 							   "nop\n\t"
264 266
 							   "cli\n\t" ) : : );
265
-			return;
267
+
268
+			/* If interrupts are known to be supported,
269
+			 * then do nothing on this poll; wait for the
270
+			 * interrupt to be triggered.
271
+			 */
272
+			if ( undinic->irq_supported )
273
+				return;
266 274
 		}
267 275
 
268 276
 		/* Start ISR processing */
@@ -361,8 +369,8 @@ static int undinet_open ( struct net_device *netdev ) {
361 369
 	struct s_PXENV_UNDI_OPEN undi_open;
362 370
 	int rc;
363 371
 
364
-	/* Hook interrupt service routine and enable interrupt if supported */
365
-	if ( undinic->irq_supported ) {
372
+	/* Hook interrupt service routine and enable interrupt if applicable */
373
+	if ( undinic->irq ) {
366 374
 		undinet_hook_isr ( undinic->irq );
367 375
 		enable_irq ( undinic->irq );
368 376
 		send_eoi ( undinic->irq );
@@ -431,8 +439,8 @@ static void undinet_close ( struct net_device *netdev ) {
431 439
 	pxeparent_call ( undinet_entry, PXENV_UNDI_CLOSE,
432 440
 			 &undi_close, sizeof ( undi_close ) );
433 441
 
434
-	/* Disable interrupt and unhook ISR if supported */
435
-	if ( undinic->irq_supported ) {
442
+	/* Disable interrupt and unhook ISR if applicable */
443
+	if ( undinic->irq ) {
436 444
 		disable_irq ( undinic->irq );
437 445
 		undinet_unhook_isr ( undinic->irq );
438 446
 	}
@@ -532,8 +540,14 @@ int undinet_probe ( struct undi_device *undi ) {
532 540
 		goto err_undi_get_information;
533 541
 	memcpy ( netdev->hw_addr, undi_info.PermNodeAddress, ETH_ALEN );
534 542
 	undinic->irq = undi_info.IntNumber;
535
-	DBGC ( undinic, "UNDINIC %p has MAC address %s\n",
536
-	       undinic, eth_ntoa ( netdev->hw_addr ) );
543
+	if ( undinic->irq > IRQ_MAX ) {
544
+		DBGC ( undinic, "UNDINIC %p has invalid IRQ %d\n",
545
+		       undinic, undinic->irq );
546
+		rc = -EINVAL;
547
+		goto err_bad_irq;
548
+	}
549
+	DBGC ( undinic, "UNDINIC %p has MAC address %s and IRQ %d\n",
550
+	       undinic, eth_ntoa ( netdev->hw_addr ), undinic->irq );
537 551
 
538 552
 	/* Get interface information */
539 553
 	memset ( &undi_iface, 0, sizeof ( undi_iface ) );
@@ -544,17 +558,10 @@ int undinet_probe ( struct undi_device *undi ) {
544 558
 	DBGC ( undinic, "UNDINIC %p has type %s, speed %d, flags %08x\n",
545 559
 	       undinic, undi_iface.IfaceType, undi_iface.LinkSpeed,
546 560
 	       undi_iface.ServiceFlags );
547
-	if ( undi_iface.ServiceFlags & SUPPORTED_IRQ ) {
548
-		if ( undinic->irq > IRQ_MAX ) {
549
-			DBGC ( undinic, "UNDINIC %p has invalid IRQ %d\n",
550
-			       undinic, undinic->irq );
551
-			rc = -EINVAL;
552
-			goto err_bad_irq;
553
-		}
561
+	if ( undi_iface.ServiceFlags & SUPPORTED_IRQ )
554 562
 		undinic->irq_supported = 1;
555
-		DBGC ( undinic, "UNDINIC %p uses IRQ %d\n",
556
-		       undinic, undinic->irq );
557
-	}
563
+	DBGC ( undinic, "UNDINIC %p using %s mode\n", undinic,
564
+	       ( undinic->irq_supported ? "interrupt" : "polling" ) );
558 565
 	if ( strncmp ( ( ( char * ) undi_iface.IfaceType ), "Etherboot",
559 566
 		       sizeof ( undi_iface.IfaceType ) ) == 0 ) {
560 567
 		DBGC ( undinic, "UNDINIC %p Etherboot 5.4 workaround enabled\n",
@@ -573,8 +580,8 @@ int undinet_probe ( struct undi_device *undi ) {
573 580
 	return 0;
574 581
 
575 582
  err_register:
576
- err_bad_irq:
577 583
  err_undi_get_iface_info:
584
+ err_bad_irq:
578 585
  err_undi_get_information:
579 586
  err_undi_initialize:
580 587
 	/* Shut down UNDI stack */

Loading…
Cancel
Save