root/drivers/char/ttyprintk.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. tpk_flush
  2. tpk_printk
  3. tpk_open
  4. tpk_close
  5. tpk_write
  6. tpk_write_room
  7. tpk_ioctl
  8. ttyprintk_init
  9. ttyprintk_exit

   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 = tpk_curr;
  56 
  57         if (buf == NULL) {
  58                 tpk_flush();
  59                 return i;
  60         }
  61 
  62         for (i = 0; i < count; i++) {
  63                 if (tpk_curr >= TPK_STR_SIZE) {
  64                         /* end of tmp buffer reached: cut the message in two */
  65                         tpk_buffer[tpk_curr++] = '\\';
  66                         tpk_flush();
  67                 }
  68 
  69                 switch (buf[i]) {
  70                 case '\r':
  71                         tpk_flush();
  72                         if ((i + 1) < count && buf[i + 1] == '\n')
  73                                 i++;
  74                         break;
  75                 case '\n':
  76                         tpk_flush();
  77                         break;
  78                 default:
  79                         tpk_buffer[tpk_curr++] = buf[i];
  80                         break;
  81                 }
  82         }
  83 
  84         return count;
  85 }
  86 
  87 /*
  88  * TTY operations open function.
  89  */
  90 static int tpk_open(struct tty_struct *tty, struct file *filp)
  91 {
  92         tty->driver_data = &tpk_port;
  93 
  94         return tty_port_open(&tpk_port.port, tty, filp);
  95 }
  96 
  97 /*
  98  * TTY operations close function.
  99  */
 100 static void tpk_close(struct tty_struct *tty, struct file *filp)
 101 {
 102         struct ttyprintk_port *tpkp = tty->driver_data;
 103         unsigned long flags;
 104 
 105         spin_lock_irqsave(&tpkp->spinlock, flags);
 106         /* flush tpk_printk buffer */
 107         tpk_printk(NULL, 0);
 108         spin_unlock_irqrestore(&tpkp->spinlock, flags);
 109 
 110         tty_port_close(&tpkp->port, tty, filp);
 111 }
 112 
 113 /*
 114  * TTY operations write function.
 115  */
 116 static int tpk_write(struct tty_struct *tty,
 117                 const unsigned char *buf, int count)
 118 {
 119         struct ttyprintk_port *tpkp = tty->driver_data;
 120         unsigned long flags;
 121         int ret;
 122 
 123 
 124         /* exclusive use of tpk_printk within this tty */
 125         spin_lock_irqsave(&tpkp->spinlock, flags);
 126         ret = tpk_printk(buf, count);
 127         spin_unlock_irqrestore(&tpkp->spinlock, flags);
 128 
 129         return ret;
 130 }
 131 
 132 /*
 133  * TTY operations write_room function.
 134  */
 135 static int tpk_write_room(struct tty_struct *tty)
 136 {
 137         return TPK_MAX_ROOM;
 138 }
 139 
 140 /*
 141  * TTY operations ioctl function.
 142  */
 143 static int tpk_ioctl(struct tty_struct *tty,
 144                         unsigned int cmd, unsigned long arg)
 145 {
 146         struct ttyprintk_port *tpkp = tty->driver_data;
 147 
 148         if (!tpkp)
 149                 return -EINVAL;
 150 
 151         switch (cmd) {
 152         /* Stop TIOCCONS */
 153         case TIOCCONS:
 154                 return -EOPNOTSUPP;
 155         default:
 156                 return -ENOIOCTLCMD;
 157         }
 158         return 0;
 159 }
 160 
 161 static const struct tty_operations ttyprintk_ops = {
 162         .open = tpk_open,
 163         .close = tpk_close,
 164         .write = tpk_write,
 165         .write_room = tpk_write_room,
 166         .ioctl = tpk_ioctl,
 167 };
 168 
 169 static const struct tty_port_operations null_ops = { };
 170 
 171 static struct tty_driver *ttyprintk_driver;
 172 
 173 static int __init ttyprintk_init(void)
 174 {
 175         int ret = -ENOMEM;
 176 
 177         spin_lock_init(&tpk_port.spinlock);
 178 
 179         ttyprintk_driver = tty_alloc_driver(1,
 180                         TTY_DRIVER_RESET_TERMIOS |
 181                         TTY_DRIVER_REAL_RAW |
 182                         TTY_DRIVER_UNNUMBERED_NODE);
 183         if (IS_ERR(ttyprintk_driver))
 184                 return PTR_ERR(ttyprintk_driver);
 185 
 186         tty_port_init(&tpk_port.port);
 187         tpk_port.port.ops = &null_ops;
 188 
 189         ttyprintk_driver->driver_name = "ttyprintk";
 190         ttyprintk_driver->name = "ttyprintk";
 191         ttyprintk_driver->major = TTYAUX_MAJOR;
 192         ttyprintk_driver->minor_start = 3;
 193         ttyprintk_driver->type = TTY_DRIVER_TYPE_CONSOLE;
 194         ttyprintk_driver->init_termios = tty_std_termios;
 195         ttyprintk_driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET;
 196         tty_set_operations(ttyprintk_driver, &ttyprintk_ops);
 197         tty_port_link_device(&tpk_port.port, ttyprintk_driver, 0);
 198 
 199         ret = tty_register_driver(ttyprintk_driver);
 200         if (ret < 0) {
 201                 printk(KERN_ERR "Couldn't register ttyprintk driver\n");
 202                 goto error;
 203         }
 204 
 205         return 0;
 206 
 207 error:
 208         put_tty_driver(ttyprintk_driver);
 209         tty_port_destroy(&tpk_port.port);
 210         return ret;
 211 }
 212 
 213 static void __exit ttyprintk_exit(void)
 214 {
 215         tty_unregister_driver(ttyprintk_driver);
 216         put_tty_driver(ttyprintk_driver);
 217         tty_port_destroy(&tpk_port.port);
 218 }
 219 
 220 device_initcall(ttyprintk_init);
 221 module_exit(ttyprintk_exit);
 222 
 223 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */