ttyprintk.c (5270B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * linux/drivers/char/ttyprintk.c 4 * 5 * Copyright (C) 2010 Samo Pogacnik 6 */ 7 8/* 9 * This pseudo device allows user to make printk messages. It is possible 10 * to store "console" messages inline with kernel messages for better analyses 11 * of the boot process, for example. 12 */ 13 14#include <linux/console.h> 15#include <linux/device.h> 16#include <linux/serial.h> 17#include <linux/tty.h> 18#include <linux/module.h> 19#include <linux/spinlock.h> 20 21struct ttyprintk_port { 22 struct tty_port port; 23 spinlock_t spinlock; 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 fragmented, with '\'nl between fragments 34 * - TPK_STR_SIZE isn't really the write_room limiting factor, because 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#define TPK_PREFIX KERN_SOH __stringify(CONFIG_TTY_PRINTK_LEVEL) 40 41static int tpk_curr; 42 43static char tpk_buffer[TPK_STR_SIZE + 4]; 44 45static void tpk_flush(void) 46{ 47 if (tpk_curr > 0) { 48 tpk_buffer[tpk_curr] = '\0'; 49 printk(TPK_PREFIX "[U] %s\n", tpk_buffer); 50 tpk_curr = 0; 51 } 52} 53 54static int tpk_printk(const unsigned char *buf, int count) 55{ 56 int i; 57 58 for (i = 0; i < count; i++) { 59 if (tpk_curr >= TPK_STR_SIZE) { 60 /* end of tmp buffer reached: cut the message in two */ 61 tpk_buffer[tpk_curr++] = '\\'; 62 tpk_flush(); 63 } 64 65 switch (buf[i]) { 66 case '\r': 67 tpk_flush(); 68 if ((i + 1) < count && buf[i + 1] == '\n') 69 i++; 70 break; 71 case '\n': 72 tpk_flush(); 73 break; 74 default: 75 tpk_buffer[tpk_curr++] = buf[i]; 76 break; 77 } 78 } 79 80 return count; 81} 82 83/* 84 * TTY operations open function. 85 */ 86static int tpk_open(struct tty_struct *tty, struct file *filp) 87{ 88 tty->driver_data = &tpk_port; 89 90 return tty_port_open(&tpk_port.port, tty, filp); 91} 92 93/* 94 * TTY operations close function. 95 */ 96static void tpk_close(struct tty_struct *tty, struct file *filp) 97{ 98 struct ttyprintk_port *tpkp = tty->driver_data; 99 100 tty_port_close(&tpkp->port, tty, filp); 101} 102 103/* 104 * TTY operations write function. 105 */ 106static int tpk_write(struct tty_struct *tty, 107 const unsigned char *buf, int count) 108{ 109 struct ttyprintk_port *tpkp = tty->driver_data; 110 unsigned long flags; 111 int ret; 112 113 /* exclusive use of tpk_printk within this tty */ 114 spin_lock_irqsave(&tpkp->spinlock, flags); 115 ret = tpk_printk(buf, count); 116 spin_unlock_irqrestore(&tpkp->spinlock, flags); 117 118 return ret; 119} 120 121/* 122 * TTY operations write_room function. 123 */ 124static unsigned int tpk_write_room(struct tty_struct *tty) 125{ 126 return TPK_MAX_ROOM; 127} 128 129/* 130 * TTY operations hangup function. 131 */ 132static void tpk_hangup(struct tty_struct *tty) 133{ 134 struct ttyprintk_port *tpkp = tty->driver_data; 135 136 tty_port_hangup(&tpkp->port); 137} 138 139/* 140 * TTY port operations shutdown function. 141 */ 142static void tpk_port_shutdown(struct tty_port *tport) 143{ 144 struct ttyprintk_port *tpkp = 145 container_of(tport, struct ttyprintk_port, port); 146 unsigned long flags; 147 148 spin_lock_irqsave(&tpkp->spinlock, flags); 149 tpk_flush(); 150 spin_unlock_irqrestore(&tpkp->spinlock, flags); 151} 152 153static const struct tty_operations ttyprintk_ops = { 154 .open = tpk_open, 155 .close = tpk_close, 156 .write = tpk_write, 157 .write_room = tpk_write_room, 158 .hangup = tpk_hangup, 159}; 160 161static const struct tty_port_operations tpk_port_ops = { 162 .shutdown = tpk_port_shutdown, 163}; 164 165static struct tty_driver *ttyprintk_driver; 166 167static struct tty_driver *ttyprintk_console_device(struct console *c, 168 int *index) 169{ 170 *index = 0; 171 return ttyprintk_driver; 172} 173 174static struct console ttyprintk_console = { 175 .name = "ttyprintk", 176 .device = ttyprintk_console_device, 177}; 178 179static int __init ttyprintk_init(void) 180{ 181 int ret; 182 183 spin_lock_init(&tpk_port.spinlock); 184 185 ttyprintk_driver = tty_alloc_driver(1, 186 TTY_DRIVER_RESET_TERMIOS | 187 TTY_DRIVER_REAL_RAW | 188 TTY_DRIVER_UNNUMBERED_NODE); 189 if (IS_ERR(ttyprintk_driver)) 190 return PTR_ERR(ttyprintk_driver); 191 192 tty_port_init(&tpk_port.port); 193 tpk_port.port.ops = &tpk_port_ops; 194 195 ttyprintk_driver->driver_name = "ttyprintk"; 196 ttyprintk_driver->name = "ttyprintk"; 197 ttyprintk_driver->major = TTYAUX_MAJOR; 198 ttyprintk_driver->minor_start = 3; 199 ttyprintk_driver->type = TTY_DRIVER_TYPE_CONSOLE; 200 ttyprintk_driver->init_termios = tty_std_termios; 201 ttyprintk_driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET; 202 tty_set_operations(ttyprintk_driver, &ttyprintk_ops); 203 tty_port_link_device(&tpk_port.port, ttyprintk_driver, 0); 204 205 ret = tty_register_driver(ttyprintk_driver); 206 if (ret < 0) { 207 printk(KERN_ERR "Couldn't register ttyprintk driver\n"); 208 goto error; 209 } 210 211 register_console(&ttyprintk_console); 212 213 return 0; 214 215error: 216 tty_driver_kref_put(ttyprintk_driver); 217 tty_port_destroy(&tpk_port.port); 218 return ret; 219} 220 221static void __exit ttyprintk_exit(void) 222{ 223 unregister_console(&ttyprintk_console); 224 tty_unregister_driver(ttyprintk_driver); 225 tty_driver_kref_put(ttyprintk_driver); 226 tty_port_destroy(&tpk_port.port); 227} 228 229device_initcall(ttyprintk_init); 230module_exit(ttyprintk_exit); 231 232MODULE_LICENSE("GPL");