ttyprintk.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  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. * This pseudo device allows user to make printk messages. It is possible
  9. * to store "console" messages inline with kernel messages for better analyses
  10. * of the boot process, for example.
  11. */
  12. #include <linux/console.h>
  13. #include <linux/device.h>
  14. #include <linux/serial.h>
  15. #include <linux/tty.h>
  16. #include <linux/module.h>
  17. #include <linux/spinlock.h>
  18. struct ttyprintk_port {
  19. struct tty_port port;
  20. spinlock_t spinlock;
  21. };
  22. static struct ttyprintk_port tpk_port;
  23. /*
  24. * Our simple preformatting supports transparent output of (time-stamped)
  25. * printk messages (also suitable for logging service):
  26. * - any cr is replaced by nl
  27. * - adds a ttyprintk source tag in front of each line
  28. * - too long message is fragmented, with '\'nl between fragments
  29. * - TPK_STR_SIZE isn't really the write_room limiting factor, because
  30. * it is emptied on the fly during preformatting.
  31. */
  32. #define TPK_STR_SIZE 508 /* should be bigger then max expected line length */
  33. #define TPK_MAX_ROOM 4096 /* we could assume 4K for instance */
  34. #define TPK_PREFIX KERN_SOH __stringify(CONFIG_TTY_PRINTK_LEVEL)
  35. static int tpk_curr;
  36. static u8 tpk_buffer[TPK_STR_SIZE + 4];
  37. static void tpk_flush(void)
  38. {
  39. if (tpk_curr > 0) {
  40. tpk_buffer[tpk_curr] = '\0';
  41. printk(TPK_PREFIX "[U] %s\n", tpk_buffer);
  42. tpk_curr = 0;
  43. }
  44. }
  45. static int tpk_printk(const u8 *buf, size_t count)
  46. {
  47. size_t i;
  48. for (i = 0; i < count; i++) {
  49. if (tpk_curr >= TPK_STR_SIZE) {
  50. /* end of tmp buffer reached: cut the message in two */
  51. tpk_buffer[tpk_curr++] = '\\';
  52. tpk_flush();
  53. }
  54. switch (buf[i]) {
  55. case '\r':
  56. tpk_flush();
  57. if ((i + 1) < count && buf[i + 1] == '\n')
  58. i++;
  59. break;
  60. case '\n':
  61. tpk_flush();
  62. break;
  63. default:
  64. tpk_buffer[tpk_curr++] = buf[i];
  65. break;
  66. }
  67. }
  68. return count;
  69. }
  70. /*
  71. * TTY operations open function.
  72. */
  73. static int tpk_open(struct tty_struct *tty, struct file *filp)
  74. {
  75. tty->driver_data = &tpk_port;
  76. return tty_port_open(&tpk_port.port, tty, filp);
  77. }
  78. /*
  79. * TTY operations close function.
  80. */
  81. static void tpk_close(struct tty_struct *tty, struct file *filp)
  82. {
  83. struct ttyprintk_port *tpkp = tty->driver_data;
  84. tty_port_close(&tpkp->port, tty, filp);
  85. }
  86. /*
  87. * TTY operations write function.
  88. */
  89. static ssize_t tpk_write(struct tty_struct *tty, const u8 *buf, size_t count)
  90. {
  91. struct ttyprintk_port *tpkp = tty->driver_data;
  92. unsigned long flags;
  93. int ret;
  94. /* exclusive use of tpk_printk within this tty */
  95. spin_lock_irqsave(&tpkp->spinlock, flags);
  96. ret = tpk_printk(buf, count);
  97. spin_unlock_irqrestore(&tpkp->spinlock, flags);
  98. return ret;
  99. }
  100. /*
  101. * TTY operations write_room function.
  102. */
  103. static unsigned int tpk_write_room(struct tty_struct *tty)
  104. {
  105. return TPK_MAX_ROOM;
  106. }
  107. /*
  108. * TTY operations hangup function.
  109. */
  110. static void tpk_hangup(struct tty_struct *tty)
  111. {
  112. struct ttyprintk_port *tpkp = tty->driver_data;
  113. tty_port_hangup(&tpkp->port);
  114. }
  115. /*
  116. * TTY port operations shutdown function.
  117. */
  118. static void tpk_port_shutdown(struct tty_port *tport)
  119. {
  120. struct ttyprintk_port *tpkp =
  121. container_of(tport, struct ttyprintk_port, port);
  122. unsigned long flags;
  123. spin_lock_irqsave(&tpkp->spinlock, flags);
  124. tpk_flush();
  125. spin_unlock_irqrestore(&tpkp->spinlock, flags);
  126. }
  127. static const struct tty_operations ttyprintk_ops = {
  128. .open = tpk_open,
  129. .close = tpk_close,
  130. .write = tpk_write,
  131. .write_room = tpk_write_room,
  132. .hangup = tpk_hangup,
  133. };
  134. static const struct tty_port_operations tpk_port_ops = {
  135. .shutdown = tpk_port_shutdown,
  136. };
  137. static struct tty_driver *ttyprintk_driver;
  138. static struct tty_driver *ttyprintk_console_device(struct console *c,
  139. int *index)
  140. {
  141. *index = 0;
  142. return ttyprintk_driver;
  143. }
  144. static struct console ttyprintk_console = {
  145. .name = "ttyprintk",
  146. .device = ttyprintk_console_device,
  147. };
  148. static int __init ttyprintk_init(void)
  149. {
  150. int ret;
  151. spin_lock_init(&tpk_port.spinlock);
  152. ttyprintk_driver = tty_alloc_driver(1,
  153. TTY_DRIVER_RESET_TERMIOS |
  154. TTY_DRIVER_REAL_RAW |
  155. TTY_DRIVER_UNNUMBERED_NODE);
  156. if (IS_ERR(ttyprintk_driver))
  157. return PTR_ERR(ttyprintk_driver);
  158. tty_port_init(&tpk_port.port);
  159. tpk_port.port.ops = &tpk_port_ops;
  160. ttyprintk_driver->driver_name = "ttyprintk";
  161. ttyprintk_driver->name = "ttyprintk";
  162. ttyprintk_driver->major = TTYAUX_MAJOR;
  163. ttyprintk_driver->minor_start = 3;
  164. ttyprintk_driver->type = TTY_DRIVER_TYPE_CONSOLE;
  165. ttyprintk_driver->init_termios = tty_std_termios;
  166. ttyprintk_driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET;
  167. tty_set_operations(ttyprintk_driver, &ttyprintk_ops);
  168. tty_port_link_device(&tpk_port.port, ttyprintk_driver, 0);
  169. ret = tty_register_driver(ttyprintk_driver);
  170. if (ret < 0) {
  171. printk(KERN_ERR "Couldn't register ttyprintk driver\n");
  172. goto error;
  173. }
  174. register_console(&ttyprintk_console);
  175. return 0;
  176. error:
  177. tty_driver_kref_put(ttyprintk_driver);
  178. tty_port_destroy(&tpk_port.port);
  179. return ret;
  180. }
  181. static void __exit ttyprintk_exit(void)
  182. {
  183. unregister_console(&ttyprintk_console);
  184. tty_unregister_driver(ttyprintk_driver);
  185. tty_driver_kref_put(ttyprintk_driver);
  186. tty_port_destroy(&tpk_port.port);
  187. }
  188. device_initcall(ttyprintk_init);
  189. module_exit(ttyprintk_exit);
  190. MODULE_DESCRIPTION("TTY driver to output user messages via printk");
  191. MODULE_LICENSE("GPL");