Blob Blame History Raw
From 30b4e122d71cbec2944a5f8b558b88936ee42f10 Mon Sep 17 00:00:00 2001
From: Sean Young <sean@mess.org>
Date: Wed, 8 Nov 2017 16:19:45 -0500
Subject: [PATCH] media: rc: sir_ir: detect presence of port
Git-commit: 30b4e122d71cbec2944a5f8b558b88936ee42f10
Patch-mainline: v4.15-rc3
References: bsc#1051510

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>
Acked-by: Takashi Iwai <tiwai@suse.de>

---
 drivers/media/rc/sir_ir.c |   34 +++++++++++++++++++++++++++++++++-
 1 file changed, 33 insertions(+), 1 deletion(-)

--- a/drivers/media/rc/sir_ir.c
+++ b/drivers/media/rc/sir_ir.c
@@ -290,9 +290,34 @@ static void send_pulse(unsigned long len
 
 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);
@@ -310,6 +335,7 @@ static int 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;
 }
 
@@ -345,6 +371,13 @@ static int init_port(void)
 		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);
 
 	return 0;
@@ -364,7 +397,6 @@ static int init_sir_ir(void)
 	retval = init_port();
 	if (retval < 0)
 		return retval;
-	init_hardware();
 	return 0;
 }