* Re: [PATCH 0/4 V2] pch_uart: Cleanups, board quirks, and user uartclk parameter
From: Darren Hart @ 2012-03-01 1:12 UTC (permalink / raw)
To: Tomoya MORINAGA
Cc: Alan Cox, Linux Kernel Mailing List, Feng Tang,
Greg Kroah-Hartman, linux-serial
In-Reply-To: <CANKRQnjQ4WB-ExmM5G5diw1LyA_HwySzkNyhKmf4fDLn0ZO7wg@mail.gmail.com>
On 02/29/2012 05:06 PM, Tomoya MORINAGA wrote:
> 2012年3月1日9:47 Darren Hart <dvhart@linux.intel.com>:
>> how do we force the
>> 192MHz clock setting BEFORE the pch_phub code runs?
>
> To realize your requirement,
> I think that set by BIOS is only way.
Perhaps a pch_phub.bios_uartclk parameter could be used for setting up
the initial console and once the phub driver is loaded, it can make the
necessary changes and fix things up in the uart port? This would add a
binding between the pch_uart and pch_phub code.
Thoughts?
--
Darren Hart
Intel Open Source Technology Center
Yocto Project - Linux Kernel
^ permalink raw reply
* Re: [PATCH 0/4 V2] pch_uart: Cleanups, board quirks, and user uartclk parameter
From: Tomoya MORINAGA @ 2012-03-01 1:06 UTC (permalink / raw)
To: Darren Hart
Cc: Alan Cox, Linux Kernel Mailing List, Feng Tang,
Greg Kroah-Hartman, linux-serial
In-Reply-To: <4F4EC73E.40209@linux.intel.com>
2012年3月1日9:47 Darren Hart <dvhart@linux.intel.com>:
> how do we force the
> 192MHz clock setting BEFORE the pch_phub code runs?
To realize your requirement,
I think that set by BIOS is only way.
ROHM Co., Ltd.
tomoya
^ permalink raw reply
* Re: [PATCH 0/4 V2] pch_uart: Cleanups, board quirks, and user uartclk parameter
From: Greg Kroah-Hartman @ 2012-03-01 0:53 UTC (permalink / raw)
To: Darren Hart
Cc: Tomoya MORINAGA, Alan Cox, Linux Kernel Mailing List, Feng Tang,
linux-serial
In-Reply-To: <4F4EC73E.40209@linux.intel.com>
On Wed, Feb 29, 2012 at 04:47:58PM -0800, Darren Hart wrote:
>
>
> On 02/29/2012 04:38 PM, Tomoya MORINAGA wrote:
> > Hi
> >
> > Darren's patch series, I think OK.
> > However, need to proceed next step.
>
> Great, Greg are you happy with these as they are?
I'll look them over in the next few days and let you know.
thanks,
greg k-h
^ permalink raw reply
* Re: [PATCH 0/4 V2] pch_uart: Cleanups, board quirks, and user uartclk parameter
From: Darren Hart @ 2012-03-01 0:47 UTC (permalink / raw)
To: Tomoya MORINAGA
Cc: Alan Cox, Linux Kernel Mailing List, Feng Tang,
Greg Kroah-Hartman, linux-serial
In-Reply-To: <CANKRQnhFVJAUHAg0qTPFJ8L0L_+-_q4ZtfOnV0wSR1Xa6Mn8UQ@mail.gmail.com>
On 02/29/2012 04:38 PM, Tomoya MORINAGA wrote:
> Hi
>
> Darren's patch series, I think OK.
> However, need to proceed next step.
Great, Greg are you happy with these as they are?
>
> As I said before,
> internal clock(USB_48MHz) should be used than external clock(UART_CLK)
> as default setting.
> --Reasons
> - Users don't always use 1.8432MHz as UART_CLK.
> So, In case of using except 1.8432MHz,
> the user needs to change both pch_phub and pch_uart setting (It's
> bother things).
> - High baud rate can't be used more than 115Kbps
>
> I think there is no merit to use UART_CLK.
>
> ---Merit to use 192MHz
> - Users don't have to care both pch_phub and pch_uart clock setting.
> - High baud rate can be supported
I have no objection to this provided the early serial console can work
without additional patches. So the question now is, how do we force the
192MHz clock setting BEFORE the pch_phub code runs?
--
Darren Hart
Intel Open Source Technology Center
Yocto Project - Linux Kernel
^ permalink raw reply
* Re: [PATCH 0/4 V2] pch_uart: Cleanups, board quirks, and user uartclk parameter
From: Tomoya MORINAGA @ 2012-03-01 0:38 UTC (permalink / raw)
To: Darren Hart, Alan Cox
Cc: Linux Kernel Mailing List, Feng Tang, Greg Kroah-Hartman,
linux-serial
In-Reply-To: <cover.1330539597.git.dvhart@linux.intel.com>
Hi
Darren's patch series, I think OK.
However, need to proceed next step.
As I said before,
internal clock(USB_48MHz) should be used than external clock(UART_CLK)
as default setting.
--Reasons
- Users don't always use 1.8432MHz as UART_CLK.
So, In case of using except 1.8432MHz,
the user needs to change both pch_phub and pch_uart setting (It's
bother things).
- High baud rate can't be used more than 115Kbps
I think there is no merit to use UART_CLK.
---Merit to use 192MHz
- Users don't have to care both pch_phub and pch_uart clock setting.
- High baud rate can be supported
thanks,
ROHM Co., Ltd.
tomoya
^ permalink raw reply
* [PATCH 4/4] pch_uart: Use existing default_baud in setup_console
From: Darren Hart @ 2012-02-29 18:24 UTC (permalink / raw)
To: Linux Kernel Mailing List
Cc: Darren Hart, Tomoya MORINAGA, Feng Tang, Greg Kroah-Hartman,
Alan Cox, linux-serial
In-Reply-To: <cover.1330539597.git.dvhart@linux.intel.com>
Rather than hardcode 9600, use the existing default_baud parameter (which
also defaults to 9600).
Signed-off-by: Darren Hart <dvhart@linux.intel.com>
CC: Tomoya MORINAGA <tomoya.rohm@gmail.com>
CC: Feng Tang <feng.tang@intel.com>
CC: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
CC: Alan Cox <alan@linux.intel.com>
CC: linux-serial@vger.kernel.org
---
drivers/tty/serial/pch_uart.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
index 105d982..21af194b 100644
--- a/drivers/tty/serial/pch_uart.c
+++ b/drivers/tty/serial/pch_uart.c
@@ -1516,7 +1516,7 @@ pch_console_write(struct console *co, const char *s, unsigned int count)
static int __init pch_console_setup(struct console *co, char *options)
{
struct uart_port *port;
- int baud = 9600;
+ int baud = default_baud;
int bits = 8;
int parity = 'n';
int flow = 'n';
--
1.7.6.5
^ permalink raw reply related
* [PATCH 1/4] pch_uart: Use uartclk instead of base_baud
From: Darren Hart @ 2012-02-29 18:24 UTC (permalink / raw)
To: Linux Kernel Mailing List
Cc: Darren Hart, Tomoya MORINAGA, Feng Tang, Greg Kroah-Hartman,
Alan Cox, linux-serial
In-Reply-To: <cover.1330539597.git.dvhart@linux.intel.com>
The term "base baud" refers to the fastest baud rate the device can communicate
at. This is clock/16. pch_uart is using base_baud as the clock itself. Rename
the variables to be semantically correct.
Signed-off-by: Darren Hart <dvhart@linux.intel.com>
CC: Tomoya MORINAGA <tomoya.rohm@gmail.com>
CC: Feng Tang <feng.tang@intel.com>
CC: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
CC: Alan Cox <alan@linux.intel.com>
CC: linux-serial@vger.kernel.org
---
drivers/tty/serial/pch_uart.c | 24 ++++++++++++------------
1 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
index 17ae657..c565817 100644
--- a/drivers/tty/serial/pch_uart.c
+++ b/drivers/tty/serial/pch_uart.c
@@ -203,7 +203,7 @@ enum {
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
-#define DEFAULT_BAUD_RATE 1843200 /* 1.8432MHz */
+#define DEFAULT_UARTCLK 1843200 /* 1.8432MHz */
struct pch_uart_buffer {
unsigned char *buf;
@@ -218,7 +218,7 @@ struct eg20t_port {
unsigned int iobase;
struct pci_dev *pdev;
int fifo_size;
- int base_baud;
+ int uartclk;
int start_tx;
int start_rx;
int tx_empty;
@@ -293,7 +293,7 @@ static const int trigger_level_16[4] = { 1, 4, 8, 14 };
static const int trigger_level_1[4] = { 1, 1, 1, 1 };
static void pch_uart_hal_request(struct pci_dev *pdev, int fifosize,
- int base_baud)
+ int uartclk)
{
struct eg20t_port *priv = pci_get_drvdata(pdev);
@@ -332,7 +332,7 @@ static int pch_uart_hal_set_line(struct eg20t_port *priv, int baud,
unsigned int dll, dlm, lcr;
int div;
- div = DIV_ROUND_CLOSEST(priv->base_baud / 16, baud);
+ div = DIV_ROUND_CLOSEST(priv->uartclk / 16, baud);
if (div < 0 || USHRT_MAX <= div) {
dev_err(priv->port.dev, "Invalid Baud(div=0x%x)\n", div);
return -EINVAL;
@@ -1153,9 +1153,9 @@ static int pch_uart_startup(struct uart_port *port)
priv->tx_empty = 1;
if (port->uartclk)
- priv->base_baud = port->uartclk;
+ priv->uartclk = port->uartclk;
else
- port->uartclk = priv->base_baud;
+ port->uartclk = priv->uartclk;
pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_ALL_INT);
ret = pch_uart_hal_set_line(priv, default_baud,
@@ -1507,7 +1507,7 @@ static int __init pch_console_setup(struct console *co, char *options)
return -ENODEV;
/* setup uartclock */
- port->uartclk = DEFAULT_BAUD_RATE;
+ port->uartclk = DEFAULT_UARTCLK;
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
@@ -1550,7 +1550,7 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev,
unsigned int iobase;
unsigned int mapbase;
unsigned char *rxbuf;
- int fifosize, base_baud;
+ int fifosize, uartclk;
int port_type;
struct pch_uart_driver_data *board;
const char *board_name;
@@ -1566,12 +1566,12 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev,
if (!rxbuf)
goto init_port_free_txbuf;
- base_baud = DEFAULT_BAUD_RATE;
+ uartclk = DEFAULT_UARTCLK;
/* quirk for CM-iTC board */
board_name = dmi_get_system_info(DMI_BOARD_NAME);
if (board_name && strstr(board_name, "CM-iTC"))
- base_baud = 192000000; /* 192.0MHz */
+ uartclk = 192000000; /* 192.0MHz */
switch (port_type) {
case PORT_UNKNOWN:
@@ -1597,7 +1597,7 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev,
priv->rxbuf.size = PAGE_SIZE;
priv->fifo_size = fifosize;
- priv->base_baud = base_baud;
+ priv->uartclk = uartclk;
priv->port_type = PORT_MAX_8250 + port_type + 1;
priv->port.dev = &pdev->dev;
priv->port.iobase = iobase;
@@ -1614,7 +1614,7 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev,
spin_lock_init(&priv->port.lock);
pci_set_drvdata(pdev, priv);
- pch_uart_hal_request(pdev, fifosize, base_baud);
+ pch_uart_hal_request(pdev, fifosize, uartclk);
#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE
pch_uart_ports[board->line_no] = priv;
--
1.7.6.5
^ permalink raw reply related
* [PATCH 3/4] pch_uart: Add user_uartclk parameter
From: Darren Hart @ 2012-02-29 18:24 UTC (permalink / raw)
To: Linux Kernel Mailing List
Cc: Darren Hart, Tomoya MORINAGA, Feng Tang, Greg Kroah-Hartman,
Alan Cox, linux-serial
In-Reply-To: <cover.1330539597.git.dvhart@linux.intel.com>
For cases where boards with non-default clocks are not yet added to the kernel
or when the clock varies across hardware revisions, it is useful to be
able to specify the UART clock on the kernel command line.
Add the user_uartclk parameter and prefer it, if set, to the default and
board specific UART clock settings. Specify user_uartclock on the command-line
with "pch_uart.user_uartclk=48000000".
Signed-off-by: Darren Hart <dvhart@linux.intel.com>
CC: Tomoya MORINAGA <tomoya.rohm@gmail.com>
CC: Feng Tang <feng.tang@intel.com>
CC: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
CC: Alan Cox <alan@linux.intel.com>
CC: linux-serial@vger.kernel.org
---
drivers/tty/serial/pch_uart.c | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
index 3a2b0ae..105d982 100644
--- a/drivers/tty/serial/pch_uart.c
+++ b/drivers/tty/serial/pch_uart.c
@@ -290,6 +290,7 @@ static struct pch_uart_driver_data drv_dat[] = {
static struct eg20t_port *pch_uart_ports[PCH_UART_NR];
#endif
static unsigned int default_baud = 9600;
+static unsigned int user_uartclk = 0;
static const int trigger_level_256[4] = { 1, 64, 128, 224 };
static const int trigger_level_64[4] = { 1, 16, 32, 56 };
static const int trigger_level_16[4] = { 1, 4, 8, 14 };
@@ -300,6 +301,9 @@ static int pch_uart_get_uartclk(void)
{
const char *cmp;
+ if (user_uartclk)
+ return user_uartclk;
+
cmp = dmi_get_system_info(DMI_BOARD_NAME);
if (cmp && strstr(cmp, "CM-iTC"))
return CMITC_UARTCLK;
@@ -1799,3 +1803,4 @@ module_exit(pch_uart_module_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel EG20T PCH UART PCI Driver");
module_param(default_baud, uint, S_IRUGO);
+module_param(user_uartclk, uint, S_IRUGO);
--
1.7.6.5
^ permalink raw reply related
* [PATCH 2/4] pch_uart: Add Fish River Island II uart clock quirks
From: Darren Hart @ 2012-02-29 18:24 UTC (permalink / raw)
To: Linux Kernel Mailing List
Cc: Darren Hart, Tomoya MORINAGA, Feng Tang, Greg Kroah-Hartman,
Alan Cox, linux-serial
In-Reply-To: <cover.1330539597.git.dvhart@linux.intel.com>
Add support for the Fish River Island II (FRI2) UART clock following the CM-iTC
quirk handling mechanism. Depending on the firmware installed on the device, the
FRI2 uses a 48MHz or a 64MHz UART clock. This is detected with DMI strings.
Add similar UART clock quirk handling to the pch_console_setup() function to
enable kernel messages on boards with non-standard UART clocks.
Per Alan's suggestion, abstract out UART clock selection into
pch_uart_get_uartclk() to avoid code duplication.
Signed-off-by: Darren Hart <dvhart@linux.intel.com>
CC: Tomoya MORINAGA <tomoya.rohm@gmail.com>
CC: Feng Tang <feng.tang@intel.com>
CC: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
CC: Alan Cox <alan@linux.intel.com>
CC: linux-serial@vger.kernel.org
---
drivers/tty/serial/pch_uart.c | 42 +++++++++++++++++++++++++++-------------
1 files changed, 28 insertions(+), 14 deletions(-)
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
index c565817..3a2b0ae 100644
--- a/drivers/tty/serial/pch_uart.c
+++ b/drivers/tty/serial/pch_uart.c
@@ -203,7 +203,10 @@ enum {
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
-#define DEFAULT_UARTCLK 1843200 /* 1.8432MHz */
+#define DEFAULT_UARTCLK 1843200 /* 1.8432 MHz */
+#define CMITC_UARTCLK 192000000 /* 192.0000 MHz */
+#define FRI2_64_UARTCLK 64000000 /* 64.0000 MHz */
+#define FRI2_48_UARTCLK 48000000 /* 48.0000 MHz */
struct pch_uart_buffer {
unsigned char *buf;
@@ -292,6 +295,26 @@ static const int trigger_level_64[4] = { 1, 16, 32, 56 };
static const int trigger_level_16[4] = { 1, 4, 8, 14 };
static const int trigger_level_1[4] = { 1, 1, 1, 1 };
+/* Return UART clock, checking for board specific clocks. */
+static int pch_uart_get_uartclk(void)
+{
+ const char *cmp;
+
+ cmp = dmi_get_system_info(DMI_BOARD_NAME);
+ if (cmp && strstr(cmp, "CM-iTC"))
+ return CMITC_UARTCLK;
+
+ cmp = dmi_get_system_info(DMI_BIOS_VERSION);
+ if (cmp && strnstr(cmp, "FRI2", 4))
+ return FRI2_64_UARTCLK;
+
+ cmp = dmi_get_system_info(DMI_PRODUCT_NAME);
+ if (cmp && strstr(cmp, "Fish River Island II"))
+ return FRI2_48_UARTCLK;
+
+ return DEFAULT_UARTCLK;
+}
+
static void pch_uart_hal_request(struct pci_dev *pdev, int fifosize,
int uartclk)
{
@@ -1506,8 +1529,7 @@ static int __init pch_console_setup(struct console *co, char *options)
if (!port || (!port->iobase && !port->membase))
return -ENODEV;
- /* setup uartclock */
- port->uartclk = DEFAULT_UARTCLK;
+ port->uartclk = pch_uart_get_uartclk();
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
@@ -1550,10 +1572,9 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev,
unsigned int iobase;
unsigned int mapbase;
unsigned char *rxbuf;
- int fifosize, uartclk;
+ int fifosize;
int port_type;
struct pch_uart_driver_data *board;
- const char *board_name;
board = &drv_dat[id->driver_data];
port_type = board->port_type;
@@ -1566,13 +1587,6 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev,
if (!rxbuf)
goto init_port_free_txbuf;
- uartclk = DEFAULT_UARTCLK;
-
- /* quirk for CM-iTC board */
- board_name = dmi_get_system_info(DMI_BOARD_NAME);
- if (board_name && strstr(board_name, "CM-iTC"))
- uartclk = 192000000; /* 192.0MHz */
-
switch (port_type) {
case PORT_UNKNOWN:
fifosize = 256; /* EG20T/ML7213: UART0 */
@@ -1597,7 +1611,7 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev,
priv->rxbuf.size = PAGE_SIZE;
priv->fifo_size = fifosize;
- priv->uartclk = uartclk;
+ priv->uartclk = pch_uart_get_uartclk();
priv->port_type = PORT_MAX_8250 + port_type + 1;
priv->port.dev = &pdev->dev;
priv->port.iobase = iobase;
@@ -1614,7 +1628,7 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev,
spin_lock_init(&priv->port.lock);
pci_set_drvdata(pdev, priv);
- pch_uart_hal_request(pdev, fifosize, uartclk);
+ pch_uart_hal_request(pdev, fifosize, priv->uartclk);
#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE
pch_uart_ports[board->line_no] = priv;
--
1.7.6.5
^ permalink raw reply related
* [PATCH 0/4 V2] pch_uart: Cleanups, board quirks, and user uartclk parameter
From: Darren Hart @ 2012-02-29 18:24 UTC (permalink / raw)
To: Linux Kernel Mailing List
Cc: Tomoya MORINAGA, Feng Tang, Greg Kroah-Hartman, Alan Cox,
linux-serial, Darren Hart
This series does some minor clean-up to the pch_uart driver, adds support
for the Fish River Island II UART clock quirks, and introduces a user_uartclk
parameter to aid in developing for early and changing hardware.
In order to support an early serial console, we cannot rely on the pch_phub
probe function to setup the CFGCLK register. This series relies on the board
quirks and does not force the setting of the CLKREG in the pch_phub code.
Instead, it aligns with what is the default configuration (defined by firmware)
for a given board. The user_uartclk parameter provides a mechanism to force a
specific uartclk if necessary.
I looked at the PCI code briefly to see about forcing the clock to 192MHz on
all boards early on to avoid having to use quirks, but it was not clear to me
if this could be done before PCI was initialized as the IO base is derived
from pci_iomap(pdev...). Perhaps this can be done, and I would be happy to
revisit this as a follow-up patch. As this current patch series follows existing
precedent to support a specific board and it does not impact the existing boards
or the default behavior, I'd like to see this merged as is, rather than hold out
for a much more invasive change forcing the clock to 192MHz. Is this acceptable?
V2: Incorporates Alan Cox's feedback into 2/4: Add Fish River Island II uart
clock quirks, refactoring the clock quirks into a new function and using
a more appropriate name for a reused string variable.
Add support for the two firmware variants for the FRI2.
--
Darren
The following changes since commit 164974a8f2a482f1abcb027c6d1a89dd79b14297:
ecryptfs: fix printk format warning for size_t (2012-02-28 16:55:30 -0800)
are available in the git repository at:
git://git.infradead.org/users/dvhart/linux-2.6.git pch_uart_v2
http://git.infradead.org/users/dvhart/linux-2.6.git/shortlog/refs/heads/pch_uart_v2
Darren Hart (4):
pch_uart: Use uartclk instead of base_baud
pch_uart: Add Fish River Island II uart clock quirks
pch_uart: Add user_uartclk parameter
pch_uart: Use existing default_baud in setup_console
drivers/tty/serial/pch_uart.c | 59 +++++++++++++++++++++++++++--------------
1 files changed, 39 insertions(+), 20 deletions(-)
--
1.7.6.5
^ permalink raw reply
* Re: [PATCH] Clear previous interrupts after fifo is disabled
From: Chanho Min @ 2012-02-29 2:47 UTC (permalink / raw)
To: Russell King - ARM Linux
Cc: Alan Cox, Greg Kroah-Hartman, Linus Walleij, Shreshtha Kumar Sahu,
Kim, Jong-Sung, linux-kernel, linux-serial
In-Reply-To: <20120228102337.GF18045@n2100.arm.linux.org.uk>
> Which is why my patch explicitly clears the receive interrupt status
> before requesting the interrupt. Have you read my patch?
This is the hang-up scenario with your patch.
pl011_startup(struct uart_port *port)
uap->port.uartclk = clk_get_rate(uap->clk);
/* Clear pending error and receive interrupts */
writew(UART011_OEIS | UART011_BEIS | UART011_PEIS | UART011_FEIS |
UART011_RTIS | UART011_RXIS, uap->port.membase + UART011_ICR);
...
1. RX Interrupt is occurred after interrupt is cleared. Even if
interrupt is masked/disabled before(or probe time), RIS's Rx interrupt
is set to 1. Of course, masked status is zero.
...
2. RXFE of flag register is zero and fifo is not empty before LCRH is cleared.
writew(0, uap->port.membase + uap->lcrh_rx);
3. RXFE of flag register is changed to '1' after LCRH is cleared. but
the fifo is not actually empty.
...
4. Finally, We enable interrupts.
spin_lock_irq(&uap->port.lock);
uap->im = UART011_RTIM;
if (!pl011_dma_rx_running(uap))
uap->im |= UART011_RXIM;
writew(uap->im, uap->port.membase + UART011_IMSC);
spin_unlock_irq(&uap->port.lock);
5. The RIS's field which is enabled by IMSC is reflected to MIS as
soon as the interrupt enable. (We checked this on our ARM platform )
6. IRQ context is started. pl011_fifo_to_tty is called by pl011_int.
static int pl011_fifo_to_tty(struct uart_amba_port *uap)
...
while (max_count--) {
status = readw(uap->port.membase + UART01x_FR);
if (status & UART01x_FR_RXFE)
break;
...
7. pl011_fifo_to_tty can't read any data from DR because of the break
condition for RXFE. Rx interrupt can't be cleared. cpu is looping in
irq context.
This is why we need to be cleared just after LCRH is cleared not
before irq_request. Let's get back to my patch. Even if data is
received before or after interrupt is cleared, flag register will show
actual fifo status. Interrupt handler runs normally after the uart
operation is started up by enabling interrupt.
+ spin_lock_irq(&uap->port.lock);
writew(0, uap->port.membase + uap->lcrh_rx);
+ /* Clear pending error and receive interrupts */
+ writew(UART011_OEIS | UART011_BEIS | UART011_PEIS | UART011_FEIS |
+ UART011_RTIS | UART011_RXIS, uap->port.membase + UART011_ICR);
+ spin_unlock_irq(&uap->port.lock);
Thanks,
Chanho Min
--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] tty: serial: OMAP: Fix oops due to NULL pdata in DT boot
From: Kevin Hilman @ 2012-02-28 18:23 UTC (permalink / raw)
To: Greg KH
Cc: Cousson, Benoit, linux-serial, Alan Cox, Paul Walmsley,
linux-omap, linux-arm-kernel, Govindraj.R
In-Reply-To: <20120228172851.GA13653@kroah.com>
Greg KH <gregkh@linuxfoundation.org> writes:
> On Tue, Feb 28, 2012 at 06:22:12PM +0100, Cousson, Benoit wrote:
>> The following commit: be4b0281956c5cae4f63f31f11d07625a6988766
>> (tty: serial: OMAP: block idle while the UART is transferring data in PIO mode),
>> is introducing an oops if OMAP is booted using device tree blob because
>> the pdata will not be initialized.
>
> Is that something that happens today on systems? Does this need to go
> in for 3.3-final, or can it wait for 3.4?
It is needed for 3.3-final.
Thanks,
Kevin
^ permalink raw reply
* Re: [PATCH] tty: serial: OMAP: Fix oops due to NULL pdata in DT boot
From: Greg KH @ 2012-02-28 17:28 UTC (permalink / raw)
To: Cousson, Benoit
Cc: linux-serial, Alan Cox, Paul Walmsley, linux-omap,
linux-arm-kernel, Kevin Hilman, Govindraj.R
In-Reply-To: <4F4D0D44.3010604@ti.com>
On Tue, Feb 28, 2012 at 06:22:12PM +0100, Cousson, Benoit wrote:
> The following commit: be4b0281956c5cae4f63f31f11d07625a6988766
> (tty: serial: OMAP: block idle while the UART is transferring data in PIO mode),
> is introducing an oops if OMAP is booted using device tree blob because
> the pdata will not be initialized.
Is that something that happens today on systems? Does this need to go
in for 3.3-final, or can it wait for 3.4?
thanks,
greg k-h
^ permalink raw reply
* [PATCH] tty: serial: OMAP: Fix oops due to NULL pdata in DT boot
From: Cousson, Benoit @ 2012-02-28 17:22 UTC (permalink / raw)
To: linux-serial, Alan Cox, gregkh
Cc: Paul Walmsley, linux-omap, linux-arm-kernel, Kevin Hilman,
Govindraj.R
In-Reply-To: <20120126025036.31613.67819.stgit@dusk>
The following commit: be4b0281956c5cae4f63f31f11d07625a6988766
(tty: serial: OMAP: block idle while the UART is transferring data in PIO mode),
is introducing an oops if OMAP is booted using device tree blob because
the pdata will not be initialized.
Check if pdata is set before de-referencing it.
Signed-off-by: Benoit Cousson <b-cousson@ti.com>
Reviewed-by: Paul Walmsley <paul@pwsan.com>
---
drivers/tty/serial/omap-serial.c | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index f809041..0121486 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -159,7 +159,7 @@ static void serial_omap_stop_tx(struct uart_port *port)
serial_out(up, UART_IER, up->ier);
}
- if (!up->use_dma && pdata->set_forceidle)
+ if (!up->use_dma && pdata && pdata->set_forceidle)
pdata->set_forceidle(up->pdev);
pm_runtime_mark_last_busy(&up->pdev->dev);
@@ -298,7 +298,7 @@ static void serial_omap_start_tx(struct uart_port *port)
if (!up->use_dma) {
pm_runtime_get_sync(&up->pdev->dev);
serial_omap_enable_ier_thri(up);
- if (pdata->set_noidle)
+ if (pdata && pdata->set_noidle)
pdata->set_noidle(up->pdev);
pm_runtime_mark_last_busy(&up->pdev->dev);
pm_runtime_put_autosuspend(&up->pdev->dev);
@@ -1613,7 +1613,7 @@ static int serial_omap_runtime_resume(struct device *dev)
struct uart_omap_port *up = dev_get_drvdata(dev);
struct omap_uart_port_info *pdata = dev->platform_data;
- if (up) {
+ if (up && pdata) {
if (pdata->get_context_loss_count) {
u32 loss_cnt = pdata->get_context_loss_count(dev);
--
1.7.0.4
^ permalink raw reply related
* Re: [PATCH 2/3] tty: serial: OMAP: block idle while the UART is transferring data in PIO mode
From: Paul Walmsley @ 2012-02-28 17:13 UTC (permalink / raw)
To: Cousson, Benoit
Cc: linux-omap, linux-serial, linux-arm-kernel, Kevin Hilman,
Govindraj.R, Greg Kroah-Hartman, Alan Cox, Tomi Valkeinen,
Nayak, Rajendra
In-Reply-To: <4F47A040.6030502@ti.com>
Hi
On Fri, 24 Feb 2012, Cousson, Benoit wrote:
> On 2/21/2012 11:02 PM, Paul Walmsley wrote:
>
> > Looks fine to me, although I'd suggest fixing the DMA workaround path as
> > well.
>
> Mmm, I've tried to fix every instances of pdata in the code, you think I
> missed some?
>
> I'll check again.
>
> > Reviewed-by: Paul Walmsley<paul@pwsan.com>
I just doublechecked the code paths and looks like you covered them all.
Sorry about the confusion.
Reviewed-by: Paul Walmsley <paul@pwsan.com>
- Paul
^ permalink raw reply
* Re: [PATCH] Clear previous interrupts after fifo is disabled
From: Russell King - ARM Linux @ 2012-02-28 10:23 UTC (permalink / raw)
To: Chanho Min
Cc: Alan Cox, Greg Kroah-Hartman, Linus Walleij, Shreshtha Kumar Sahu,
Kim, Jong-Sung, linux-kernel, linux-serial
In-Reply-To: <CAOAMb1Cqfrk-b-yd1CMt2Hac7_j3i6WL8D8yQJDWjg_S18dAsw@mail.gmail.com>
On Tue, Feb 28, 2012 at 06:46:12PM +0900, Chanho Min wrote:
> > Because the flags are manipulated to give the illusion of a one byte
> > FIFO, as stated in the TRM.
> Yes. It is the problem that rx interrupt is pended with this status as
> I mentioned.
Which is why my patch explicitly clears the receive interrupt status
before requesting the interrupt. Have you read my patch?
> > And we don't set the mask register to 1 until later.
> In the last part of startup, set to 1. Interrupt can be occurred just
> after it.
>
> uap->im = UART011_RTIM;
> if (!pl011_dma_rx_running(uap))
> uap->im |= UART011_RXIM;
> writew(uap->im, uap->port.membase + UART011_IMSC);
>
> > But we want to do the transmit interrupt provocation with the FIFO disabled.
> I know. It's test only.
Wrong, it's fundamental to the UARTs operation.
^ permalink raw reply
* Re: [PATCH] Clear previous interrupts after fifo is disabled
From: Chanho Min @ 2012-02-28 9:46 UTC (permalink / raw)
To: Russell King - ARM Linux
Cc: Alan Cox, Greg Kroah-Hartman, Linus Walleij, Shreshtha Kumar Sahu,
Kim, Jong-Sung, linux-kernel, linux-serial
In-Reply-To: <20120228092140.GD18045@n2100.arm.linux.org.uk>
> Because the flags are manipulated to give the illusion of a one byte
> FIFO, as stated in the TRM.
Yes. It is the problem that rx interrupt is pended with this status as
I mentioned.
> And we don't set the mask register to 1 until later.
In the last part of startup, set to 1. Interrupt can be occurred just after it.
uap->im = UART011_RTIM;
if (!pl011_dma_rx_running(uap))
uap->im |= UART011_RXIM;
writew(uap->im, uap->port.membase + UART011_IMSC);
> But we want to do the transmit interrupt provocation with the FIFO disabled.
I know. It's test only.
^ permalink raw reply
* Re: [PATCH] Clear previous interrupts after fifo is disabled
From: Russell King - ARM Linux @ 2012-02-28 9:21 UTC (permalink / raw)
To: Chanho Min
Cc: Alan Cox, Greg Kroah-Hartman, Linus Walleij, Shreshtha Kumar Sahu,
Kim, Jong-Sung, linux-kernel, linux-serial
In-Reply-To: <CAOAMb1CJ=G45G3JsxwP23GSdEOnDmY5xVoQmWLXaKJJv2cLOLg@mail.gmail.com>
On Tue, Feb 28, 2012 at 06:16:10PM +0900, Chanho Min wrote:
> > RXFE _will_ be set. Think about it - RXFE means Receive Fifo Empty.
> > If the receive fifo is empty, it _will_ be set.
> I know meaning of the RXFE. I also don't understand why RXFE is set by
> clearing FEN. We checked this by bellow debug codes.
>
> fr_before = readw(uap->port.membase + UART01x_FR);
> writew(0, uap->port.membase + uap->lcrh_rx);
> fr_after = readw(uap->port.membase + UART01x_FR);
>
> If rx interrupt is ocurred before, fr_after becomes 0x90 but fr_before is 0x80.
Because the flags are manipulated to give the illusion of a one byte
FIFO, as stated in the TRM.
> > And RIS is the _Raw_ interrupt status. That's the status _before_ the
> > mask is acted upon.
> >
> > But it won't be delivered because the mask register is zero.
> It can be delivered just after mask register is set to 1.
And we don't set the mask register to 1 until later.
> >> Root cause is that Rx interrupt set but Rx fifo is empty. If we just
> >> remove the sentence for clearing LCRH, nothing happens and interrupt
> >> handler don't this misbehave.
> >
> > No.
> When we just removed the sentence for clearing LCRH, this hangup doesn't
> happen.
But we want to do the transmit interrupt provocation with the FIFO disabled.
^ permalink raw reply
* Re: [PATCH] Clear previous interrupts after fifo is disabled
From: Chanho Min @ 2012-02-28 9:16 UTC (permalink / raw)
To: Russell King - ARM Linux
Cc: Alan Cox, Greg Kroah-Hartman, Linus Walleij, Shreshtha Kumar Sahu,
Kim, Jong-Sung, linux-kernel, linux-serial
In-Reply-To: <20120228083545.GA18045@n2100.arm.linux.org.uk>
> RXFE _will_ be set. Think about it - RXFE means Receive Fifo Empty.
> If the receive fifo is empty, it _will_ be set.
I know meaning of the RXFE. I also don't understand why RXFE is set by
clearing FEN. We checked this by bellow debug codes.
fr_before = readw(uap->port.membase + UART01x_FR);
writew(0, uap->port.membase + uap->lcrh_rx);
fr_after = readw(uap->port.membase + UART01x_FR);
If rx interrupt is ocurred before, fr_after becomes 0x90 but fr_before is 0x80.
> And RIS is the _Raw_ interrupt status. That's the status _before_ the
> mask is acted upon.
>
> But it won't be delivered because the mask register is zero.
It can be delivered just after mask register is set to 1.
>> Root cause is that Rx interrupt set but Rx fifo is empty. If we just
>> remove the sentence for clearing LCRH, nothing happens and interrupt
>> handler don't this misbehave.
>
> No.
When we just removed the sentence for clearing LCRH, this hangup doesn't happen.
--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] Clear previous interrupts after fifo is disabled
From: Russell King - ARM Linux @ 2012-02-28 8:35 UTC (permalink / raw)
To: Chanho Min
Cc: Alan Cox, Greg Kroah-Hartman, Linus Walleij, Shreshtha Kumar Sahu,
Kim, Jong-Sung, linux-kernel, linux-serial
In-Reply-To: <CAOAMb1BjC2bF=rpJAhRUrB_U2mp8HGWxfcp+=TZ1etWzjUEwig@mail.gmail.com>
On Tue, Feb 28, 2012 at 10:35:30AM +0900, Chanho Min wrote:
> On Mon, Feb 27, 2012 at 8:02 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> > On Mon, Feb 27, 2012 at 10:48:58AM +0000, Russell King - ARM Linux wrote:
> >> I'd much prefer to only clear those interrupts which actually need to be
> >> cleared at this point. So, I'd suggest this approach instead:
> >
> > Thinking about this a little more, we definitely want to mask and clear
> > interrupts at probe time as well:
> >
> I'm not satisfied with this completely. RIS has some pending
> interrupts even if interrupts are masked/disabled in IMSC. If your
> patch is applied, interrupt can be pended as bellows and RXFE of the
> flag register is set as well.
RXFE _will_ be set. Think about it - RXFE means Receive Fifo Empty.
If the receive fifo is empty, it _will_ be set.
And RIS is the _Raw_ interrupt status. That's the status _before_ the
mask is acted upon.
> writew(UART011_OEIS | UART011_BEIS | UART011_PEIS | UART011_FEIS |
> UART011_RTIS | UART011_RXIS, uap->port.membase + UART011_ICR);
> ...
> Interrupt is occured and pended in RIS
But it won't be delivered because the mask register is zero.
> ..
> writew(uap->im, uap->port.membase + UART011_IMSC);
>
> Root cause is that Rx interrupt set but Rx fifo is empty. If we just
> remove the sentence for clearing LCRH, nothing happens and interrupt
> handler don't this misbehave.
No.
--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] Clear previous interrupts after fifo is disabled
From: Chanho Min @ 2012-02-28 1:35 UTC (permalink / raw)
To: Russell King - ARM Linux
Cc: Alan Cox, Greg Kroah-Hartman, Linus Walleij, Shreshtha Kumar Sahu,
Kim, Jong-Sung, linux-kernel, linux-serial
In-Reply-To: <20120227110204.GA30199@n2100.arm.linux.org.uk>
On Mon, Feb 27, 2012 at 8:02 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Mon, Feb 27, 2012 at 10:48:58AM +0000, Russell King - ARM Linux wrote:
>> I'd much prefer to only clear those interrupts which actually need to be
>> cleared at this point. So, I'd suggest this approach instead:
>
> Thinking about this a little more, we definitely want to mask and clear
> interrupts at probe time as well:
>
I'm not satisfied with this completely. RIS has some pending
interrupts even if interrupts are masked/disabled in IMSC. If your
patch is applied, interrupt can be pended as bellows and RXFE of the
flag register is set as well.
writew(UART011_OEIS | UART011_BEIS | UART011_PEIS | UART011_FEIS |
UART011_RTIS | UART011_RXIS, uap->port.membase + UART011_ICR);
...
Interrupt is occured and pended in RIS
..
writew(uap->im, uap->port.membase + UART011_IMSC);
Root cause is that Rx interrupt set but Rx fifo is empty. If we just
remove the sentence for clearing LCRH, nothing happens and interrupt
handler don't this misbehave. Also I don't fully understand why we
need to clear interrupts at probe time. If we prefer to only clear
those interrupts which actually need to be cleared, this is the
improved patch.
Thanks,
Signed-off-by: Chanho Min <chanho.min@lge.com>
---
drivers/tty/serial/amba-pl011.c | 9 +++++----
1 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 6800f5f..96d1828 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -1397,7 +1397,12 @@ static int pl011_startup(struct uart_port *port)
writew(cr, uap->port.membase + UART011_CR);
writew(0, uap->port.membase + UART011_FBRD);
writew(1, uap->port.membase + UART011_IBRD);
+ spin_lock_irq(&uap->port.lock);
writew(0, uap->port.membase + uap->lcrh_rx);
+ /* Clear pending error and receive interrupts */
+ writew(UART011_OEIS | UART011_BEIS | UART011_PEIS | UART011_FEIS |
+ UART011_RTIS | UART011_RXIS, uap->port.membase + UART011_ICR);
+ spin_unlock_irq(&uap->port.lock);
if (uap->lcrh_tx != uap->lcrh_rx) {
int i;
/*
@@ -1417,10 +1422,6 @@ static int pl011_startup(struct uart_port *port)
cr |= UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE;
writew(cr, uap->port.membase + UART011_CR);
- /* Clear pending error interrupts */
- writew(UART011_OEIS | UART011_BEIS | UART011_PEIS | UART011_FEIS,
- uap->port.membase + UART011_ICR);
-
/*
* initialise the old status of the modem signals
*/
--
1.7.0.4
^ permalink raw reply related
* Re: [RFC PATCH] vt:tackle kbd_table
From: Greg KH @ 2012-02-28 1:11 UTC (permalink / raw)
To: Alan Cox; +Cc: linux-kernel, linux-serial
In-Reply-To: <20120227160739.8711.95344.stgit@bob.linux.org.uk>
On Mon, Feb 27, 2012 at 04:10:23PM +0000, Alan Cox wrote:
> This could do with some extra eyes and double checking. With this one out of
> the day we can start to make a dent in the console locking logic and thus
> eventually into tty_lock().
>
> From: Alan Cox <alan@linux.intel.com>
>
> Keyboard struct lifetime is easy, but the locking is not and is completely
> ignored by the existing code. Tackle this one head on
>
> - Make the kbd_table private so we can run down all direct users
> - Hoick the relevant ioctl handlers into the keyboard layer
> - Lock them with the keyboard lock so they don't change mid keypress
> - Add helpers for things like console stop/start so we isolate the poking
> around properly
> - Tweak the braille console so it still builds
The drivers/staging/speakup/main.c code also would need to be tweaked in
this same manner, but that's not a big deal.
Other than that, at first glance, this looks good, nice job. Care to
make the speakup change and resend it so we can try it out in
linux-next?
greg k-h
^ permalink raw reply
* Re: USB Smart Card reader
From: Greg KH @ 2012-02-27 20:36 UTC (permalink / raw)
To: James Courtier-Dutton; +Cc: linux-serial
In-Reply-To: <CAAMvbhH5wyiWf+1i4FWkAwGRtAK=xdZejbrtThzEdgTURo+uqg@mail.gmail.com>
On Mon, Feb 27, 2012 at 08:17:50PM +0000, James Courtier-Dutton wrote:
> On 25 February 2012 16:41, Greg KH <greg@kroah.com> wrote:
> > On Sat, Feb 25, 2012 at 10:01:17AM +0000, James Courtier-Dutton wrote:
> >> Hi,
> >>
> >> I have done some development work on the iuu_phoenix.c driver in the
> >> Linux kernel.
> >> The iuu_phoenix is a smart card reader.
> >> The smart card reader can be given a unique identification string of
> >> 16 characters/hex bytes. This ID can be read when the card reader is
> >> inserted into the USB port.
> >> I would like to be able to set up device names in /dev so that when
> >> inserting a card reader into the USB port, it always will appear withudev
> >> the same unique name in /dev.
> >> Similar to the by-uuid used for hard disk drives.
> >
> > Your distro should already have /dev/serial/ which provides this for you
> > automatically. Have you used that and found it somehow lacking?
> >
>
> My question is how to I get the card reader ID, that is going to be
> read by a special usb request, to influence the name of the
> /dev/serial device name.
> If the first card reader has ID: 0001, and the second has the ID:
> 0002. The IDs are programmed into the EEPROM of the card reader. I
> would like the IDs to be passed to udev so that even if only one card
> reader with ID:0002 is plugged in, it always gets the same name in
> /dev/serial
> The reason for this, is so that someone can look at the device name in
> /dev/serial and then know exactly which physical device it applies to.
If the card reader's "id" shows up in the serial number of the USB
device, /dev/serial/ will reflect this in the naming of the symlinks it
generates for the device.
Try it and see :)
greg k-h
--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: USB Smart Card reader
From: James Courtier-Dutton @ 2012-02-27 20:17 UTC (permalink / raw)
To: Greg KH; +Cc: linux-serial
In-Reply-To: <20120225164118.GA21495@kroah.com>
On 25 February 2012 16:41, Greg KH <greg@kroah.com> wrote:
> On Sat, Feb 25, 2012 at 10:01:17AM +0000, James Courtier-Dutton wrote:
>> Hi,
>>
>> I have done some development work on the iuu_phoenix.c driver in the
>> Linux kernel.
>> The iuu_phoenix is a smart card reader.
>> The smart card reader can be given a unique identification string of
>> 16 characters/hex bytes. This ID can be read when the card reader is
>> inserted into the USB port.
>> I would like to be able to set up device names in /dev so that when
>> inserting a card reader into the USB port, it always will appear withudev
>> the same unique name in /dev.
>> Similar to the by-uuid used for hard disk drives.
>
> Your distro should already have /dev/serial/ which provides this for you
> automatically. Have you used that and found it somehow lacking?
>
My question is how to I get the card reader ID, that is going to be
read by a special usb request, to influence the name of the
/dev/serial device name.
If the first card reader has ID: 0001, and the second has the ID:
0002. The IDs are programmed into the EEPROM of the card reader. I
would like the IDs to be passed to udev so that even if only one card
reader with ID:0002 is plugged in, it always gets the same name in
/dev/serial
The reason for this, is so that someone can look at the device name in
/dev/serial and then know exactly which physical device it applies to.
--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [RFC PATCH] vt:tackle kbd_table
From: Alan Cox @ 2012-02-27 16:10 UTC (permalink / raw)
To: linux-kernel, linux-serial
This could do with some extra eyes and double checking. With this one out of
the day we can start to make a dent in the console locking logic and thus
eventually into tty_lock().
From: Alan Cox <alan@linux.intel.com>
Keyboard struct lifetime is easy, but the locking is not and is completely
ignored by the existing code. Tackle this one head on
- Make the kbd_table private so we can run down all direct users
- Hoick the relevant ioctl handlers into the keyboard layer
- Lock them with the keyboard lock so they don't change mid keypress
- Add helpers for things like console stop/start so we isolate the poking
around properly
- Tweak the braille console so it still builds
There are a couple of FIXME locking cases left for ioctls that are so hideous
they should be addressed in a later patch. After this patch the kbd_table is
private and all the keyboard jiggery pokery is in one place.
Signed-off-by: Alan Cox <alan@linux.intel.com>
---
drivers/accessibility/braille/braille_console.c | 9
drivers/tty/sysrq.c | 6
drivers/tty/vt/keyboard.c | 620 ++++++++++++++++++++++-
drivers/tty/vt/selection.c | 9
drivers/tty/vt/vt.c | 27 -
drivers/tty/vt/vt_ioctl.c | 325 +-----------
include/linux/kbd_kern.h | 7
include/linux/keyboard.h | 2
include/linux/vt_kern.h | 23 +
9 files changed, 655 insertions(+), 373 deletions(-)
diff --git a/drivers/accessibility/braille/braille_console.c b/drivers/accessibility/braille/braille_console.c
index c339a08..d21167b 100644
--- a/drivers/accessibility/braille/braille_console.c
+++ b/drivers/accessibility/braille/braille_console.c
@@ -244,16 +244,13 @@ static int keyboard_notifier_call(struct notifier_block *blk,
switch (val) {
case KVAL(K_CAPS):
- on_off = vc_kbd_led(kbd_table + fg_console,
- VC_CAPSLOCK);
+ on_off = vt_get_leds(fg_console, VC_CAPSLOCK);
break;
case KVAL(K_NUM):
- on_off = vc_kbd_led(kbd_table + fg_console,
- VC_NUMLOCK);
+ on_off = vt_get_leds(fg_console, VC_NUMLOCK);
break;
case KVAL(K_HOLD):
- on_off = vc_kbd_led(kbd_table + fg_console,
- VC_SCROLLOCK);
+ on_off = vt_get_leds(fg_console, VC_SCROLLOCK);
break;
}
if (on_off == 1)
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index 8db9125..ecb8e22 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -110,11 +110,9 @@ static struct sysrq_key_op sysrq_SAK_op = {
#ifdef CONFIG_VT
static void sysrq_handle_unraw(int key)
{
- struct kbd_struct *kbd = &kbd_table[fg_console];
-
- if (kbd)
- kbd->kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE;
+ vt_reset_unicode(fg_console);
}
+
static struct sysrq_key_op sysrq_unraw_op = {
.handler = sysrq_handle_unraw,
.help_msg = "unRaw",
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 0479114..8a401e6 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -67,8 +67,6 @@ extern void ctrl_alt_del(void);
#define KBD_DEFLOCK 0
-void compute_shiftstate(void);
-
/*
* Handler Tables.
*/
@@ -99,35 +97,29 @@ static fn_handler_fn *fn_handler[] = { FN_HANDLERS };
* Variables exported for vt_ioctl.c
*/
-/* maximum values each key_handler can handle */
-const int max_vals[] = {
- 255, ARRAY_SIZE(func_table) - 1, ARRAY_SIZE(fn_handler) - 1, NR_PAD - 1,
- NR_DEAD - 1, 255, 3, NR_SHIFT - 1, 255, NR_ASCII - 1, NR_LOCK - 1,
- 255, NR_LOCK - 1, 255, NR_BRL - 1
-};
-
-const int NR_TYPES = ARRAY_SIZE(max_vals);
-
-struct kbd_struct kbd_table[MAX_NR_CONSOLES];
-EXPORT_SYMBOL_GPL(kbd_table);
-static struct kbd_struct *kbd = kbd_table;
-
struct vt_spawn_console vt_spawn_con = {
.lock = __SPIN_LOCK_UNLOCKED(vt_spawn_con.lock),
.pid = NULL,
.sig = 0,
};
-/*
- * Variables exported for vt.c
- */
-
-int shift_state = 0;
/*
* Internal Data.
*/
+static struct kbd_struct kbd_table[MAX_NR_CONSOLES];
+static struct kbd_struct *kbd = kbd_table;
+
+/* maximum values each key_handler can handle */
+static const int max_vals[] = {
+ 255, ARRAY_SIZE(func_table) - 1, ARRAY_SIZE(fn_handler) - 1, NR_PAD - 1,
+ NR_DEAD - 1, 255, 3, NR_SHIFT - 1, 255, NR_ASCII - 1, NR_LOCK - 1,
+ 255, NR_LOCK - 1, 255, NR_BRL - 1
+};
+
+static const int NR_TYPES = ARRAY_SIZE(max_vals);
+
static struct input_handler kbd_handler;
static DEFINE_SPINLOCK(kbd_event_lock);
static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */
@@ -137,6 +129,8 @@ static int npadch = -1; /* -1 or number assembled on pad */
static unsigned int diacr;
static char rep; /* flag telling character repeat */
+static int shift_state = 0;
+
static unsigned char ledstate = 0xff; /* undefined */
static unsigned char ledioctl;
@@ -187,7 +181,7 @@ static int getkeycode_helper(struct input_handle *handle, void *data)
return d->error == 0; /* stop as soon as we successfully get one */
}
-int getkeycode(unsigned int scancode)
+static int getkeycode(unsigned int scancode)
{
struct getset_keycode_data d = {
.ke = {
@@ -214,7 +208,7 @@ static int setkeycode_helper(struct input_handle *handle, void *data)
return d->error == 0; /* stop as soon as we successfully set one */
}
-int setkeycode(unsigned int scancode, unsigned int keycode)
+static int setkeycode(unsigned int scancode, unsigned int keycode)
{
struct getset_keycode_data d = {
.ke = {
@@ -382,9 +376,11 @@ static void to_utf8(struct vc_data *vc, uint c)
/*
* Called after returning from RAW mode or when changing consoles - recompute
* shift_down[] and shift_state from key_down[] maybe called when keymap is
- * undefined, so that shiftkey release is seen
+ * undefined, so that shiftkey release is seen. The caller must hold the
+ * kbd_event_lock.
*/
-void compute_shiftstate(void)
+
+static void do_compute_shiftstate(void)
{
unsigned int i, j, k, sym, val;
@@ -417,6 +413,15 @@ void compute_shiftstate(void)
}
}
+/* We still have to export this method to vt.c */
+void compute_shiftstate(void)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&kbd_event_lock, flags);
+ do_compute_shiftstate();
+ spin_unlock_irqrestore(&kbd_event_lock, flags);
+}
+
/*
* We have a combining character DIACR here, followed by the character CH.
* If the combination occurs in the table, return the corresponding value.
@@ -636,7 +641,7 @@ static void fn_SAK(struct vc_data *vc)
static void fn_null(struct vc_data *vc)
{
- compute_shiftstate();
+ do_compute_shiftstate();
}
/*
@@ -989,6 +994,8 @@ unsigned char getledstate(void)
void setledstate(struct kbd_struct *kbd, unsigned int led)
{
+ unsigned long flags;
+ spin_lock_irqsave(&kbd_event_lock, flags);
if (!(led & ~7)) {
ledioctl = led;
kbd->ledmode = LED_SHOW_IOCTL;
@@ -996,6 +1003,7 @@ void setledstate(struct kbd_struct *kbd, unsigned int led)
kbd->ledmode = LED_SHOW_FLAGS;
set_leds();
+ spin_unlock_irqrestore(&kbd_event_lock, flags);
}
static inline unsigned char getleds(void)
@@ -1035,6 +1043,75 @@ static int kbd_update_leds_helper(struct input_handle *handle, void *data)
return 0;
}
+/**
+ * vt_get_leds - helper for braille console
+ * @console: console to read
+ * @flag: flag we want to check
+ *
+ * Check the status of a keyboard led flag and report it back
+ */
+int vt_get_leds(int console, int flag)
+{
+ unsigned long flags;
+ struct kbd_struct * kbd = kbd_table + console;
+ int ret;
+
+ spin_lock_irqsave(&kbd_event_lock, flags);
+ ret = vc_kbd_led(kbd, flag);
+ spin_unlock_irqrestore(&kbd_event_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vt_get_leds);
+
+/**
+ * vt_set_led_state - set LED state of a console
+ * @console: console to set
+ * @leds: LED bits
+ *
+ * Set the LEDs on a console. This is a wrapper for the VT layer
+ * so that we can keep kbd knowledge internal
+ */
+void vt_set_led_state(int console, int leds)
+{
+ struct kbd_struct * kbd = kbd_table + console;
+ setledstate(kbd, leds);
+}
+
+/**
+ * vt_kbd_con_start - Keyboard side of console start
+ * @console: console
+ *
+ * Handle console start. This is a wrapper for the VT layer
+ * so that we can keep kbd knowledge internal
+ */
+void vt_kbd_con_start(int console)
+{
+ struct kbd_struct * kbd = kbd_table + console;
+ unsigned long flags;
+ spin_lock_irqsave(&kbd_event_lock, flags);
+ clr_vc_kbd_led(kbd, VC_SCROLLOCK);
+ set_leds();
+ spin_unlock_irqrestore(&kbd_event_lock, flags);
+}
+
+/**
+ * vt_kbd_con_stop - Keyboard side of console stop
+ * @console: console
+ *
+ * Handle console stop. This is a wrapper for the VT layer
+ * so that we can keep kbd knowledge internal
+ */
+void vt_kbd_con_stop(int console)
+{
+ struct kbd_struct * kbd = kbd_table + console;
+ unsigned long flags;
+ spin_lock_irqsave(&kbd_event_lock, flags);
+ set_vc_kbd_led(kbd, VC_SCROLLOCK);
+ set_leds();
+ spin_unlock_irqrestore(&kbd_event_lock, flags);
+}
+
/*
* This is the tasklet that updates LED state on all keyboards
* attached to the box. The reason we use tasklet is that we
@@ -1254,7 +1331,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
if (rc == NOTIFY_STOP || !key_map) {
atomic_notifier_call_chain(&keyboard_notifier_list,
KBD_UNBOUND_KEYCODE, ¶m);
- compute_shiftstate();
+ do_compute_shiftstate();
kbd->slockstate = 0;
return;
}
@@ -1614,3 +1691,494 @@ int vt_do_diacrit(unsigned int cmd, void __user *up, int perm)
}
return ret;
}
+
+/**
+ * vt_do_kdskbmode - set keyboard mode ioctl
+ * @console: the console to use
+ * @arg: the requested mode
+ *
+ * Update the keyboard mode bits while holding the correct locks.
+ * Return 0 for success or an error code.
+ */
+int vt_do_kdskbmode(int console, unsigned int arg)
+{
+ struct kbd_struct * kbd = kbd_table + console;
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&kbd_event_lock, flags);
+ switch(arg) {
+ case K_RAW:
+ kbd->kbdmode = VC_RAW;
+ break;
+ case K_MEDIUMRAW:
+ kbd->kbdmode = VC_MEDIUMRAW;
+ break;
+ case K_XLATE:
+ kbd->kbdmode = VC_XLATE;
+ do_compute_shiftstate();
+ break;
+ case K_UNICODE:
+ kbd->kbdmode = VC_UNICODE;
+ do_compute_shiftstate();
+ break;
+ case K_OFF:
+ kbd->kbdmode = VC_OFF;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ spin_unlock_irqrestore(&kbd_event_lock, flags);
+ return ret;
+}
+
+/**
+ * vt_do_kdskbmeta - set keyboard meta state
+ * @console: the console to use
+ * @arg: the requested meta state
+ *
+ * Update the keyboard meta bits while holding the correct locks.
+ * Return 0 for success or an error code.
+ */
+int vt_do_kdskbmeta(int console, unsigned int arg)
+{
+ struct kbd_struct * kbd = kbd_table + console;
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&kbd_event_lock, flags);
+ switch(arg) {
+ case K_METABIT:
+ clr_vc_kbd_mode(kbd, VC_META);
+ break;
+ case K_ESCPREFIX:
+ set_vc_kbd_mode(kbd, VC_META);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ spin_unlock_irqrestore(&kbd_event_lock, flags);
+ return ret;
+}
+
+int vt_do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc,
+ int perm)
+{
+ struct kbkeycode tmp;
+ int kc = 0;
+
+ if (copy_from_user(&tmp, user_kbkc, sizeof(struct kbkeycode)))
+ return -EFAULT;
+ switch (cmd) {
+ case KDGETKEYCODE:
+ kc = getkeycode(tmp.scancode);
+ if (kc >= 0)
+ kc = put_user(kc, &user_kbkc->keycode);
+ break;
+ case KDSETKEYCODE:
+ if (!perm)
+ return -EPERM;
+ kc = setkeycode(tmp.scancode, tmp.keycode);
+ break;
+ }
+ return kc;
+}
+
+#define i (tmp.kb_index)
+#define s (tmp.kb_table)
+#define v (tmp.kb_value)
+
+int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm,
+ int console)
+{
+ struct kbd_struct * kbd = kbd_table + console;
+ struct kbentry tmp;
+ ushort *key_map, *new_map, val, ov;
+ unsigned long flags;
+
+ if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry)))
+ return -EFAULT;
+
+ if (!capable(CAP_SYS_TTY_CONFIG))
+ perm = 0;
+
+ switch (cmd) {
+ case KDGKBENT:
+ /* Ensure another thread doesn't free it under us */
+ spin_lock_irqsave(&kbd_event_lock, flags);
+ key_map = key_maps[s];
+ if (key_map) {
+ val = U(key_map[i]);
+ if (kbd->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES)
+ val = K_HOLE;
+ } else
+ val = (i ? K_HOLE : K_NOSUCHMAP);
+ spin_unlock_irqrestore(&kbd_event_lock, flags);
+ return put_user(val, &user_kbe->kb_value);
+ case KDSKBENT:
+ if (!perm)
+ return -EPERM;
+ if (!i && v == K_NOSUCHMAP) {
+ spin_lock_irqsave(&kbd_event_lock, flags);
+ /* deallocate map */
+ key_map = key_maps[s];
+ if (s && key_map) {
+ key_maps[s] = NULL;
+ if (key_map[0] == U(K_ALLOCATED)) {
+ kfree(key_map);
+ keymap_count--;
+ }
+ }
+ spin_unlock_irqrestore(&kbd_event_lock, flags);
+ break;
+ }
+
+ if (KTYP(v) < NR_TYPES) {
+ if (KVAL(v) > max_vals[KTYP(v)])
+ return -EINVAL;
+ } else
+ if (kbd->kbdmode != VC_UNICODE)
+ return -EINVAL;
+
+ /* ++Geert: non-PC keyboards may generate keycode zero */
+#if !defined(__mc68000__) && !defined(__powerpc__)
+ /* assignment to entry 0 only tests validity of args */
+ if (!i)
+ break;
+#endif
+
+ new_map = kmalloc(sizeof(plain_map), GFP_KERNEL);
+ if (!new_map)
+ return -ENOMEM;
+ spin_lock_irqsave(&kbd_event_lock, flags);
+ key_map = key_maps[s];
+ if (key_map == NULL) {
+ int j;
+
+ if (keymap_count >= MAX_NR_OF_USER_KEYMAPS &&
+ !capable(CAP_SYS_RESOURCE)) {
+ spin_unlock_irqrestore(&kbd_event_lock, flags);
+ return -EPERM;
+ }
+ key_maps[s] = new_map;
+ key_map[0] = U(K_ALLOCATED);
+ for (j = 1; j < NR_KEYS; j++)
+ key_map[j] = U(K_HOLE);
+ keymap_count++;
+ } else
+ kfree(new_map);
+
+ ov = U(key_map[i]);
+ if (v == ov)
+ goto out;
+ /*
+ * Attention Key.
+ */
+ if (((ov == K_SAK) || (v == K_SAK)) && !capable(CAP_SYS_ADMIN)) {
+ spin_unlock_irqrestore(&kbd_event_lock, flags);
+ return -EPERM;
+ }
+ key_map[i] = U(v);
+ if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT))
+ do_compute_shiftstate();
+out:
+ spin_unlock_irqrestore(&kbd_event_lock, flags);
+ break;
+ }
+ return 0;
+}
+#undef i
+#undef s
+#undef v
+
+/* FIXME: This one needs untangling and locking */
+int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
+{
+ struct kbsentry *kbs;
+ char *p;
+ u_char *q;
+ u_char __user *up;
+ int sz;
+ int delta;
+ char *first_free, *fj, *fnw;
+ int i, j, k;
+ int ret;
+
+ if (!capable(CAP_SYS_TTY_CONFIG))
+ perm = 0;
+
+ kbs = kmalloc(sizeof(*kbs), GFP_KERNEL);
+ if (!kbs) {
+ ret = -ENOMEM;
+ goto reterr;
+ }
+
+ /* we mostly copy too much here (512bytes), but who cares ;) */
+ if (copy_from_user(kbs, user_kdgkb, sizeof(struct kbsentry))) {
+ ret = -EFAULT;
+ goto reterr;
+ }
+ kbs->kb_string[sizeof(kbs->kb_string)-1] = '\0';
+ i = kbs->kb_func;
+
+ switch (cmd) {
+ case KDGKBSENT:
+ sz = sizeof(kbs->kb_string) - 1; /* sz should have been
+ a struct member */
+ up = user_kdgkb->kb_string;
+ p = func_table[i];
+ if(p)
+ for ( ; *p && sz; p++, sz--)
+ if (put_user(*p, up++)) {
+ ret = -EFAULT;
+ goto reterr;
+ }
+ if (put_user('\0', up)) {
+ ret = -EFAULT;
+ goto reterr;
+ }
+ kfree(kbs);
+ return ((p && *p) ? -EOVERFLOW : 0);
+ case KDSKBSENT:
+ if (!perm) {
+ ret = -EPERM;
+ goto reterr;
+ }
+
+ q = func_table[i];
+ first_free = funcbufptr + (funcbufsize - funcbufleft);
+ for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++)
+ ;
+ if (j < MAX_NR_FUNC)
+ fj = func_table[j];
+ else
+ fj = first_free;
+
+ delta = (q ? -strlen(q) : 1) + strlen(kbs->kb_string);
+ if (delta <= funcbufleft) { /* it fits in current buf */
+ if (j < MAX_NR_FUNC) {
+ memmove(fj + delta, fj, first_free - fj);
+ for (k = j; k < MAX_NR_FUNC; k++)
+ if (func_table[k])
+ func_table[k] += delta;
+ }
+ if (!q)
+ func_table[i] = fj;
+ funcbufleft -= delta;
+ } else { /* allocate a larger buffer */
+ sz = 256;
+ while (sz < funcbufsize - funcbufleft + delta)
+ sz <<= 1;
+ fnw = kmalloc(sz, GFP_KERNEL);
+ if(!fnw) {
+ ret = -ENOMEM;
+ goto reterr;
+ }
+
+ if (!q)
+ func_table[i] = fj;
+ if (fj > funcbufptr)
+ memmove(fnw, funcbufptr, fj - funcbufptr);
+ for (k = 0; k < j; k++)
+ if (func_table[k])
+ func_table[k] = fnw + (func_table[k] - funcbufptr);
+
+ if (first_free > fj) {
+ memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj);
+ for (k = j; k < MAX_NR_FUNC; k++)
+ if (func_table[k])
+ func_table[k] = fnw + (func_table[k] - funcbufptr) + delta;
+ }
+ if (funcbufptr != func_buf)
+ kfree(funcbufptr);
+ funcbufptr = fnw;
+ funcbufleft = funcbufleft - delta + sz - funcbufsize;
+ funcbufsize = sz;
+ }
+ strcpy(func_table[i], kbs->kb_string);
+ break;
+ }
+ ret = 0;
+reterr:
+ kfree(kbs);
+ return ret;
+}
+
+int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm)
+{
+ struct kbd_struct * kbd = kbd_table + console;
+ unsigned long flags;
+ unsigned char ucval;
+
+ switch(cmd) {
+ /* the ioctls below read/set the flags usually shown in the leds */
+ /* don't use them - they will go away without warning */
+ case KDGKBLED:
+ spin_lock_irqsave(&kbd_event_lock, flags);
+ ucval = kbd->ledflagstate | (kbd->default_ledflagstate << 4);
+ spin_unlock_irqrestore(&kbd_event_lock, flags);
+ return put_user(ucval, (char __user *)arg);
+
+ case KDSKBLED:
+ if (!perm)
+ return -EPERM;
+ if (arg & ~0x77)
+ return -EINVAL;
+ spin_lock_irqsave(&kbd_event_lock, flags);
+ kbd->ledflagstate = (arg & 7);
+ kbd->default_ledflagstate = ((arg >> 4) & 7);
+ set_leds();
+ spin_unlock_irqrestore(&kbd_event_lock, flags);
+ break;
+
+ /* the ioctls below only set the lights, not the functions */
+ /* for those, see KDGKBLED and KDSKBLED above */
+ case KDGETLED:
+ ucval = getledstate();
+ return put_user(ucval, (char __user *)arg);
+
+ case KDSETLED:
+ if (!perm)
+ return -EPERM;
+ setledstate(kbd, arg);
+ return 0;
+ }
+ return -ENOIOCTLCMD;
+}
+
+int vt_do_kdgkbmode(int console)
+{
+ struct kbd_struct * kbd = kbd_table + console;
+ /* This is a spot read so needs no locking */
+ switch (kbd->kbdmode) {
+ case VC_RAW:
+ return K_RAW;
+ case VC_MEDIUMRAW:
+ return K_MEDIUMRAW;
+ case VC_UNICODE:
+ return K_UNICODE;
+ case VC_OFF:
+ return K_OFF;
+ default:
+ return K_XLATE;
+ }
+}
+
+/**
+ * vt_do_kdgkbmeta - report meta status
+ * @console: console to report
+ *
+ * Report the meta flag status of this console
+ */
+int vt_do_kdgkbmeta(int console)
+{
+ struct kbd_struct * kbd = kbd_table + console;
+ /* Again a spot read so no locking */
+ return vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX : K_METABIT;
+}
+
+/**
+ * vt_reset_unicode - reset the unicode status
+ * @console: console being reset
+ *
+ * Restore the unicode console state to its default
+ */
+void vt_reset_unicode(int console)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&kbd_event_lock, flags);
+ kbd_table[console].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE;
+ spin_unlock_irqrestore(&kbd_event_lock, flags);
+}
+
+/**
+ * vt_get_shiftstate - shift bit state
+ *
+ * Report the shift bits from the keyboard state. We have to export
+ * this to support some oddities in the vt layer.
+ */
+int vt_get_shift_state(void)
+{
+ /* Don't lock as this is a transient report */
+ return shift_state;
+}
+
+/**
+ * vt_reset_keyboard - reset keyboard state
+ * @console: console to reset
+ *
+ * Reset the keyboard bits for a console as part of a general console
+ * reset event
+ */
+void vt_reset_keyboard(int console)
+{
+ struct kbd_struct * kbd = kbd_table + console;
+ unsigned long flags;
+
+ spin_lock_irqsave(&kbd_event_lock, flags);
+ set_vc_kbd_mode(kbd, VC_REPEAT);
+ clr_vc_kbd_mode(kbd, VC_CKMODE);
+ clr_vc_kbd_mode(kbd, VC_APPLIC);
+ clr_vc_kbd_mode(kbd, VC_CRLF);
+ kbd->lockstate = 0;
+ kbd->slockstate = 0;
+ kbd->ledmode = LED_SHOW_FLAGS;
+ kbd->ledflagstate = kbd->default_ledflagstate;
+ /* do not do set_leds here because this causes an endless tasklet loop
+ when the keyboard hasn't been initialized yet */
+ spin_unlock_irqrestore(&kbd_event_lock, flags);
+}
+
+/**
+ * vt_get_kbd_mode_bit - read keyboard status bits
+ * @console: console to read from
+ * @bit: mode bit to read
+ *
+ * Report back a vt mode bit. We do this without locking so the
+ * caller must be sure that there are no synchronization needs
+ */
+
+int vt_get_kbd_mode_bit(int console, int bit)
+{
+ struct kbd_struct * kbd = kbd_table + console;
+ return vc_kbd_mode(kbd, bit);
+}
+
+/**
+ * vt_set_kbd_mode_bit - read keyboard status bits
+ * @console: console to read from
+ * @bit: mode bit to read
+ *
+ * Set a vt mode bit. We do this without locking so the
+ * caller must be sure that there are no synchronization needs
+ */
+
+void vt_set_kbd_mode_bit(int console, int bit)
+{
+ struct kbd_struct * kbd = kbd_table + console;
+ unsigned long flags;
+
+ spin_lock_irqsave(&kbd_event_lock, flags);
+ set_vc_kbd_mode(kbd, bit);
+ spin_unlock_irqrestore(&kbd_event_lock, flags);
+}
+
+/**
+ * vt_clr_kbd_mode_bit - read keyboard status bits
+ * @console: console to read from
+ * @bit: mode bit to read
+ *
+ * Report back a vt mode bit. We do this without locking so the
+ * caller must be sure that there are no synchronization needs
+ */
+
+void vt_clr_kbd_mode_bit(int console, int bit)
+{
+ struct kbd_struct * kbd = kbd_table + console;
+ unsigned long flags;
+
+ spin_lock_irqsave(&kbd_event_lock, flags);
+ clr_vc_kbd_mode(kbd, bit);
+ spin_unlock_irqrestore(&kbd_event_lock, flags);
+}
diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c
index 7a0a12a..738e45a 100644
--- a/drivers/tty/vt/selection.c
+++ b/drivers/tty/vt/selection.c
@@ -30,6 +30,7 @@
extern void poke_blanked_console(void);
+/* FIXME: all this needs locking */
/* Variables for selection control. */
/* Use a dynamic buffer, instead of static (Dec 1994) */
struct vc_data *sel_cons; /* must not be deallocated */
@@ -138,7 +139,7 @@ int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *t
char *bp, *obp;
int i, ps, pe, multiplier;
u16 c;
- struct kbd_struct *kbd = kbd_table + fg_console;
+ int mode;
poke_blanked_console();
@@ -182,7 +183,11 @@ int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *t
clear_selection();
sel_cons = vc_cons[fg_console].d;
}
- use_unicode = kbd && kbd->kbdmode == VC_UNICODE;
+ mode = vt_do_kdgkbmode(fg_console);
+ if (mode == K_UNICODE)
+ use_unicode = 1;
+ else
+ use_unicode = 0;
switch (sel_mode)
{
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 3c7c56b..8205578 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -1028,9 +1028,9 @@ void vc_deallocate(unsigned int currcons)
* VT102 emulator
*/
-#define set_kbd(vc, x) set_vc_kbd_mode(kbd_table + (vc)->vc_num, (x))
-#define clr_kbd(vc, x) clr_vc_kbd_mode(kbd_table + (vc)->vc_num, (x))
-#define is_kbd(vc, x) vc_kbd_mode(kbd_table + (vc)->vc_num, (x))
+#define set_kbd(vc, x) vt_set_kbd_mode_bit((vc)->vc_num, (x))
+#define clr_kbd(vc, x) vt_clr_kbd_mode_bit((vc)->vc_num, (x))
+#define is_kbd(vc, x) vt_get_kbd_mode_bit((vc)->vc_num, (x))
#define decarm VC_REPEAT
#define decckm VC_CKMODE
@@ -1652,16 +1652,7 @@ static void reset_terminal(struct vc_data *vc, int do_clear)
vc->vc_deccm = global_cursor_default;
vc->vc_decim = 0;
- set_kbd(vc, decarm);
- clr_kbd(vc, decckm);
- clr_kbd(vc, kbdapplic);
- clr_kbd(vc, lnm);
- kbd_table[vc->vc_num].lockstate = 0;
- kbd_table[vc->vc_num].slockstate = 0;
- kbd_table[vc->vc_num].ledmode = LED_SHOW_FLAGS;
- kbd_table[vc->vc_num].ledflagstate = kbd_table[vc->vc_num].default_ledflagstate;
- /* do not do set_leds here because this causes an endless tasklet loop
- when the keyboard hasn't been initialized yet */
+ vt_reset_keyboard(vc->vc_num);
vc->vc_cursor_type = cur_default;
vc->vc_complement_mask = vc->vc_s_complement_mask;
@@ -1979,7 +1970,7 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
case 'q': /* DECLL - but only 3 leds */
/* map 0,1,2,3 to 0,1,2,4 */
if (vc->vc_par[0] < 4)
- setledstate(kbd_table + vc->vc_num,
+ vt_set_led_state(vc->vc_num,
(vc->vc_par[0] < 3) ? vc->vc_par[0] : 4);
return;
case 'r':
@@ -2642,7 +2633,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
* kernel-internal variable; programs not closely
* related to the kernel should not use this.
*/
- data = shift_state;
+ data = vt_get_shift_state();
ret = __put_user(data, p);
break;
case TIOCL_GETMOUSEREPORTING:
@@ -2753,8 +2744,7 @@ static void con_stop(struct tty_struct *tty)
console_num = tty->index;
if (!vc_cons_allocated(console_num))
return;
- set_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK);
- set_leds();
+ vt_kbd_con_stop(console_num);
}
/*
@@ -2768,8 +2758,7 @@ static void con_start(struct tty_struct *tty)
console_num = tty->index;
if (!vc_cons_allocated(console_num))
return;
- clr_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK);
- set_leds();
+ vt_kbd_con_start(console_num);
}
static void con_flush_chars(struct tty_struct *tty)
diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c
index 80af0f9..28ca0aa 100644
--- a/drivers/tty/vt/vt_ioctl.c
+++ b/drivers/tty/vt/vt_ioctl.c
@@ -195,232 +195,7 @@ int vt_waitactive(int n)
#define GPLAST 0x3df
#define GPNUM (GPLAST - GPFIRST + 1)
-#define i (tmp.kb_index)
-#define s (tmp.kb_table)
-#define v (tmp.kb_value)
-static inline int
-do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm, struct kbd_struct *kbd)
-{
- struct kbentry tmp;
- ushort *key_map, val, ov;
-
- if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry)))
- return -EFAULT;
-
- if (!capable(CAP_SYS_TTY_CONFIG))
- perm = 0;
-
- switch (cmd) {
- case KDGKBENT:
- key_map = key_maps[s];
- if (key_map) {
- val = U(key_map[i]);
- if (kbd->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES)
- val = K_HOLE;
- } else
- val = (i ? K_HOLE : K_NOSUCHMAP);
- return put_user(val, &user_kbe->kb_value);
- case KDSKBENT:
- if (!perm)
- return -EPERM;
- if (!i && v == K_NOSUCHMAP) {
- /* deallocate map */
- key_map = key_maps[s];
- if (s && key_map) {
- key_maps[s] = NULL;
- if (key_map[0] == U(K_ALLOCATED)) {
- kfree(key_map);
- keymap_count--;
- }
- }
- break;
- }
-
- if (KTYP(v) < NR_TYPES) {
- if (KVAL(v) > max_vals[KTYP(v)])
- return -EINVAL;
- } else
- if (kbd->kbdmode != VC_UNICODE)
- return -EINVAL;
-
- /* ++Geert: non-PC keyboards may generate keycode zero */
-#if !defined(__mc68000__) && !defined(__powerpc__)
- /* assignment to entry 0 only tests validity of args */
- if (!i)
- break;
-#endif
-
- if (!(key_map = key_maps[s])) {
- int j;
-
- if (keymap_count >= MAX_NR_OF_USER_KEYMAPS &&
- !capable(CAP_SYS_RESOURCE))
- return -EPERM;
-
- key_map = kmalloc(sizeof(plain_map),
- GFP_KERNEL);
- if (!key_map)
- return -ENOMEM;
- key_maps[s] = key_map;
- key_map[0] = U(K_ALLOCATED);
- for (j = 1; j < NR_KEYS; j++)
- key_map[j] = U(K_HOLE);
- keymap_count++;
- }
- ov = U(key_map[i]);
- if (v == ov)
- break; /* nothing to do */
- /*
- * Attention Key.
- */
- if (((ov == K_SAK) || (v == K_SAK)) && !capable(CAP_SYS_ADMIN))
- return -EPERM;
- key_map[i] = U(v);
- if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT))
- compute_shiftstate();
- break;
- }
- return 0;
-}
-#undef i
-#undef s
-#undef v
-
-static inline int
-do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc, int perm)
-{
- struct kbkeycode tmp;
- int kc = 0;
-
- if (copy_from_user(&tmp, user_kbkc, sizeof(struct kbkeycode)))
- return -EFAULT;
- switch (cmd) {
- case KDGETKEYCODE:
- kc = getkeycode(tmp.scancode);
- if (kc >= 0)
- kc = put_user(kc, &user_kbkc->keycode);
- break;
- case KDSETKEYCODE:
- if (!perm)
- return -EPERM;
- kc = setkeycode(tmp.scancode, tmp.keycode);
- break;
- }
- return kc;
-}
-
-static inline int
-do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
-{
- struct kbsentry *kbs;
- char *p;
- u_char *q;
- u_char __user *up;
- int sz;
- int delta;
- char *first_free, *fj, *fnw;
- int i, j, k;
- int ret;
-
- if (!capable(CAP_SYS_TTY_CONFIG))
- perm = 0;
-
- kbs = kmalloc(sizeof(*kbs), GFP_KERNEL);
- if (!kbs) {
- ret = -ENOMEM;
- goto reterr;
- }
-
- /* we mostly copy too much here (512bytes), but who cares ;) */
- if (copy_from_user(kbs, user_kdgkb, sizeof(struct kbsentry))) {
- ret = -EFAULT;
- goto reterr;
- }
- kbs->kb_string[sizeof(kbs->kb_string)-1] = '\0';
- i = kbs->kb_func;
-
- switch (cmd) {
- case KDGKBSENT:
- sz = sizeof(kbs->kb_string) - 1; /* sz should have been
- a struct member */
- up = user_kdgkb->kb_string;
- p = func_table[i];
- if(p)
- for ( ; *p && sz; p++, sz--)
- if (put_user(*p, up++)) {
- ret = -EFAULT;
- goto reterr;
- }
- if (put_user('\0', up)) {
- ret = -EFAULT;
- goto reterr;
- }
- kfree(kbs);
- return ((p && *p) ? -EOVERFLOW : 0);
- case KDSKBSENT:
- if (!perm) {
- ret = -EPERM;
- goto reterr;
- }
- q = func_table[i];
- first_free = funcbufptr + (funcbufsize - funcbufleft);
- for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++)
- ;
- if (j < MAX_NR_FUNC)
- fj = func_table[j];
- else
- fj = first_free;
-
- delta = (q ? -strlen(q) : 1) + strlen(kbs->kb_string);
- if (delta <= funcbufleft) { /* it fits in current buf */
- if (j < MAX_NR_FUNC) {
- memmove(fj + delta, fj, first_free - fj);
- for (k = j; k < MAX_NR_FUNC; k++)
- if (func_table[k])
- func_table[k] += delta;
- }
- if (!q)
- func_table[i] = fj;
- funcbufleft -= delta;
- } else { /* allocate a larger buffer */
- sz = 256;
- while (sz < funcbufsize - funcbufleft + delta)
- sz <<= 1;
- fnw = kmalloc(sz, GFP_KERNEL);
- if(!fnw) {
- ret = -ENOMEM;
- goto reterr;
- }
-
- if (!q)
- func_table[i] = fj;
- if (fj > funcbufptr)
- memmove(fnw, funcbufptr, fj - funcbufptr);
- for (k = 0; k < j; k++)
- if (func_table[k])
- func_table[k] = fnw + (func_table[k] - funcbufptr);
-
- if (first_free > fj) {
- memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj);
- for (k = j; k < MAX_NR_FUNC; k++)
- if (func_table[k])
- func_table[k] = fnw + (func_table[k] - funcbufptr) + delta;
- }
- if (funcbufptr != func_buf)
- kfree(funcbufptr);
- funcbufptr = fnw;
- funcbufleft = funcbufleft - delta + sz - funcbufsize;
- funcbufsize = sz;
- }
- strcpy(func_table[i], kbs->kb_string);
- break;
- }
- ret = 0;
-reterr:
- kfree(kbs);
- return ret;
-}
static inline int
do_fontx_ioctl(int cmd, struct consolefontdesc __user *user_cfd, int perm, struct console_font_op *op)
@@ -497,7 +272,6 @@ int vt_ioctl(struct tty_struct *tty,
{
struct vc_data *vc = tty->driver_data;
struct console_font_op op; /* used in multiple places here */
- struct kbd_struct * kbd;
unsigned int console;
unsigned char ucval;
unsigned int uival;
@@ -523,7 +297,6 @@ int vt_ioctl(struct tty_struct *tty,
if (current->signal->tty == tty || capable(CAP_SYS_TTY_CONFIG))
perm = 1;
- kbd = kbd_table + console;
switch (cmd) {
case TIOCLINUX:
ret = tioclinux(tty, arg);
@@ -565,7 +338,8 @@ int vt_ioctl(struct tty_struct *tty,
* this is naive.
*/
ucval = KB_101;
- goto setchar;
+ ret = put_user(ucval, (char __user *)arg);
+ break;
/*
* These cannot be implemented on any machine that implements
@@ -670,68 +444,25 @@ int vt_ioctl(struct tty_struct *tty,
case KDSKBMODE:
if (!perm)
goto eperm;
- switch(arg) {
- case K_RAW:
- kbd->kbdmode = VC_RAW;
- break;
- case K_MEDIUMRAW:
- kbd->kbdmode = VC_MEDIUMRAW;
- break;
- case K_XLATE:
- kbd->kbdmode = VC_XLATE;
- compute_shiftstate();
- break;
- case K_UNICODE:
- kbd->kbdmode = VC_UNICODE;
- compute_shiftstate();
- break;
- case K_OFF:
- kbd->kbdmode = VC_OFF;
- break;
- default:
- ret = -EINVAL;
- goto out;
- }
- tty_ldisc_flush(tty);
+ ret = vt_do_kdskbmode(console, arg);
+ if (ret == 0)
+ tty_ldisc_flush(tty);
break;
case KDGKBMODE:
- switch (kbd->kbdmode) {
- case VC_RAW:
- uival = K_RAW;
- break;
- case VC_MEDIUMRAW:
- uival = K_MEDIUMRAW;
- break;
- case VC_UNICODE:
- uival = K_UNICODE;
- break;
- case VC_OFF:
- uival = K_OFF;
- break;
- default:
- uival = K_XLATE;
- break;
- }
- goto setint;
+ uival = vt_do_kdgkbmode(console);
+ ret = put_user(uival, (int __user *)arg);
+ break;
/* this could be folded into KDSKBMODE, but for compatibility
reasons it is not so easy to fold KDGKBMETA into KDGKBMODE */
case KDSKBMETA:
- switch(arg) {
- case K_METABIT:
- clr_vc_kbd_mode(kbd, VC_META);
- break;
- case K_ESCPREFIX:
- set_vc_kbd_mode(kbd, VC_META);
- break;
- default:
- ret = -EINVAL;
- }
+ ret = vt_do_kdskbmeta(console, arg);
break;
case KDGKBMETA:
- uival = (vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX : K_METABIT);
+ /* FIXME: should review whether this is worth locking */
+ uival = vt_do_kdgkbmeta(console);
setint:
ret = put_user(uival, (int __user *)arg);
break;
@@ -740,17 +471,17 @@ int vt_ioctl(struct tty_struct *tty,
case KDSETKEYCODE:
if(!capable(CAP_SYS_TTY_CONFIG))
perm = 0;
- ret = do_kbkeycode_ioctl(cmd, up, perm);
+ ret = vt_do_kbkeycode_ioctl(cmd, up, perm);
break;
case KDGKBENT:
case KDSKBENT:
- ret = do_kdsk_ioctl(cmd, up, perm, kbd);
+ ret = vt_do_kdsk_ioctl(cmd, up, perm, console);
break;
case KDGKBSENT:
case KDSKBSENT:
- ret = do_kdgkb_ioctl(cmd, up, perm);
+ ret = vt_do_kdgkb_ioctl(cmd, up, perm);
break;
/* Diacritical processing. Handled in keyboard.c as it has
@@ -765,33 +496,10 @@ int vt_ioctl(struct tty_struct *tty,
/* the ioctls below read/set the flags usually shown in the leds */
/* don't use them - they will go away without warning */
case KDGKBLED:
- ucval = kbd->ledflagstate | (kbd->default_ledflagstate << 4);
- goto setchar;
-
case KDSKBLED:
- if (!perm)
- goto eperm;
- if (arg & ~0x77) {
- ret = -EINVAL;
- break;
- }
- kbd->ledflagstate = (arg & 7);
- kbd->default_ledflagstate = ((arg >> 4) & 7);
- set_leds();
- break;
-
- /* the ioctls below only set the lights, not the functions */
- /* for those, see KDGKBLED and KDSKBLED above */
case KDGETLED:
- ucval = getledstate();
- setchar:
- ret = put_user(ucval, (char __user *)arg);
- break;
-
case KDSETLED:
- if (!perm)
- goto eperm;
- setledstate(kbd, arg);
+ ret = vt_do_kdskled(console, cmd, arg, perm);
break;
/*
@@ -1286,7 +994,7 @@ eperm:
void reset_vc(struct vc_data *vc)
{
vc->vc_mode = KD_TEXT;
- kbd_table[vc->vc_num].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE;
+ vt_reset_unicode(vc->vc_num);
vc->vt_mode.mode = VT_AUTO;
vc->vt_mode.waitv = 0;
vc->vt_mode.relsig = 0;
@@ -1309,6 +1017,7 @@ void vc_SAK(struct work_struct *work)
console_lock();
vc = vc_con->d;
if (vc) {
+ /* FIXME: review tty ref counting */
tty = vc->port.tty;
/*
* SAK should also work in all raw modes and reset
diff --git a/include/linux/kbd_kern.h b/include/linux/kbd_kern.h
index ec2d17b..daf4a3a 100644
--- a/include/linux/kbd_kern.h
+++ b/include/linux/kbd_kern.h
@@ -7,8 +7,6 @@
extern struct tasklet_struct keyboard_tasklet;
-extern int shift_state;
-
extern char *func_table[MAX_NR_FUNC];
extern char func_buf[];
extern char *funcbufptr;
@@ -65,8 +63,6 @@ struct kbd_struct {
#define VC_META 4 /* 0 - meta, 1 - meta=prefix with ESC */
};
-extern struct kbd_struct kbd_table[];
-
extern int kbd_init(void);
extern unsigned char getledstate(void);
@@ -79,6 +75,7 @@ extern void (*kbd_ledfunc)(unsigned int led);
extern int set_console(int nr);
extern void schedule_console_callback(void);
+/* FIXME: review locking for vt.c callers */
static inline void set_leds(void)
{
tasklet_schedule(&keyboard_tasklet);
@@ -142,8 +139,6 @@ static inline void chg_vc_kbd_led(struct kbd_struct * kbd, int flag)
struct console;
-int getkeycode(unsigned int scancode);
-int setkeycode(unsigned int scancode, unsigned int keycode);
void compute_shiftstate(void);
/* defkeymap.c */
diff --git a/include/linux/keyboard.h b/include/linux/keyboard.h
index 33a63f6..86e5214 100644
--- a/include/linux/keyboard.h
+++ b/include/linux/keyboard.h
@@ -24,8 +24,6 @@
#ifdef __KERNEL__
struct notifier_block;
-extern const int NR_TYPES;
-extern const int max_vals[];
extern unsigned short *key_maps[MAX_NR_KEYMAPS];
extern unsigned short plain_map[NR_KEYS];
diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h
index 5d8726a..e33d77f 100644
--- a/include/linux/vt_kern.h
+++ b/include/linux/vt_kern.h
@@ -169,5 +169,28 @@ extern void hide_boot_cursor(bool hide);
/* keyboard provided interfaces */
extern int vt_do_diacrit(unsigned int cmd, void __user *up, int eperm);
+extern int vt_do_kdskbmode(int console, unsigned int arg);
+extern int vt_do_kdskbmeta(int console, unsigned int arg);
+extern int vt_do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc,
+ int perm);
+extern int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe,
+ int perm, int console);
+extern int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb,
+ int perm);
+extern int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm);
+extern int vt_do_kdgkbmode(int console);
+extern int vt_do_kdgkbmeta(int console);
+extern void vt_reset_unicode(int console);
+extern int vt_get_shift_state(void);
+extern void vt_reset_keyboard(int console);
+extern int vt_get_leds(int console, int flag);
+extern int vt_get_kbd_mode_bit(int console, int bit);
+extern void vt_set_kbd_mode_bit(int console, int bit);
+extern void vt_clr_kbd_mode_bit(int console, int bit);
+extern void vt_set_led_state(int console, int leds);
+extern void vt_set_led_state(int console, int leds);
+extern void vt_kbd_con_start(int console);
+extern void vt_kbd_con_stop(int console);
+
#endif /* _VT_KERN_H */
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox