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 21 struct ttyprintk_port { 22 struct tty_port port; 23 spinlock_t spinlock; 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 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 41 static int tpk_curr; 42 43 static u8 tpk_buffer[TPK_STR_SIZE + 4]; 44 45 static 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 54 static 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 */ 86 static 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 */ 96 static 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 */ 106 static 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 */ 123 static unsigned int tpk_write_room(struct tty_struct *tty) 124 { 125 return TPK_MAX_ROOM; 126 } 127 128 /* 129 * TTY operations hangup function. 130 */ 131 static 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 */ 141 static 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 152 static 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 160 static const struct tty_port_operations tpk_port_ops = { 161 .shutdown = tpk_port_shutdown, 162 }; 163 164 static struct tty_driver *ttyprintk_driver; 165 166 static struct tty_driver *ttyprintk_console_device(struct console *c, 167 int *index) 168 { 169 *index = 0; 170 return ttyprintk_driver; 171 } 172 173 static struct console ttyprintk_console = { 174 .name = "ttyprintk", 175 .device = ttyprintk_console_device, 176 }; 177 178 static 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 214 error: 215 tty_driver_kref_put(ttyprintk_driver); 216 tty_port_destroy(&tpk_port.port); 217 return ret; 218 } 219 220 static 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 228 device_initcall(ttyprintk_init); 229 module_exit(ttyprintk_exit); 230 231 MODULE_LICENSE("GPL"); 232