ttyprintk.c 5.1 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 char 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 unsigned char *buf, int count)
  46. {
  47. int 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 int tpk_write(struct tty_struct *tty,
  90. const unsigned char *buf, int count)
  91. {
  92. struct ttyprintk_port *tpkp = tty->driver_data;
  93. unsigned long flags;
  94. int ret;
  95. /* exclusive use of tpk_printk within this tty */
  96. spin_lock_irqsave(&tpkp->spinlock, flags);
  97. ret = tpk_printk(buf, count);
  98. spin_unlock_irqrestore(&tpkp->spinlock, flags);
  99. return ret;
  100. }
  101. /*
  102. * TTY operations write_room function.
  103. */
  104. static unsigned int tpk_write_room(struct tty_struct *tty)
  105. {
  106. return TPK_MAX_ROOM;
  107. }
  108. /*
  109. * TTY operations hangup function.
  110. */
  111. static void tpk_hangup(struct tty_struct *tty)
  112. {
  113. struct ttyprintk_port *tpkp = tty->driver_data;
  114. tty_port_hangup(&tpkp->port);
  115. }
  116. /*
  117. * TTY port operations shutdown function.
  118. */
  119. static void tpk_port_shutdown(struct tty_port *tport)
  120. {
  121. struct ttyprintk_port *tpkp =
  122. container_of(tport, struct ttyprintk_port, port);
  123. unsigned long flags;
  124. spin_lock_irqsave(&tpkp->spinlock, flags);
  125. tpk_flush();
  126. spin_unlock_irqrestore(&tpkp->spinlock, flags);
  127. }
  128. static const struct tty_operations ttyprintk_ops = {
  129. .open = tpk_open,
  130. .close = tpk_close,
  131. .write = tpk_write,
  132. .write_room = tpk_write_room,
  133. .hangup = tpk_hangup,
  134. };
  135. static const struct tty_port_operations tpk_port_ops = {
  136. .shutdown = tpk_port_shutdown,
  137. };
  138. static struct tty_driver *ttyprintk_driver;
  139. static struct tty_driver *ttyprintk_console_device(struct console *c,
  140. int *index)
  141. {
  142. *index = 0;
  143. return ttyprintk_driver;
  144. }
  145. static struct console ttyprintk_console = {
  146. .name = "ttyprintk",
  147. .device = ttyprintk_console_device,
  148. };
  149. static int __init ttyprintk_init(void)
  150. {
  151. int ret;
  152. spin_lock_init(&tpk_port.spinlock);
  153. ttyprintk_driver = tty_alloc_driver(1,
  154. TTY_DRIVER_RESET_TERMIOS |
  155. TTY_DRIVER_REAL_RAW |
  156. TTY_DRIVER_UNNUMBERED_NODE);
  157. if (IS_ERR(ttyprintk_driver))
  158. return PTR_ERR(ttyprintk_driver);
  159. tty_port_init(&tpk_port.port);
  160. tpk_port.port.ops = &tpk_port_ops;
  161. ttyprintk_driver->driver_name = "ttyprintk";
  162. ttyprintk_driver->name = "ttyprintk";
  163. ttyprintk_driver->major = TTYAUX_MAJOR;
  164. ttyprintk_driver->minor_start = 3;
  165. ttyprintk_driver->type = TTY_DRIVER_TYPE_CONSOLE;
  166. ttyprintk_driver->init_termios = tty_std_termios;
  167. ttyprintk_driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET;
  168. tty_set_operations(ttyprintk_driver, &ttyprintk_ops);
  169. tty_port_link_device(&tpk_port.port, ttyprintk_driver, 0);
  170. ret = tty_register_driver(ttyprintk_driver);
  171. if (ret < 0) {
  172. printk(KERN_ERR "Couldn't register ttyprintk driver\n");
  173. goto error;
  174. }
  175. register_console(&ttyprintk_console);
  176. return 0;
  177. error:
  178. tty_driver_kref_put(ttyprintk_driver);
  179. tty_port_destroy(&tpk_port.port);
  180. return ret;
  181. }
  182. static void __exit ttyprintk_exit(void)
  183. {
  184. unregister_console(&ttyprintk_console);
  185. tty_unregister_driver(ttyprintk_driver);
  186. tty_driver_kref_put(ttyprintk_driver);
  187. tty_port_destroy(&tpk_port.port);
  188. }
  189. device_initcall(ttyprintk_init);
  190. module_exit(ttyprintk_exit);
  191. MODULE_LICENSE("GPL");