[PATCH 4.14 078/164] media: rc: sir_ir: detect presence of port
From: Greg Kroah-Hartman
Date: Tue Dec 12 2017 - 07:49:36 EST
4.14-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Young <sean@xxxxxxxx>
commit 30b4e122d71cbec2944a5f8b558b88936ee42f10 upstream.
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@xxxxxxxxx>
Tested-by: Fengguang Wu <fengguang.wu@xxxxxxxxx>
Signed-off-by: Sean Young <sean@xxxxxxxx>
Signed-off-by: Mauro Carvalho Chehab <mchehab@xxxxxxxxxxxxxxxx>
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
---
drivers/media/rc/sir_ir.c | 40 ++++++++++++++++++++++++++++++++++++----
1 file changed, 36 insertions(+), 4 deletions(-)
--- a/drivers/media/rc/sir_ir.c
+++ b/drivers/media/rc/sir_ir.c
@@ -57,7 +57,7 @@ static void add_read_queue(int flag, uns
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_
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;
}