diff options
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/Kconfig | 15 | ||||
-rw-r--r-- | drivers/char/Makefile | 1 | ||||
-rw-r--r-- | drivers/char/ttyprintk.c | 225 |
3 files changed, 241 insertions, 0 deletions
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 | ||
496 | config 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 | |||
496 | config BRIQ_PANEL | 511 | config 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 | |||
12 | obj-y += tty_mutex.o | 12 | obj-y += tty_mutex.o |
13 | obj-$(CONFIG_LEGACY_PTYS) += pty.o | 13 | obj-$(CONFIG_LEGACY_PTYS) += pty.o |
14 | obj-$(CONFIG_UNIX98_PTYS) += pty.o | 14 | obj-$(CONFIG_UNIX98_PTYS) += pty.o |
15 | obj-$(CONFIG_TTY_PRINTK) += ttyprintk.o | ||
15 | obj-y += misc.o | 16 | obj-y += misc.o |
16 | obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o selection.o keyboard.o | 17 | obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o selection.o keyboard.o |
17 | obj-$(CONFIG_BFIN_JTAG_COMM) += bfin_jtag_comm.o | 18 | obj-$(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 | |||
21 | struct ttyprintk_port { | ||
22 | struct tty_port port; | ||
23 | struct mutex port_write_mutex; | ||
24 | }; | ||
25 | |||
26 | static 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 */ | ||
39 | static const char *tpk_tag = "[U] "; /* U for User */ | ||
40 | static int tpk_curr; | ||
41 | |||
42 | static 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 | */ | ||
96 | static 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 | */ | ||
106 | static 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 | */ | ||
121 | static 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 | */ | ||
139 | static int tpk_write_room(struct tty_struct *tty) | ||
140 | { | ||
141 | return TPK_MAX_ROOM; | ||
142 | } | ||
143 | |||
144 | /* | ||
145 | * TTY operations ioctl function. | ||
146 | */ | ||
147 | static 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 | |||
165 | static 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 | |||
173 | struct tty_port_operations null_ops = { }; | ||
174 | |||
175 | static struct tty_driver *ttyprintk_driver; | ||
176 | |||
177 | static 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 | |||
220 | error: | ||
221 | put_tty_driver(ttyprintk_driver); | ||
222 | ttyprintk_driver = NULL; | ||
223 | return ret; | ||
224 | } | ||
225 | module_init(ttyprintk_init); | ||