linux/drivers/char/ttyprintk.c
<<
>>
Prefs
   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
  20struct ttyprintk_port {
  21        struct tty_port port;
  22        spinlock_t spinlock;
  23};
  24
  25static 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
  40static int tpk_curr;
  41
  42static char tpk_buffer[TPK_STR_SIZE + 4];
  43
  44static 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
  53static 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 */
  85static 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 */
  95static 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 */
 105static 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 */
 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 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
 200error:
 201        put_tty_driver(ttyprintk_driver);
 202        tty_port_destroy(&tpk_port.port);
 203        return ret;
 204}
 205
 206static 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
 213device_initcall(ttyprintk_init);
 214module_exit(ttyprintk_exit);
 215
 216MODULE_LICENSE("GPL");
 217