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 char 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 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 */ 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 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 */ 124 static unsigned int tpk_write_room(struct tty_struct *tty) 125 { 126 return TPK_MAX_ROOM; 127 } 128 129 /* 130 * TTY operations hangup function. 131 */ 132 static 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 */ 142 static 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 153 static 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 161 static const struct tty_port_operations tpk_port_ops = { 162 .shutdown = tpk_port_shutdown, 163 }; 164 165 static struct tty_driver *ttyprintk_driver; 166 167 static struct tty_driver *ttyprintk_console_device(struct console *c, 168 int *index) 169 { 170 *index = 0; 171 return ttyprintk_driver; 172 } 173 174 static struct console ttyprintk_console = { 175 .name = "ttyprintk", 176 .device = ttyprintk_console_device, 177 }; 178 179 static 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 215 error: 216 tty_driver_kref_put(ttyprintk_driver); 217 tty_port_destroy(&tpk_port.port); 218 return ret; 219 } 220 221 static 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 229 device_initcall(ttyprintk_init); 230 module_exit(ttyprintk_exit); 231 232 MODULE_LICENSE("GPL"); 233