|
@@ -45,8 +45,20 @@ struct undi_nic {
|
45
|
45
|
unsigned int irq;
|
46
|
46
|
/** Currently processing ISR */
|
47
|
47
|
int isr_processing;
|
|
48
|
+ /** Bug workarounds */
|
|
49
|
+ int hacks;
|
48
|
50
|
};
|
49
|
51
|
|
|
52
|
+/**
|
|
53
|
+ * @defgroup undi_hacks UNDI workarounds
|
|
54
|
+ * @{
|
|
55
|
+ */
|
|
56
|
+
|
|
57
|
+/** Work around Etherboot 5.4 bugs */
|
|
58
|
+#define UNDI_HACK_EB54 0x0001
|
|
59
|
+
|
|
60
|
+/** @} */
|
|
61
|
+
|
50
|
62
|
static void undinet_close ( struct net_device *netdev );
|
51
|
63
|
|
52
|
64
|
/*****************************************************************************
|
|
@@ -245,6 +257,9 @@ static struct segoff prev_handler[ IRQ_MAX + 1 ];
|
245
|
257
|
static volatile uint8_t __text16 ( trigger_count ) = 0;
|
246
|
258
|
#define trigger_count __use_text16 ( trigger_count )
|
247
|
259
|
|
|
260
|
+/** Last observed trigger count */
|
|
261
|
+static unsigned int last_trigger_count = 0;
|
|
262
|
+
|
248
|
263
|
/**
|
249
|
264
|
* Hook UNDI interrupt service routine
|
250
|
265
|
*
|
|
@@ -292,7 +307,6 @@ static void undinet_unhook_isr ( unsigned int irq ) {
|
292
|
307
|
* @ret triggered ISR has been triggered since last check
|
293
|
308
|
*/
|
294
|
309
|
static int undinet_isr_triggered ( void ) {
|
295
|
|
- static unsigned int last_trigger_count = 0;
|
296
|
310
|
unsigned int this_trigger_count;
|
297
|
311
|
|
298
|
312
|
/* Read trigger_count. Do this only once; it is volatile */
|
|
@@ -470,9 +484,15 @@ static void undinet_poll ( struct net_device *netdev, unsigned int rx_quota ) {
|
470
|
484
|
undi_isr.Frame.segment,
|
471
|
485
|
undi_isr.Frame.offset, frag_len );
|
472
|
486
|
if ( iob_len ( iobuf ) == len ) {
|
|
487
|
+ /* Whole packet received; deliver it */
|
473
|
488
|
netdev_rx ( netdev, iobuf );
|
474
|
489
|
iobuf = NULL;
|
475
|
490
|
--rx_quota;
|
|
491
|
+ /* Etherboot 5.4 fails to return all packets
|
|
492
|
+ * under mild load; pretend it retriggered.
|
|
493
|
+ */
|
|
494
|
+ if ( undinic->hacks & UNDI_HACK_EB54 )
|
|
495
|
+ --last_trigger_count;
|
476
|
496
|
}
|
477
|
497
|
break;
|
478
|
498
|
case PXENV_UNDI_ISR_OUT_DONE:
|
|
@@ -592,6 +612,7 @@ int undinet_probe ( struct undi_device *undi ) {
|
592
|
612
|
struct s_PXENV_UNDI_STARTUP undi_startup;
|
593
|
613
|
struct s_PXENV_UNDI_INITIALIZE undi_initialize;
|
594
|
614
|
struct s_PXENV_UNDI_GET_INFORMATION undi_info;
|
|
615
|
+ struct s_PXENV_UNDI_GET_IFACE_INFO undi_iface;
|
595
|
616
|
struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
|
596
|
617
|
struct s_PXENV_UNDI_CLEANUP undi_cleanup;
|
597
|
618
|
struct s_PXENV_STOP_UNDI stop_undi;
|
|
@@ -649,6 +670,21 @@ int undinet_probe ( struct undi_device *undi ) {
|
649
|
670
|
DBGC ( undinic, "UNDINIC %p is %s on IRQ %d\n",
|
650
|
671
|
undinic, eth_ntoa ( netdev->ll_addr ), undinic->irq );
|
651
|
672
|
|
|
673
|
+ /* Get interface information */
|
|
674
|
+ memset ( &undi_iface, 0, sizeof ( undi_iface ) );
|
|
675
|
+ if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_IFACE_INFO,
|
|
676
|
+ &undi_iface,
|
|
677
|
+ sizeof ( undi_iface ) ) ) != 0 )
|
|
678
|
+ goto err_undi_get_iface_info;
|
|
679
|
+ DBGC ( undinic, "UNDINIC %p has type %s and link speed %ld\n",
|
|
680
|
+ undinic, undi_iface.IfaceType, undi_iface.LinkSpeed );
|
|
681
|
+ if ( strncmp ( ( ( char * ) undi_iface.IfaceType ), "Etherboot",
|
|
682
|
+ sizeof ( undi_iface.IfaceType ) ) == 0 ) {
|
|
683
|
+ DBGC ( undinic, "UNDINIC %p Etherboot 5.4 workaround enabled\n",
|
|
684
|
+ undinic );
|
|
685
|
+ undinic->hacks |= UNDI_HACK_EB54;
|
|
686
|
+ }
|
|
687
|
+
|
652
|
688
|
/* Point to NIC specific routines */
|
653
|
689
|
netdev->open = undinet_open;
|
654
|
690
|
netdev->close = undinet_close;
|
|
@@ -663,6 +699,7 @@ int undinet_probe ( struct undi_device *undi ) {
|
663
|
699
|
return 0;
|
664
|
700
|
|
665
|
701
|
err_register:
|
|
702
|
+ err_undi_get_iface_info:
|
666
|
703
|
err_bad_irq:
|
667
|
704
|
err_undi_get_information:
|
668
|
705
|
err_undi_initialize:
|