Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f2d937f3 authored by Jason Wessel's avatar Jason Wessel Committed by Ingo Molnar
Browse files

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: default avatarJason Wessel <jason.wessel@windriver.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
Signed-off-by: default avatarJan Kiszka <jan.kiszka@web.de>
Reviewed-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent dc7d5527
Loading
Loading
Loading
Loading
+47 −0
Original line number Original line Diff line number Diff line
@@ -1155,6 +1155,48 @@ static struct tty_driver *get_tty_driver(dev_t device, int *index)
	return NULL;
	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_check_change	-	check for POSIX terminal changes
 *	@tty: tty to check
 *	@tty: tty to check
@@ -3850,6 +3892,11 @@ void tty_set_operations(struct tty_driver *driver,
	driver->write_proc = op->write_proc;
	driver->write_proc = op->write_proc;
	driver->tiocmget = op->tiocmget;
	driver->tiocmget = op->tiocmget;
	driver->tiocmset = op->tiocmset;
	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
}
}




+58 −0
Original line number Original line Diff line number Diff line
@@ -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)
static int serial8250_startup(struct uart_port *port)
{
{
	struct uart_8250_port *up = (struct uart_8250_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,
	.request_port	= serial8250_request_port,
	.config_port	= serial8250_config_port,
	.config_port	= serial8250_config_port,
	.verify_port	= serial8250_verify_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];
static struct uart_8250_port serial8250_ports[UART_NR];
+3 −0
Original line number Original line Diff line number Diff line
@@ -961,6 +961,9 @@ config SERIAL_CORE
config SERIAL_CORE_CONSOLE
config SERIAL_CORE_CONSOLE
	bool
	bool


config CONSOLE_POLL
	bool

config SERIAL_68328
config SERIAL_68328
	bool "68328 serial support"
	bool "68328 serial support"
	depends on M68328 || M68EZ328 || M68VZ328
	depends on M68328 || M68EZ328 || M68VZ328
+1 −0
Original line number Original line Diff line number Diff line
@@ -66,4 +66,5 @@ obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o
obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o
obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o
obj-$(CONFIG_SERIAL_QE) += ucc_uart.o
obj-$(CONFIG_SERIAL_QE) += ucc_uart.o
+163 −0
Original line number Original line Diff line number Diff line
/*
 * 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");
Loading