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/device.h> 15 #include <linux/serial.h> 16 #include <linux/tty.h> 17 #include <linux/module.h> 18 #include <linux/spinlock.h> 19 20 struct ttyprintk_port { 21 struct tty_port port; 22 spinlock_t spinlock; 23 }; 24 25 static struct ttyprintk_port tpk_port; 26 27 /* 28 * Our simple preformatting supports transparent output of (time-stamped) 29 * printk messages (also suitable for logging service): 30 * - any cr is replaced by nl 31 * - adds a ttyprintk source tag in front of each line 32 * - too long message is fragmented, with '\'nl between fragments 33 * - TPK_STR_SIZE isn't really the write_room limiting factor, because 34 * it is emptied on the fly during preformatting. 35 */ 36 #define TPK_STR_SIZE 508 /* should be bigger then max expected line length */ 37 #define TPK_MAX_ROOM 4096 /* we could assume 4K for instance */ 38 #define TPK_PREFIX KERN_SOH __stringify(CONFIG_TTY_PRINTK_LEVEL) 39 40 static int tpk_curr; 41 42 static char tpk_buffer[TPK_STR_SIZE + 4]; 43 44 static void tpk_flush(void) 45 { 46 if (tpk_curr > 0) { 47 tpk_buffer[tpk_curr] = '\0'; 48 printk(TPK_PREFIX "[U] %s\n", tpk_buffer); 49 tpk_curr = 0; 50 } 51 } 52 53 static int tpk_printk(const unsigned char *buf, int count) 54 { 55 int i; 56 57 for (i = 0; i < count; i++) { 58 if (tpk_curr >= TPK_STR_SIZE) { 59 /* end of tmp buffer reached: cut the message in two */ 60 tpk_buffer[tpk_curr++] = '\\'; 61 tpk_flush(); 62 } 63 64 switch (buf[i]) { 65 case '\r': 66 tpk_flush(); 67 if ((i + 1) < count && buf[i + 1] == '\n') 68 i++; 69 break; 70 case '\n': 71 tpk_flush(); 72 break; 73 default: 74 tpk_buffer[tpk_curr++] = buf[i]; 75 break; 76 } 77 } 78 79 return count; 80 } 81 82 /* 83 * TTY operations open function. 84 */ 85 static int tpk_open(struct tty_struct *tty, struct file *filp) 86 { 87 tty->driver_data = &tpk_port; 88 89 return tty_port_open(&tpk_port.port, tty, filp); 90 } 91 92 /* 93 * TTY operations close function. 94 */ 95 static void tpk_close(struct tty_struct *tty, struct file *filp) 96 { 97 struct ttyprintk_port *tpkp = tty->driver_data; 98 99 tty_port_close(&tpkp->port, tty, filp); 100 } 101 102 /* 103 * TTY operations write function. 104 */ 105 static int tpk_write(struct tty_struct *tty, 106 const unsigned char *buf, int 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 int __init ttyprintk_init(void) 167 { 168 int ret; 169 170 spin_lock_init(&tpk_port.spinlock); 171 172 ttyprintk_driver = tty_alloc_driver(1, 173 TTY_DRIVER_RESET_TERMIOS | 174 TTY_DRIVER_REAL_RAW | 175 TTY_DRIVER_UNNUMBERED_NODE); 176 if (IS_ERR(ttyprintk_driver)) 177 return PTR_ERR(ttyprintk_driver); 178 179 tty_port_init(&tpk_port.port); 180 tpk_port.port.ops = &tpk_port_ops; 181 182 ttyprintk_driver->driver_name = "ttyprintk"; 183 ttyprintk_driver->name = "ttyprintk"; 184 ttyprintk_driver->major = TTYAUX_MAJOR; 185 ttyprintk_driver->minor_start = 3; 186 ttyprintk_driver->type = TTY_DRIVER_TYPE_CONSOLE; 187 ttyprintk_driver->init_termios = tty_std_termios; 188 ttyprintk_driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET; 189 tty_set_operations(ttyprintk_driver, &ttyprintk_ops); 190 tty_port_link_device(&tpk_port.port, ttyprintk_driver, 0); 191 192 ret = tty_register_driver(ttyprintk_driver); 193 if (ret < 0) { 194 printk(KERN_ERR "Couldn't register ttyprintk driver\n"); 195 goto error; 196 } 197 198 return 0; 199 200 error: 201 put_tty_driver(ttyprintk_driver); 202 tty_port_destroy(&tpk_port.port); 203 return ret; 204 } 205 206 static void __exit ttyprintk_exit(void) 207 { 208 tty_unregister_driver(ttyprintk_driver); 209 put_tty_driver(ttyprintk_driver); 210 tty_port_destroy(&tpk_port.port); 211 } 212 213 device_initcall(ttyprintk_init); 214 module_exit(ttyprintk_exit); 215 216 MODULE_LICENSE("GPL"); 217