> > +static int tpk_write_room(struct tty_struct *tty) > > +{ > > + int ret = tpk_space; > > + > > + /* allow char by char under max pressure */ > > + if (tpk_space == 0) > > + tpk_space = 1; > > That won't do what you think, the ldisc will keep seeing progress and > generate millions of 1 byte I/Os in a loop ! > Somewhat changed to slowly increase delay in write, if ratelimiting doesn't end. > Otherwise looks excellent. > Thanks to you. > > > + switch (cmd) { > > + /* Stop TIOCCONS */ > > + case TIOCCONS: > > + return -EOPNOTSUPP; > > And I'll fix this bit up to work properly in the core code. > > > With my devices.txt owner hat on I'll allocate the minor as you suggest > (and double check this causes no problems), with my tty hat on can you > send it to GregKH for merging into the tree. I am not sure if i understand. Should i exclude devices.txt from patch before sending it to GregKH? Samo --- Signed-off-by: Samo Pogacnik <samo_pogacnik@xxxxxxx> diff --git a_linux/Documentation/devices.txt b_linux/Documentation/devices.txt index 53d64d3..71aef33 100644 --- a_linux/Documentation/devices.txt +++ b_linux/Documentation/devices.txt @@ -239,6 +239,7 @@ Your cooperation is appreciated. 0 = /dev/tty Current TTY device 1 = /dev/console System console 2 = /dev/ptmx PTY master multiplex + 3 = /dev/ttyprintk User messages via printk TTY device 64 = /dev/cua0 Callout device for ttyS0 ... 255 = /dev/cua191 Callout device for ttyS191 diff --git a_linux/drivers/char/Kconfig b_linux/drivers/char/Kconfig index 3141dd3..5c38a06 100644 --- a_linux/drivers/char/Kconfig +++ b_linux/drivers/char/Kconfig @@ -485,6 +485,20 @@ config LEGACY_PTY_COUNT When not in use, each legacy PTY occupies 12 bytes on 32-bit architectures and 24 bytes on 64-bit architectures. +config TTY_PRINTK + bool "TTY driver to output user messages via printk" + default n + ---help--- + If you say Y here, the support for writing user messages (i.e. + console messages) via printk is available. + + The feature is useful to inline user messages with kernel + messages. + In order to use this feature, you should output user messages + to /dev/ttyprintk or redirect console to this TTY. + + If unsure, say N. + config BRIQ_PANEL tristate 'Total Impact briQ front panel driver' depends on PPC_CHRP diff --git a_linux/drivers/char/Makefile b_linux/drivers/char/Makefile index f957edf..ed60f45 100644 --- a_linux/drivers/char/Makefile +++ b_linux/drivers/char/Makefile @@ -11,6 +11,7 @@ obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o t obj-$(CONFIG_LEGACY_PTYS) += pty.o obj-$(CONFIG_UNIX98_PTYS) += pty.o +obj-$(CONFIG_TTY_PRINTK) += ttyprintk.o obj-y += misc.o obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o selection.o keyboard.o obj-$(CONFIG_BFIN_JTAG_COMM) += bfin_jtag_comm.o diff --git a_linux/drivers/char/ttyprintk.c b_linux/drivers/char/ttyprintk.c new file mode 100644 index 0000000..2fefa93 --- /dev/null +++ b_linux/drivers/char/ttyprintk.c @@ -0,0 +1,297 @@ +/* + * linux/drivers/char/ttyprintk.c + * + * Copyright (C) 2010 Samo Pogacnik + * + * This program is free software; you can redistribute it and/or modify + * it under the smems of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +/* + * This pseudo device allows user to make printk messages. It is possible + * to store "console" messages inline with kernel messages for better analyses + * of the boot process, for example. + */ + +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/ratelimit.h> +#include <linux/device.h> +#include <linux/serial.h> +#include <linux/tty.h> + +struct ttyprintk_port { + struct tty_port port; + struct mutex port_write_mutex; +}; + +static struct ttyprintk_port tpk_port; + +#define TTY_PRINTK_STR_SIZE 508 +static int tpk_space = TTY_PRINTK_STR_SIZE; +static const char *tpk_tag = "[U] "; /* U for User */ + +/* + * Ratelimiting support to handle to much output to this device, + * because of explicit writes or because of unintentional loop + * setup (caught printks again sent to this device). + */ +static struct ratelimit_state ttyprintk_rs = { + .interval = DEFAULT_RATELIMIT_INTERVAL, + .burst = DEFAULT_RATELIMIT_BURST, +}; + +static int tpk_ratelimiting; + +#define ttyprintk_printk(fmt, ...) \ +{ \ + printk(KERN_INFO fmt, ##__VA_ARGS__); \ +} + +/* + * Our private ratelimit function, to suppress its printk warnings about + * missed callbacks, which are irrelevant in a flow control mechanism. + */ +static void ttyprintk_ratelimit(struct ratelimit_state *rs, int count) +{ + /* clear ratelimit missed callbacks counter */ + rs->missed = 0; + if (__ratelimit(rs)) { + tpk_ratelimiting = 0; + tpk_space = TTY_PRINTK_STR_SIZE; + rs->burst = DEFAULT_RATELIMIT_BURST; + } else { + tpk_ratelimiting = 1; + if (TTY_PRINTK_STR_SIZE > count) + tpk_space = TTY_PRINTK_STR_SIZE - count; + else + tpk_space = 0; + } +} + +/* + * Our simple preformatting supports transparent output of (time-stamped) + * printk messages (also suitable for logging service): + * - any cr is replaced by nl + * - adds a ttyprintk source tag in front of each line + * - too long message is fragmeted, with '\'nl between fragments + */ +static int tpk_printk(const unsigned char *buf, int count) +{ + static char tmp[TTY_PRINTK_STR_SIZE + 4]; + static int curr; + int i = curr; + + if (buf == NULL) { + /* flush tmp[] */ + if (curr > 0) { + /* non nl or cr terminated message - add nl */ + tmp[curr + 0] = '\n'; + tmp[curr + 1] = '\0'; + ttyprintk_printk("%s%s", tpk_tag, tmp); + curr = 0; + } + return i; + } + + for (i = 0; i < count; i++) { + tmp[curr] = buf[i]; + if (curr < TTY_PRINTK_STR_SIZE) { + switch (buf[i]) { + case '\r': + /* replace cr with nl */ + tmp[curr + 0] = '\n'; + tmp[curr + 1] = '\0'; + ttyprintk_printk("%s%s", tpk_tag, tmp); + curr = 0; + if (buf[i + 1] == '\n') + i++; + break; + case '\n': + tmp[curr + 1] = '\0'; + ttyprintk_printk("%s%s", tpk_tag, tmp); + curr = 0; + break; + default: + curr++; + } + } else { + /* end of tmp buffer reached: cut the message in two */ + tmp[curr + 1] = '\\'; + tmp[curr + 2] = '\n'; + tmp[curr + 3] = '\0'; + ttyprintk_printk("%s%s", tpk_tag, tmp); + curr = 0; + } + } + if ((tpk_ratelimiting == 0) && (curr > 0)) { + /* non nl or cr terminated message - add nl */ + tmp[curr + 0] = '\n'; + tmp[curr + 1] = '\0'; + ttyprintk_printk("%s%s", tpk_tag, tmp); + curr = 0; + } + + return count; +} + +/* + * TTY operations open function. + */ +static int tpk_open(struct tty_struct *tty, struct file *filp) +{ + tty->driver_data = &tpk_port; + + return tty_port_open(&tpk_port.port, tty, filp); +} + +/* + * TTY operations close function. + */ +static void tpk_close(struct tty_struct *tty, struct file *filp) +{ + struct ttyprintk_port *tpkp = tty->driver_data; + + mutex_lock(&tpkp->port_write_mutex); + /* flush tpk_printk buffer */ + tpk_printk(NULL, 0); + tpk_space = TTY_PRINTK_STR_SIZE; + tpk_ratelimiting = 0; + mutex_unlock(&tpkp->port_write_mutex); + + tty_port_close(&tpkp->port, tty, filp); +} + +/* + * TTY operations write function. + */ +static int tpk_write(struct tty_struct *tty, + const unsigned char *buf, int count) +{ + static unsigned int tpk_write_delay; + struct ttyprintk_port *tpkp = tty->driver_data; + int ret; + + + /* exclusive use of tpk_printk within this tty */ + mutex_lock(&tpkp->port_write_mutex); + ttyprintk_ratelimit(&ttyprintk_rs, count); + if (tpk_ratelimiting == 0) { + tpk_write_delay = 0; + } else { + /* increase delay under pressure upto 10 secs */ + if (tpk_write_delay < 1000000) + tpk_write_delay++; + msleep_interruptible(tpk_write_delay / 100); + + /* eliminate delay in ratelimiting */ + ttyprintk_rs.burst = 1; + ttyprintk_rs.begin = 0; + __ratelimit(&ttyprintk_rs); + } + + ret = tpk_printk(buf, count); + mutex_unlock(&tpkp->port_write_mutex); + + return ret; +} + +/* + * TTY operations write_room function. + */ +static int tpk_write_room(struct tty_struct *tty) +{ + int ret = tpk_space; + + /* just in case we reach zero space, let one char available */ + if (tpk_space == 0) + tpk_space = 1; + + return ret; +} + +/* + * TTY operations ioctl function. + */ +static int tpk_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ttyprintk_port *port; + + port = tty->driver_data; + + if (!port) + return -EINVAL; + + switch (cmd) { + /* Stop TIOCCONS */ + case TIOCCONS: + return -EOPNOTSUPP; + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static const struct tty_operations ttyprintk_ops = { + .open = tpk_open, + .close = tpk_close, + .write = tpk_write, + .write_room = tpk_write_room, + .ioctl = tpk_ioctl, +}; + +struct tty_port_operations null_ops = { }; + +static struct tty_driver *ttyprintk_driver; + +static int __init ttyprintk_init(void) +{ + int ret = -ENOMEM; + void *rp; + + ttyprintk_driver = alloc_tty_driver(1); + if (!ttyprintk_driver) + return ret; + + ttyprintk_driver->owner = THIS_MODULE; + ttyprintk_driver->driver_name = "ttyprintk"; + ttyprintk_driver->name = "ttyprintk"; + ttyprintk_driver->major = TTYAUX_MAJOR; + ttyprintk_driver->minor_start = 3; + ttyprintk_driver->num = 1; + ttyprintk_driver->type = TTY_DRIVER_TYPE_CONSOLE; + ttyprintk_driver->init_termios = tty_std_termios; + ttyprintk_driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET; + ttyprintk_driver->flags = TTY_DRIVER_RESET_TERMIOS | + TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + tty_set_operations(ttyprintk_driver, &ttyprintk_ops); + + ret = tty_register_driver(ttyprintk_driver); + if (ret < 0) { + printk(KERN_ERR "Couldn't register ttyprintk driver\n"); + goto error; + } + + /* create our unnumbered device */ + rp = device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 3), NULL, + ttyprintk_driver->name); + if (IS_ERR(rp)) { + printk(KERN_ERR "Couldn't create ttyprintk device\n"); + ret = PTR_ERR(rp); + goto error; + } + + tty_port_init(&tpk_port.port); + tpk_port.port.ops = &null_ops; + mutex_init(&tpk_port.port_write_mutex); + + return 0; + +error: + put_tty_driver(ttyprintk_driver); + ttyprintk_driver = NULL; + return ret; +} +module_init(ttyprintk_init); -- To unsubscribe from this list: send the line "unsubscribe linux-embedded" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html