aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSamo Pogacnik <samo_pogacnik@t-2.net>2010-08-25 14:44:07 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2010-10-22 13:20:02 -0400
commit24b4b67d17c308aaa956b73ab1e88190f6642bbe (patch)
tree86ff019ce69aca93371719487869de4ad6d205e0
parent891b9dd10764352926e1e107756aa229dfa2c210 (diff)
add ttyprintk driver
Ttyprintk is a pseudo TTY driver, which allows users to make printk messages, via output to ttyprintk device. It is possible to store "console" messages inline with kernel messages for better analyses of the boot process, for example. Signed-off-by: Samo Pogacnik <samo_pogacnik@t-2.net> Acked-by: Alan Cox <alan@lxorguk.ukuu.org.uk> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--Documentation/devices.txt1
-rw-r--r--drivers/char/Kconfig15
-rw-r--r--drivers/char/Makefile1
-rw-r--r--drivers/char/ttyprintk.c225
4 files changed, 242 insertions, 0 deletions
diff --git a/Documentation/devices.txt b/Documentation/devices.txt
index d0d1df6cb5de..6a08fd731d28 100644
--- a/Documentation/devices.txt
+++ b/Documentation/devices.txt
@@ -239,6 +239,7 @@ Your cooperation is appreciated.
239 0 = /dev/tty Current TTY device 239 0 = /dev/tty Current TTY device
240 1 = /dev/console System console 240 1 = /dev/console System console
241 2 = /dev/ptmx PTY master multiplex 241 2 = /dev/ptmx PTY master multiplex
242 3 = /dev/ttyprintk User messages via printk TTY device
242 64 = /dev/cua0 Callout device for ttyS0 243 64 = /dev/cua0 Callout device for ttyS0
243 ... 244 ...
244 255 = /dev/cua191 Callout device for ttyS191 245 255 = /dev/cua191 Callout device for ttyS191
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 3d44ec724c17..43d3395325c5 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -493,6 +493,21 @@ config LEGACY_PTY_COUNT
493 When not in use, each legacy PTY occupies 12 bytes on 32-bit 493 When not in use, each legacy PTY occupies 12 bytes on 32-bit
494 architectures and 24 bytes on 64-bit architectures. 494 architectures and 24 bytes on 64-bit architectures.
495 495
496config TTY_PRINTK
497 bool "TTY driver to output user messages via printk"
498 depends on EMBEDDED
499 default n
500 ---help---
501 If you say Y here, the support for writing user messages (i.e.
502 console messages) via printk is available.
503
504 The feature is useful to inline user messages with kernel
505 messages.
506 In order to use this feature, you should output user messages
507 to /dev/ttyprintk or redirect console to this TTY.
508
509 If unsure, say N.
510
496config BRIQ_PANEL 511config BRIQ_PANEL
497 tristate 'Total Impact briQ front panel driver' 512 tristate 'Total Impact briQ front panel driver'
498 depends on PPC_CHRP 513 depends on PPC_CHRP
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index dc9641660605..3a9c01416839 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -12,6 +12,7 @@ obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o t
12obj-y += tty_mutex.o 12obj-y += tty_mutex.o
13obj-$(CONFIG_LEGACY_PTYS) += pty.o 13obj-$(CONFIG_LEGACY_PTYS) += pty.o
14obj-$(CONFIG_UNIX98_PTYS) += pty.o 14obj-$(CONFIG_UNIX98_PTYS) += pty.o
15obj-$(CONFIG_TTY_PRINTK) += ttyprintk.o
15obj-y += misc.o 16obj-y += misc.o
16obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o selection.o keyboard.o 17obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o selection.o keyboard.o
17obj-$(CONFIG_BFIN_JTAG_COMM) += bfin_jtag_comm.o 18obj-$(CONFIG_BFIN_JTAG_COMM) += bfin_jtag_comm.o
diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c
new file mode 100644
index 000000000000..c40c1612c8a7
--- /dev/null
+++ b/drivers/char/ttyprintk.c
@@ -0,0 +1,225 @@
1/*
2 * linux/drivers/char/ttyprintk.c
3 *
4 * Copyright (C) 2010 Samo Pogacnik
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the smems of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 */
10
11/*
12 * This pseudo device allows user to make printk messages. It is possible
13 * to store "console" messages inline with kernel messages for better analyses
14 * of the boot process, for example.
15 */
16
17#include <linux/device.h>
18#include <linux/serial.h>
19#include <linux/tty.h>
20
21struct ttyprintk_port {
22 struct tty_port port;
23 struct mutex port_write_mutex;
24};
25
26static struct ttyprintk_port tpk_port;
27
28/*
29 * Our simple preformatting supports transparent output of (time-stamped)
30 * printk messages (also suitable for logging service):
31 * - any cr is replaced by nl
32 * - adds a ttyprintk source tag in front of each line
33 * - too long message is fragmeted, with '\'nl between fragments
34 * - TPK_STR_SIZE isn't really the write_room limiting factor, bcause
35 * it is emptied on the fly during preformatting.
36 */
37#define TPK_STR_SIZE 508 /* should be bigger then max expected line length */
38#define TPK_MAX_ROOM 4096 /* we could assume 4K for instance */
39static const char *tpk_tag = "[U] "; /* U for User */
40static int tpk_curr;
41
42static int tpk_printk(const unsigned char *buf, int count)
43{
44 static char tmp[TPK_STR_SIZE + 4];
45 int i = tpk_curr;
46
47 if (buf == NULL) {
48 /* flush tmp[] */
49 if (tpk_curr > 0) {
50 /* non nl or cr terminated message - add nl */
51 tmp[tpk_curr + 0] = '\n';
52 tmp[tpk_curr + 1] = '\0';
53 printk(KERN_INFO "%s%s", tpk_tag, tmp);
54 tpk_curr = 0;
55 }
56 return i;
57 }
58
59 for (i = 0; i < count; i++) {
60 tmp[tpk_curr] = buf[i];
61 if (tpk_curr < TPK_STR_SIZE) {
62 switch (buf[i]) {
63 case '\r':
64 /* replace cr with nl */
65 tmp[tpk_curr + 0] = '\n';
66 tmp[tpk_curr + 1] = '\0';
67 printk(KERN_INFO "%s%s", tpk_tag, tmp);
68 tpk_curr = 0;
69 if (buf[i + 1] == '\n')
70 i++;
71 break;
72 case '\n':
73 tmp[tpk_curr + 1] = '\0';
74 printk(KERN_INFO "%s%s", tpk_tag, tmp);
75 tpk_curr = 0;
76 break;
77 default:
78 tpk_curr++;
79 }
80 } else {
81 /* end of tmp buffer reached: cut the message in two */
82 tmp[tpk_curr + 1] = '\\';
83 tmp[tpk_curr + 2] = '\n';
84 tmp[tpk_curr + 3] = '\0';
85 printk(KERN_INFO "%s%s", tpk_tag, tmp);
86 tpk_curr = 0;
87 }
88 }
89
90 return count;
91}
92
93/*
94 * TTY operations open function.
95 */
96static int tpk_open(struct tty_struct *tty, struct file *filp)
97{
98 tty->driver_data = &tpk_port;
99
100 return tty_port_open(&tpk_port.port, tty, filp);
101}
102
103/*
104 * TTY operations close function.
105 */
106static void tpk_close(struct tty_struct *tty, struct file *filp)
107{
108 struct ttyprintk_port *tpkp = tty->driver_data;
109
110 mutex_lock(&tpkp->port_write_mutex);
111 /* flush tpk_printk buffer */
112 tpk_printk(NULL, 0);
113 mutex_unlock(&tpkp->port_write_mutex);
114
115 tty_port_close(&tpkp->port, tty, filp);
116}
117
118/*
119 * TTY operations write function.
120 */
121static int tpk_write(struct tty_struct *tty,
122 const unsigned char *buf, int count)
123{
124 struct ttyprintk_port *tpkp = tty->driver_data;
125 int ret;
126
127
128 /* exclusive use of tpk_printk within this tty */
129 mutex_lock(&tpkp->port_write_mutex);
130 ret = tpk_printk(buf, count);
131 mutex_unlock(&tpkp->port_write_mutex);
132
133 return ret;
134}
135
136/*
137 * TTY operations write_room function.
138 */
139static int tpk_write_room(struct tty_struct *tty)
140{
141 return TPK_MAX_ROOM;
142}
143
144/*
145 * TTY operations ioctl function.
146 */
147static int tpk_ioctl(struct tty_struct *tty, struct file *file,
148 unsigned int cmd, unsigned long arg)
149{
150 struct ttyprintk_port *tpkp = tty->driver_data;
151
152 if (!tpkp)
153 return -EINVAL;
154
155 switch (cmd) {
156 /* Stop TIOCCONS */
157 case TIOCCONS:
158 return -EOPNOTSUPP;
159 default:
160 return -ENOIOCTLCMD;
161 }
162 return 0;
163}
164
165static const struct tty_operations ttyprintk_ops = {
166 .open = tpk_open,
167 .close = tpk_close,
168 .write = tpk_write,
169 .write_room = tpk_write_room,
170 .ioctl = tpk_ioctl,
171};
172
173struct tty_port_operations null_ops = { };
174
175static struct tty_driver *ttyprintk_driver;
176
177static int __init ttyprintk_init(void)
178{
179 int ret = -ENOMEM;
180 void *rp;
181
182 ttyprintk_driver = alloc_tty_driver(1);
183 if (!ttyprintk_driver)
184 return ret;
185
186 ttyprintk_driver->owner = THIS_MODULE;
187 ttyprintk_driver->driver_name = "ttyprintk";
188 ttyprintk_driver->name = "ttyprintk";
189 ttyprintk_driver->major = TTYAUX_MAJOR;
190 ttyprintk_driver->minor_start = 3;
191 ttyprintk_driver->num = 1;
192 ttyprintk_driver->type = TTY_DRIVER_TYPE_CONSOLE;
193 ttyprintk_driver->init_termios = tty_std_termios;
194 ttyprintk_driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET;
195 ttyprintk_driver->flags = TTY_DRIVER_RESET_TERMIOS |
196 TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
197 tty_set_operations(ttyprintk_driver, &ttyprintk_ops);
198
199 ret = tty_register_driver(ttyprintk_driver);
200 if (ret < 0) {
201 printk(KERN_ERR "Couldn't register ttyprintk driver\n");
202 goto error;
203 }
204
205 /* create our unnumbered device */
206 rp = device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 3), NULL,
207 ttyprintk_driver->name);
208 if (IS_ERR(rp)) {
209 printk(KERN_ERR "Couldn't create ttyprintk device\n");
210 ret = PTR_ERR(rp);
211 goto error;
212 }
213
214 tty_port_init(&tpk_port.port);
215 tpk_port.port.ops = &null_ops;
216 mutex_init(&tpk_port.port_write_mutex);
217
218 return 0;
219
220error:
221 put_tty_driver(ttyprintk_driver);
222 ttyprintk_driver = NULL;
223 return ret;
224}
225module_init(ttyprintk_init);