|
@@ -32,6 +32,8 @@
|
32
|
32
|
*
|
33
|
33
|
*/
|
34
|
34
|
|
|
35
|
+static void undi_close ( struct net_device *netdev );
|
|
36
|
+
|
35
|
37
|
/*****************************************************************************
|
36
|
38
|
*
|
37
|
39
|
* UNDI interrupt service routine
|
|
@@ -47,28 +49,33 @@
|
47
|
49
|
*/
|
48
|
50
|
extern void undi_isr ( void );
|
49
|
51
|
|
50
|
|
-/** Vector for chaining to other interrupts handlers */
|
51
|
|
-static struct segoff __text16 ( undi_isr_chain );
|
52
|
|
-#define undi_isr_chain __use_text16 ( undi_isr_chain )
|
|
52
|
+/** Dummy chain vector */
|
|
53
|
+static struct segoff undi_isr_dummy_chain;
|
53
|
54
|
|
54
|
55
|
/** IRQ trigger count */
|
55
|
|
-static volatile uint16_t __text16 ( trigger_count );
|
|
56
|
+static volatile uint16_t __text16 ( trigger_count ) = 0;
|
56
|
57
|
#define trigger_count __use_text16 ( trigger_count )
|
57
|
58
|
|
58
|
59
|
/**
|
59
|
60
|
* Hook UNDI interrupt service routine
|
60
|
61
|
*
|
61
|
62
|
* @v irq IRQ number
|
|
63
|
+ *
|
|
64
|
+ * The UNDI ISR specifically does @b not chain to the previous
|
|
65
|
+ * interrupt handler. BIOSes seem to install somewhat perverse
|
|
66
|
+ * default interrupt handlers; some do nothing other than an iret (and
|
|
67
|
+ * so will cause a screaming interrupt if there really is another
|
|
68
|
+ * interrupting device) and some disable the interrupt at the PIC (and
|
|
69
|
+ * so will bring our own interrupts to a shuddering halt).
|
62
|
70
|
*/
|
63
|
71
|
static void undi_hook_isr ( unsigned int irq ) {
|
64
|
72
|
__asm__ __volatile__ ( TEXT16_CODE ( "\nundi_isr:\n\t"
|
65
|
73
|
"incl %%cs:%c0\n\t"
|
66
|
|
- "ljmp *%%cs:%c1\n\t" )
|
67
|
|
- : : "p" ( & __from_text16 ( trigger_count ) ),
|
68
|
|
- "p" ( & __from_text16 ( undi_isr_chain ) ));
|
|
74
|
+ "iret\n\t" )
|
|
75
|
+ : : "p" ( & __from_text16 ( trigger_count ) ) );
|
69
|
76
|
|
70
|
77
|
hook_bios_interrupt ( IRQ_INT ( irq ), ( ( unsigned int ) undi_isr ),
|
71
|
|
- &undi_isr_chain );
|
|
78
|
+ &undi_isr_dummy_chain );
|
72
|
79
|
|
73
|
80
|
}
|
74
|
81
|
|
|
@@ -79,7 +86,7 @@ static void undi_hook_isr ( unsigned int irq ) {
|
79
|
86
|
*/
|
80
|
87
|
static void undi_unhook_isr ( unsigned int irq ) {
|
81
|
88
|
unhook_bios_interrupt ( IRQ_INT ( irq ), ( ( unsigned int ) undi_isr ),
|
82
|
|
- &undi_isr_chain );
|
|
89
|
+ &undi_isr_dummy_chain );
|
83
|
90
|
}
|
84
|
91
|
|
85
|
92
|
/**
|
|
@@ -88,7 +95,7 @@ static void undi_unhook_isr ( unsigned int irq ) {
|
88
|
95
|
* @ret triggered ISR has been triggered since last check
|
89
|
96
|
*/
|
90
|
97
|
static int undi_isr_triggered ( void ) {
|
91
|
|
- static unsigned int last_trigger_count;
|
|
98
|
+ static unsigned int last_trigger_count = 0;
|
92
|
99
|
unsigned int this_trigger_count;
|
93
|
100
|
|
94
|
101
|
/* Read trigger_count. Do this only once; it is volatile */
|
|
@@ -285,8 +292,9 @@ static int undi_open ( struct net_device *netdev ) {
|
285
|
292
|
struct s_PXENV_UNDI_OPEN open;
|
286
|
293
|
int rc;
|
287
|
294
|
|
288
|
|
- /* Hook interrupt service routine */
|
|
295
|
+ /* Hook interrupt service routine and enable interrupt */
|
289
|
296
|
undi_hook_isr ( pxe->irq );
|
|
297
|
+ enable_irq ( pxe->irq );
|
290
|
298
|
|
291
|
299
|
/* Set station address. Required for some PXE stacks; will
|
292
|
300
|
* spuriously fail on others. Ignore failures. We only ever
|
|
@@ -307,10 +315,14 @@ static int undi_open ( struct net_device *netdev ) {
|
307
|
315
|
if ( ( rc = pxe_call ( pxe, PXENV_UNDI_OPEN, &open,
|
308
|
316
|
sizeof ( open ) ) ) != 0 ) {
|
309
|
317
|
DBG ( "UNDI_OPEN failed: %s\n", strerror ( rc ) );
|
310
|
|
- return rc;
|
|
318
|
+ goto err;
|
311
|
319
|
}
|
312
|
320
|
|
313
|
321
|
return 0;
|
|
322
|
+
|
|
323
|
+ err:
|
|
324
|
+ undi_close ( netdev );
|
|
325
|
+ return rc;
|
314
|
326
|
}
|
315
|
327
|
|
316
|
328
|
/**
|
|
@@ -329,7 +341,8 @@ static void undi_close ( struct net_device *netdev ) {
|
329
|
341
|
DBG ( "UNDI_CLOSE failed: %s\n", strerror ( rc ) );
|
330
|
342
|
}
|
331
|
343
|
|
332
|
|
- /* Unhook ISR */
|
|
344
|
+ /* Disable interrupt and unhook ISR */
|
|
345
|
+ disable_irq ( pxe->irq );
|
333
|
346
|
undi_unhook_isr ( pxe->irq );
|
334
|
347
|
}
|
335
|
348
|
|
|
@@ -369,6 +382,7 @@ int undi_probe ( struct pxe_device *pxe ) {
|
369
|
382
|
|
370
|
383
|
err:
|
371
|
384
|
free_netdev ( netdev );
|
|
385
|
+ pxe_set_drvdata ( pxe, NULL );
|
372
|
386
|
return rc;
|
373
|
387
|
}
|
374
|
388
|
|