david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

Remember to enable/disable the interrupt at the PIC.

Handle failures in undi_open() properly.
This commit is contained in:
Michael Brown 2007-01-07 13:31:39 +00:00
parent 9ab5c0dde7
commit 23cb837951
1 changed files with 27 additions and 13 deletions

View File

@ -32,6 +32,8 @@
* *
*/ */
static void undi_close ( struct net_device *netdev );
/***************************************************************************** /*****************************************************************************
* *
* UNDI interrupt service routine * UNDI interrupt service routine
@ -47,28 +49,33 @@
*/ */
extern void undi_isr ( void ); extern void undi_isr ( void );
/** Vector for chaining to other interrupts handlers */ /** Dummy chain vector */
static struct segoff __text16 ( undi_isr_chain ); static struct segoff undi_isr_dummy_chain;
#define undi_isr_chain __use_text16 ( undi_isr_chain )
/** IRQ trigger count */ /** IRQ trigger count */
static volatile uint16_t __text16 ( trigger_count ); static volatile uint16_t __text16 ( trigger_count ) = 0;
#define trigger_count __use_text16 ( trigger_count ) #define trigger_count __use_text16 ( trigger_count )
/** /**
* Hook UNDI interrupt service routine * Hook UNDI interrupt service routine
* *
* @v irq IRQ number * @v irq IRQ number
*
* The UNDI ISR specifically does @b not chain to the previous
* interrupt handler. BIOSes seem to install somewhat perverse
* default interrupt handlers; some do nothing other than an iret (and
* so will cause a screaming interrupt if there really is another
* interrupting device) and some disable the interrupt at the PIC (and
* so will bring our own interrupts to a shuddering halt).
*/ */
static void undi_hook_isr ( unsigned int irq ) { static void undi_hook_isr ( unsigned int irq ) {
__asm__ __volatile__ ( TEXT16_CODE ( "\nundi_isr:\n\t" __asm__ __volatile__ ( TEXT16_CODE ( "\nundi_isr:\n\t"
"incl %%cs:%c0\n\t" "incl %%cs:%c0\n\t"
"ljmp *%%cs:%c1\n\t" ) "iret\n\t" )
: : "p" ( & __from_text16 ( trigger_count ) ), : : "p" ( & __from_text16 ( trigger_count ) ) );
"p" ( & __from_text16 ( undi_isr_chain ) ));
hook_bios_interrupt ( IRQ_INT ( irq ), ( ( unsigned int ) undi_isr ), hook_bios_interrupt ( IRQ_INT ( irq ), ( ( unsigned int ) undi_isr ),
&undi_isr_chain ); &undi_isr_dummy_chain );
} }
@ -79,7 +86,7 @@ static void undi_hook_isr ( unsigned int irq ) {
*/ */
static void undi_unhook_isr ( unsigned int irq ) { static void undi_unhook_isr ( unsigned int irq ) {
unhook_bios_interrupt ( IRQ_INT ( irq ), ( ( unsigned int ) undi_isr ), unhook_bios_interrupt ( IRQ_INT ( irq ), ( ( unsigned int ) undi_isr ),
&undi_isr_chain ); &undi_isr_dummy_chain );
} }
/** /**
@ -88,7 +95,7 @@ static void undi_unhook_isr ( unsigned int irq ) {
* @ret triggered ISR has been triggered since last check * @ret triggered ISR has been triggered since last check
*/ */
static int undi_isr_triggered ( void ) { static int undi_isr_triggered ( void ) {
static unsigned int last_trigger_count; static unsigned int last_trigger_count = 0;
unsigned int this_trigger_count; unsigned int this_trigger_count;
/* Read trigger_count. Do this only once; it is volatile */ /* Read trigger_count. Do this only once; it is volatile */
@ -285,8 +292,9 @@ static int undi_open ( struct net_device *netdev ) {
struct s_PXENV_UNDI_OPEN open; struct s_PXENV_UNDI_OPEN open;
int rc; int rc;
/* Hook interrupt service routine */ /* Hook interrupt service routine and enable interrupt */
undi_hook_isr ( pxe->irq ); undi_hook_isr ( pxe->irq );
enable_irq ( pxe->irq );
/* Set station address. Required for some PXE stacks; will /* Set station address. Required for some PXE stacks; will
* spuriously fail on others. Ignore failures. We only ever * spuriously fail on others. Ignore failures. We only ever
@ -307,10 +315,14 @@ static int undi_open ( struct net_device *netdev ) {
if ( ( rc = pxe_call ( pxe, PXENV_UNDI_OPEN, &open, if ( ( rc = pxe_call ( pxe, PXENV_UNDI_OPEN, &open,
sizeof ( open ) ) ) != 0 ) { sizeof ( open ) ) ) != 0 ) {
DBG ( "UNDI_OPEN failed: %s\n", strerror ( rc ) ); DBG ( "UNDI_OPEN failed: %s\n", strerror ( rc ) );
return rc; goto err;
} }
return 0; return 0;
err:
undi_close ( netdev );
return rc;
} }
/** /**
@ -329,7 +341,8 @@ static void undi_close ( struct net_device *netdev ) {
DBG ( "UNDI_CLOSE failed: %s\n", strerror ( rc ) ); DBG ( "UNDI_CLOSE failed: %s\n", strerror ( rc ) );
} }
/* Unhook ISR */ /* Disable interrupt and unhook ISR */
disable_irq ( pxe->irq );
undi_unhook_isr ( pxe->irq ); undi_unhook_isr ( pxe->irq );
} }
@ -369,6 +382,7 @@ int undi_probe ( struct pxe_device *pxe ) {
err: err:
free_netdev ( netdev ); free_netdev ( netdev );
pxe_set_drvdata ( pxe, NULL );
return rc; return rc;
} }