consoles: polling support, kgdboc
polled console handling support, to access a console in an irq-less way while in debug or irq context. absolutely zero impact as long as CONFIG_CONSOLE_POLL is disabled. (which is the default) [ jan.kiszka@siemens.com: lots of cleanups ] [ mingo@elte.hu: redesign, splitups, cleanups. ] Signed-off-by: Jason Wessel <jason.wessel@windriver.com> Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Jan Kiszka <jan.kiszka@web.de> Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
This commit is contained in:
parent
dc7d552705
commit
f2d937f3bf
8 changed files with 357 additions and 3 deletions
|
@ -1155,6 +1155,48 @@ static struct tty_driver *get_tty_driver(dev_t device, int *index)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CONSOLE_POLL
|
||||
|
||||
/**
|
||||
* tty_find_polling_driver - find device of a polled tty
|
||||
* @name: name string to match
|
||||
* @line: pointer to resulting tty line nr
|
||||
*
|
||||
* This routine returns a tty driver structure, given a name
|
||||
* and the condition that the tty driver is capable of polled
|
||||
* operation.
|
||||
*/
|
||||
struct tty_driver *tty_find_polling_driver(char *name, int *line)
|
||||
{
|
||||
struct tty_driver *p, *res = NULL;
|
||||
int tty_line = 0;
|
||||
char *str;
|
||||
|
||||
mutex_lock(&tty_mutex);
|
||||
/* Search through the tty devices to look for a match */
|
||||
list_for_each_entry(p, &tty_drivers, tty_drivers) {
|
||||
str = name + strlen(p->name);
|
||||
tty_line = simple_strtoul(str, &str, 10);
|
||||
if (*str == ',')
|
||||
str++;
|
||||
if (*str == '\0')
|
||||
str = 0;
|
||||
|
||||
if (tty_line >= 0 && tty_line <= p->num && p->poll_init &&
|
||||
!p->poll_init(p, tty_line, str)) {
|
||||
|
||||
res = p;
|
||||
*line = tty_line;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&tty_mutex);
|
||||
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tty_find_polling_driver);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* tty_check_change - check for POSIX terminal changes
|
||||
* @tty: tty to check
|
||||
|
@ -3850,6 +3892,11 @@ void tty_set_operations(struct tty_driver *driver,
|
|||
driver->write_proc = op->write_proc;
|
||||
driver->tiocmget = op->tiocmget;
|
||||
driver->tiocmset = op->tiocmset;
|
||||
#ifdef CONFIG_CONSOLE_POLL
|
||||
driver->poll_init = op->poll_init;
|
||||
driver->poll_get_char = op->poll_get_char;
|
||||
driver->poll_put_char = op->poll_put_char;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1740,6 +1740,60 @@ static inline void wait_for_xmitr(struct uart_8250_port *up, int bits)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CONSOLE_POLL
|
||||
/*
|
||||
* Console polling routines for writing and reading from the uart while
|
||||
* in an interrupt or debug context.
|
||||
*/
|
||||
|
||||
static int serial8250_get_poll_char(struct uart_port *port)
|
||||
{
|
||||
struct uart_8250_port *up = (struct uart_8250_port *)port;
|
||||
unsigned char lsr = serial_inp(up, UART_LSR);
|
||||
|
||||
while (!(lsr & UART_LSR_DR))
|
||||
lsr = serial_inp(up, UART_LSR);
|
||||
|
||||
return serial_inp(up, UART_RX);
|
||||
}
|
||||
|
||||
|
||||
static void serial8250_put_poll_char(struct uart_port *port,
|
||||
unsigned char c)
|
||||
{
|
||||
unsigned int ier;
|
||||
struct uart_8250_port *up = (struct uart_8250_port *)port;
|
||||
|
||||
/*
|
||||
* First save the IER then disable the interrupts
|
||||
*/
|
||||
ier = serial_in(up, UART_IER);
|
||||
if (up->capabilities & UART_CAP_UUE)
|
||||
serial_out(up, UART_IER, UART_IER_UUE);
|
||||
else
|
||||
serial_out(up, UART_IER, 0);
|
||||
|
||||
wait_for_xmitr(up, BOTH_EMPTY);
|
||||
/*
|
||||
* Send the character out.
|
||||
* If a LF, also do CR...
|
||||
*/
|
||||
serial_out(up, UART_TX, c);
|
||||
if (c == 10) {
|
||||
wait_for_xmitr(up, BOTH_EMPTY);
|
||||
serial_out(up, UART_TX, 13);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finally, wait for transmitter to become empty
|
||||
* and restore the IER
|
||||
*/
|
||||
wait_for_xmitr(up, BOTH_EMPTY);
|
||||
serial_out(up, UART_IER, ier);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_CONSOLE_POLL */
|
||||
|
||||
static int serial8250_startup(struct uart_port *port)
|
||||
{
|
||||
struct uart_8250_port *up = (struct uart_8250_port *)port;
|
||||
|
@ -2386,6 +2440,10 @@ static struct uart_ops serial8250_pops = {
|
|||
.request_port = serial8250_request_port,
|
||||
.config_port = serial8250_config_port,
|
||||
.verify_port = serial8250_verify_port,
|
||||
#ifdef CONFIG_CONSOLE_POLL
|
||||
.poll_get_char = serial8250_get_poll_char,
|
||||
.poll_put_char = serial8250_put_poll_char,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct uart_8250_port serial8250_ports[UART_NR];
|
||||
|
|
|
@ -961,6 +961,9 @@ config SERIAL_CORE
|
|||
config SERIAL_CORE_CONSOLE
|
||||
bool
|
||||
|
||||
config CONSOLE_POLL
|
||||
bool
|
||||
|
||||
config SERIAL_68328
|
||||
bool "68328 serial support"
|
||||
depends on M68328 || M68EZ328 || M68VZ328
|
||||
|
|
|
@ -66,4 +66,5 @@ obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
|
|||
obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
|
||||
obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o
|
||||
obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
|
||||
obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o
|
||||
obj-$(CONFIG_SERIAL_QE) += ucc_uart.o
|
||||
|
|
163
drivers/serial/kgdboc.c
Normal file
163
drivers/serial/kgdboc.c
Normal file
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Based on the same principle as kgdboe using the NETPOLL api, this
|
||||
* driver uses a console polling api to implement a gdb serial inteface
|
||||
* which is multiplexed on a console port.
|
||||
*
|
||||
* Maintainer: Jason Wessel <jason.wessel@windriver.com>
|
||||
*
|
||||
* 2007-2008 (c) Jason Wessel - Wind River Systems, Inc.
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/kgdb.h>
|
||||
#include <linux/tty.h>
|
||||
|
||||
#define MAX_CONFIG_LEN 40
|
||||
|
||||
static struct kgdb_io kgdboc_io_ops;
|
||||
|
||||
/* -1 = init not run yet, 0 = unconfigured, 1 = configured. */
|
||||
static int configured = -1;
|
||||
|
||||
static char config[MAX_CONFIG_LEN];
|
||||
static struct kparam_string kps = {
|
||||
.string = config,
|
||||
.maxlen = MAX_CONFIG_LEN,
|
||||
};
|
||||
|
||||
static struct tty_driver *kgdb_tty_driver;
|
||||
static int kgdb_tty_line;
|
||||
|
||||
static int kgdboc_option_setup(char *opt)
|
||||
{
|
||||
if (strlen(opt) > MAX_CONFIG_LEN) {
|
||||
printk(KERN_ERR "kgdboc: config string too long\n");
|
||||
return -ENOSPC;
|
||||
}
|
||||
strcpy(config, opt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__setup("kgdboc=", kgdboc_option_setup);
|
||||
|
||||
static int configure_kgdboc(void)
|
||||
{
|
||||
struct tty_driver *p;
|
||||
int tty_line = 0;
|
||||
int err;
|
||||
|
||||
err = kgdboc_option_setup(config);
|
||||
if (err || !strlen(config) || isspace(config[0]))
|
||||
goto noconfig;
|
||||
|
||||
err = -ENODEV;
|
||||
|
||||
p = tty_find_polling_driver(config, &tty_line);
|
||||
if (!p)
|
||||
goto noconfig;
|
||||
|
||||
kgdb_tty_driver = p;
|
||||
kgdb_tty_line = tty_line;
|
||||
|
||||
err = kgdb_register_io_module(&kgdboc_io_ops);
|
||||
if (err)
|
||||
goto noconfig;
|
||||
|
||||
configured = 1;
|
||||
|
||||
return 0;
|
||||
|
||||
noconfig:
|
||||
config[0] = 0;
|
||||
configured = 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __init init_kgdboc(void)
|
||||
{
|
||||
/* Already configured? */
|
||||
if (configured == 1)
|
||||
return 0;
|
||||
|
||||
return configure_kgdboc();
|
||||
}
|
||||
|
||||
static void cleanup_kgdboc(void)
|
||||
{
|
||||
if (configured == 1)
|
||||
kgdb_unregister_io_module(&kgdboc_io_ops);
|
||||
}
|
||||
|
||||
static int kgdboc_get_char(void)
|
||||
{
|
||||
return kgdb_tty_driver->poll_get_char(kgdb_tty_driver, kgdb_tty_line);
|
||||
}
|
||||
|
||||
static void kgdboc_put_char(u8 chr)
|
||||
{
|
||||
kgdb_tty_driver->poll_put_char(kgdb_tty_driver, kgdb_tty_line, chr);
|
||||
}
|
||||
|
||||
static int param_set_kgdboc_var(const char *kmessage, struct kernel_param *kp)
|
||||
{
|
||||
if (strlen(kmessage) >= MAX_CONFIG_LEN) {
|
||||
printk(KERN_ERR "kgdboc: config string too long\n");
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
/* Only copy in the string if the init function has not run yet */
|
||||
if (configured < 0) {
|
||||
strcpy(config, kmessage);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (kgdb_connected) {
|
||||
printk(KERN_ERR
|
||||
"kgdboc: Cannot reconfigure while KGDB is connected.\n");
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
strcpy(config, kmessage);
|
||||
|
||||
if (configured == 1)
|
||||
cleanup_kgdboc();
|
||||
|
||||
/* Go and configure with the new params. */
|
||||
return configure_kgdboc();
|
||||
}
|
||||
|
||||
static void kgdboc_pre_exp_handler(void)
|
||||
{
|
||||
/* Increment the module count when the debugger is active */
|
||||
if (!kgdb_connected)
|
||||
try_module_get(THIS_MODULE);
|
||||
}
|
||||
|
||||
static void kgdboc_post_exp_handler(void)
|
||||
{
|
||||
/* decrement the module count when the debugger detaches */
|
||||
if (!kgdb_connected)
|
||||
module_put(THIS_MODULE);
|
||||
}
|
||||
|
||||
static struct kgdb_io kgdboc_io_ops = {
|
||||
.name = "kgdboc",
|
||||
.read_char = kgdboc_get_char,
|
||||
.write_char = kgdboc_put_char,
|
||||
.pre_exception = kgdboc_pre_exp_handler,
|
||||
.post_exception = kgdboc_post_exp_handler,
|
||||
};
|
||||
|
||||
module_init(init_kgdboc);
|
||||
module_exit(cleanup_kgdboc);
|
||||
module_param_call(kgdboc, param_set_kgdboc_var, param_get_string, &kps, 0644);
|
||||
MODULE_PARM_DESC(kgdboc, "<serial_device>[,baud]");
|
||||
MODULE_DESCRIPTION("KGDB Console TTY Driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1827,7 +1827,7 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co)
|
|||
* options. The format of the string is <baud><parity><bits><flow>,
|
||||
* eg: 115200n8r
|
||||
*/
|
||||
void __init
|
||||
void
|
||||
uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow)
|
||||
{
|
||||
char *s = options;
|
||||
|
@ -1842,6 +1842,7 @@ uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow)
|
|||
if (*s)
|
||||
*flow = *s;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(uart_parse_options);
|
||||
|
||||
struct baud_rates {
|
||||
unsigned int rate;
|
||||
|
@ -1872,7 +1873,7 @@ static const struct baud_rates baud_rates[] = {
|
|||
* @bits: number of data bits
|
||||
* @flow: flow control character - 'r' (rts)
|
||||
*/
|
||||
int __init
|
||||
int
|
||||
uart_set_options(struct uart_port *port, struct console *co,
|
||||
int baud, int parity, int bits, int flow)
|
||||
{
|
||||
|
@ -1924,10 +1925,16 @@ uart_set_options(struct uart_port *port, struct console *co,
|
|||
port->mctrl |= TIOCM_DTR;
|
||||
|
||||
port->ops->set_termios(port, &termios, &dummy);
|
||||
co->cflag = termios.c_cflag;
|
||||
/*
|
||||
* Allow the setting of the UART parameters with a NULL console
|
||||
* too:
|
||||
*/
|
||||
if (co)
|
||||
co->cflag = termios.c_cflag;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(uart_set_options);
|
||||
#endif /* CONFIG_SERIAL_CORE_CONSOLE */
|
||||
|
||||
static void uart_change_pm(struct uart_state *state, int pm_state)
|
||||
|
@ -2182,6 +2189,60 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state,
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CONSOLE_POLL
|
||||
|
||||
static int uart_poll_init(struct tty_driver *driver, int line, char *options)
|
||||
{
|
||||
struct uart_driver *drv = driver->driver_state;
|
||||
struct uart_state *state = drv->state + line;
|
||||
struct uart_port *port;
|
||||
int baud = 9600;
|
||||
int bits = 8;
|
||||
int parity = 'n';
|
||||
int flow = 'n';
|
||||
|
||||
if (!state || !state->port)
|
||||
return -1;
|
||||
|
||||
port = state->port;
|
||||
if (!(port->ops->poll_get_char && port->ops->poll_put_char))
|
||||
return -1;
|
||||
|
||||
if (options) {
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
return uart_set_options(port, NULL, baud, parity, bits, flow);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uart_poll_get_char(struct tty_driver *driver, int line)
|
||||
{
|
||||
struct uart_driver *drv = driver->driver_state;
|
||||
struct uart_state *state = drv->state + line;
|
||||
struct uart_port *port;
|
||||
|
||||
if (!state || !state->port)
|
||||
return -1;
|
||||
|
||||
port = state->port;
|
||||
return port->ops->poll_get_char(port);
|
||||
}
|
||||
|
||||
static void uart_poll_put_char(struct tty_driver *driver, int line, char ch)
|
||||
{
|
||||
struct uart_driver *drv = driver->driver_state;
|
||||
struct uart_state *state = drv->state + line;
|
||||
struct uart_port *port;
|
||||
|
||||
if (!state || !state->port)
|
||||
return;
|
||||
|
||||
port = state->port;
|
||||
port->ops->poll_put_char(port, ch);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct tty_operations uart_ops = {
|
||||
.open = uart_open,
|
||||
.close = uart_close,
|
||||
|
@ -2206,6 +2267,11 @@ static const struct tty_operations uart_ops = {
|
|||
#endif
|
||||
.tiocmget = uart_tiocmget,
|
||||
.tiocmset = uart_tiocmset,
|
||||
#ifdef CONFIG_CONSOLE_POLL
|
||||
.poll_init = uart_poll_init,
|
||||
.poll_get_char = uart_poll_get_char,
|
||||
.poll_put_char = uart_poll_put_char,
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -213,6 +213,10 @@ struct uart_ops {
|
|||
void (*config_port)(struct uart_port *, int);
|
||||
int (*verify_port)(struct uart_port *, struct serial_struct *);
|
||||
int (*ioctl)(struct uart_port *, unsigned int, unsigned long);
|
||||
#ifdef CONFIG_CONSOLE_POLL
|
||||
void (*poll_put_char)(struct uart_port *, unsigned char);
|
||||
int (*poll_get_char)(struct uart_port *);
|
||||
#endif
|
||||
};
|
||||
|
||||
#define UART_CONFIG_TYPE (1 << 0)
|
||||
|
|
|
@ -125,6 +125,7 @@
|
|||
#include <linux/cdev.h>
|
||||
|
||||
struct tty_struct;
|
||||
struct tty_driver;
|
||||
|
||||
struct tty_operations {
|
||||
int (*open)(struct tty_struct * tty, struct file * filp);
|
||||
|
@ -157,6 +158,11 @@ struct tty_operations {
|
|||
int (*tiocmget)(struct tty_struct *tty, struct file *file);
|
||||
int (*tiocmset)(struct tty_struct *tty, struct file *file,
|
||||
unsigned int set, unsigned int clear);
|
||||
#ifdef CONFIG_CONSOLE_POLL
|
||||
int (*poll_init)(struct tty_driver *driver, int line, char *options);
|
||||
int (*poll_get_char)(struct tty_driver *driver, int line);
|
||||
void (*poll_put_char)(struct tty_driver *driver, int line, char ch);
|
||||
#endif
|
||||
};
|
||||
|
||||
struct tty_driver {
|
||||
|
@ -220,6 +226,11 @@ struct tty_driver {
|
|||
int (*tiocmget)(struct tty_struct *tty, struct file *file);
|
||||
int (*tiocmset)(struct tty_struct *tty, struct file *file,
|
||||
unsigned int set, unsigned int clear);
|
||||
#ifdef CONFIG_CONSOLE_POLL
|
||||
int (*poll_init)(struct tty_driver *driver, int line, char *options);
|
||||
int (*poll_get_char)(struct tty_driver *driver, int line);
|
||||
void (*poll_put_char)(struct tty_driver *driver, int line, char ch);
|
||||
#endif
|
||||
|
||||
struct list_head tty_drivers;
|
||||
};
|
||||
|
@ -230,6 +241,7 @@ struct tty_driver *alloc_tty_driver(int lines);
|
|||
void put_tty_driver(struct tty_driver *driver);
|
||||
void tty_set_operations(struct tty_driver *driver,
|
||||
const struct tty_operations *op);
|
||||
extern struct tty_driver *tty_find_polling_driver(char *name, int *line);
|
||||
|
||||
/* tty driver magic number */
|
||||
#define TTY_DRIVER_MAGIC 0x5402
|
||||
|
|
Loading…
Reference in a new issue