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 u8 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 u8 *buf, size_t count) 55{ 56 size_t 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 ssize_t tpk_write(struct tty_struct *tty, const u8 *buf, size_t count) 107{ 108 struct ttyprintk_port *tpkp = tty->driver_data; 109 unsigned long flags; 110 int ret; 111 112 /* exclusive use of tpk_printk within this tty */ 113 spin_lock_irqsave(&tpkp->spinlock, flags); 114 ret = tpk_printk(buf, count); 115 spin_unlock_irqrestore(&tpkp->spinlock, flags); 116 117 return ret; 118} 119 120/* 121 * TTY operations write_room function. 122 */ 123static unsigned int tpk_write_room(struct tty_struct *tty) 124{ 125 return TPK_MAX_ROOM; 126} 127 128/* 129 * TTY operations hangup function. 130 */ 131static void tpk_hangup(struct tty_struct *tty) 132{ 133 struct ttyprintk_port *tpkp = tty->driver_data; 134 135 tty_port_hangup(&tpkp->port); 136} 137 138/* 139 * TTY port operations shutdown function. 140 */ 141static void tpk_port_shutdown(struct tty_port *tport) 142{ 143 struct ttyprintk_port *tpkp = 144 container_of(tport, struct ttyprintk_port, port); 145 unsigned long flags; 146 147 spin_lock_irqsave(&tpkp->spinlock, flags); 148 tpk_flush(); 149 spin_unlock_irqrestore(&tpkp->spinlock, flags); 150} 151 152static const struct tty_operations ttyprintk_ops = { 153 .open = tpk_open, 154 .close = tpk_close, 155 .write = tpk_write, 156 .write_room = tpk_write_room, 157 .hangup = tpk_hangup, 158}; 159 160static const struct tty_port_operations tpk_port_ops = { 161 .shutdown = tpk_port_shutdown, 162}; 163 164static struct tty_driver *ttyprintk_driver; 165 166static struct tty_driver *ttyprintk_console_device(struct console *c, 167 int *index) 168{ 169 *index = 0; 170 return ttyprintk_driver; 171} 172 173static struct console ttyprintk_console = { 174 .name = "ttyprintk", 175 .device = ttyprintk_console_device, 176}; 177 178static int __init ttyprintk_init(void) 179{ 180 int ret; 181 182 spin_lock_init(&tpk_port.spinlock); 183 184 ttyprintk_driver = tty_alloc_driver(1, 185 TTY_DRIVER_RESET_TERMIOS | 186 TTY_DRIVER_REAL_RAW | 187 TTY_DRIVER_UNNUMBERED_NODE); 188 if (IS_ERR(ttyprintk_driver)) 189 return PTR_ERR(ttyprintk_driver); 190 191 tty_port_init(&tpk_port.port); 192 tpk_port.port.ops = &tpk_port_ops; 193 194 ttyprintk_driver->driver_name = "ttyprintk"; 195 ttyprintk_driver->name = "ttyprintk"; 196 ttyprintk_driver->major = TTYAUX_MAJOR; 197 ttyprintk_driver->minor_start = 3; 198 ttyprintk_driver->type = TTY_DRIVER_TYPE_CONSOLE; 199 ttyprintk_driver->init_termios = tty_std_termios; 200 ttyprintk_driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET; 201 tty_set_operations(ttyprintk_driver, &ttyprintk_ops); 202 tty_port_link_device(&tpk_port.port, ttyprintk_driver, 0); 203 204 ret = tty_register_driver(ttyprintk_driver); 205 if (ret < 0) { 206 printk(KERN_ERR "Couldn't register ttyprintk driver\n"); 207 goto error; 208 } 209 210 register_console(&ttyprintk_console); 211 212 return 0; 213 214error: 215 tty_driver_kref_put(ttyprintk_driver); 216 tty_port_destroy(&tpk_port.port); 217 return ret; 218} 219 220static void __exit ttyprintk_exit(void) 221{ 222 unregister_console(&ttyprintk_console); 223 tty_unregister_driver(ttyprintk_driver); 224 tty_driver_kref_put(ttyprintk_driver); 225 tty_port_destroy(&tpk_port.port); 226} 227 228device_initcall(ttyprintk_init); 229module_exit(ttyprintk_exit); 230 231MODULE_LICENSE("GPL"); 232