mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-26 13:44:15 +08:00
media: rc: sir_ir: detect presence of port
Without this test, sir_ir clumsy claims resources for a device which does not exist. The 0-day kernel test robot reports the following errors (in a loop): sir_ir sir_ir.0: Trapped in interrupt genirq: Flags mismatch irq 4. 00000000 (ttyS0) vs. 00000000 (sir_ir) When sir_ir is loaded with the default io and irq, the following happens: - sir_ir claims irq 4 - user space opens /dev/ttyS0 - in serial8250_do_startup(), some setup is done for ttyS0, which causes irq 4 to fire (in THRE test) - sir_ir does not realise it was not for it, and spins until the "trapped in interrupt" - now serial driver calls setup_irq() and fails and we get the "Flags mismatch" error. There is no port present at 0x3e8 so simply check for the presence of a port, as suggested by Linus. Reported-by: kbuild test robot <fengguang.wu@intel.com> Tested-by: Fengguang Wu <fengguang.wu@intel.com> Signed-off-by: Sean Young <sean@mess.org> Cc: <stable@vger.kernel.org> # 4.12+ Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
This commit is contained in:
parent
f2ecc3d078
commit
30b4e122d7
@ -57,7 +57,7 @@ static void add_read_queue(int flag, unsigned long val);
|
||||
static irqreturn_t sir_interrupt(int irq, void *dev_id);
|
||||
static void send_space(unsigned long len);
|
||||
static void send_pulse(unsigned long len);
|
||||
static void init_hardware(void);
|
||||
static int init_hardware(void);
|
||||
static void drop_hardware(void);
|
||||
/* Initialisation */
|
||||
|
||||
@ -263,11 +263,36 @@ static void send_pulse(unsigned long len)
|
||||
}
|
||||
}
|
||||
|
||||
static void init_hardware(void)
|
||||
static int init_hardware(void)
|
||||
{
|
||||
u8 scratch, scratch2, scratch3;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&hardware_lock, flags);
|
||||
|
||||
/*
|
||||
* This is a simple port existence test, borrowed from the autoconfig
|
||||
* function in drivers/tty/serial/8250/8250_port.c
|
||||
*/
|
||||
scratch = sinp(UART_IER);
|
||||
soutp(UART_IER, 0);
|
||||
#ifdef __i386__
|
||||
outb(0xff, 0x080);
|
||||
#endif
|
||||
scratch2 = sinp(UART_IER) & 0x0f;
|
||||
soutp(UART_IER, 0x0f);
|
||||
#ifdef __i386__
|
||||
outb(0x00, 0x080);
|
||||
#endif
|
||||
scratch3 = sinp(UART_IER) & 0x0f;
|
||||
soutp(UART_IER, scratch);
|
||||
if (scratch2 != 0 || scratch3 != 0x0f) {
|
||||
/* we fail, there's nothing here */
|
||||
spin_unlock_irqrestore(&hardware_lock, flags);
|
||||
pr_err("port existence test failed, cannot continue\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* reset UART */
|
||||
outb(0, io + UART_MCR);
|
||||
outb(0, io + UART_IER);
|
||||
@ -285,6 +310,8 @@ static void init_hardware(void)
|
||||
/* turn on UART */
|
||||
outb(UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2, io + UART_MCR);
|
||||
spin_unlock_irqrestore(&hardware_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void drop_hardware(void)
|
||||
@ -334,14 +361,19 @@ static int sir_ir_probe(struct platform_device *dev)
|
||||
pr_err("IRQ %d already in use.\n", irq);
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = init_hardware();
|
||||
if (retval) {
|
||||
del_timer_sync(&timerlist);
|
||||
return retval;
|
||||
}
|
||||
|
||||
pr_info("I/O port 0x%.4x, IRQ %d.\n", io, irq);
|
||||
|
||||
retval = devm_rc_register_device(&sir_ir_dev->dev, rcdev);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
init_hardware();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user