Blame view

drivers/char/ttyprintk.c 4.82 KB
24b4b67d1   Samo Pogacnik   add ttyprintk driver
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  /*
   *  linux/drivers/char/ttyprintk.c
   *
   *  Copyright (C) 2010  Samo Pogacnik
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the smems of the GNU General Public License as published by
   * the Free Software Foundation; version 2 of the License.
   */
  
  /*
   * This pseudo device allows user to make printk messages. It is possible
   * to store "console" messages inline with kernel messages for better analyses
   * of the boot process, for example.
   */
  
  #include <linux/device.h>
  #include <linux/serial.h>
  #include <linux/tty.h>
b24313a82   Takashi Iwai   ttyprintk: Allow ...
20
  #include <linux/module.h>
24b4b67d1   Samo Pogacnik   add ttyprintk driver
21
22
23
24
25
26
27
28
29
30
31
32
33
  
  struct ttyprintk_port {
  	struct tty_port port;
  	struct mutex port_write_mutex;
  };
  
  static struct ttyprintk_port tpk_port;
  
  /*
   * Our simple preformatting supports transparent output of (time-stamped)
   * printk messages (also suitable for logging service):
   * - any cr is replaced by nl
   * - adds a ttyprintk source tag in front of each line
0b3191d4e   Joe Perches   ttyprintk: Neaten...
34
35
   * - too long message is fragmented, with '\'nl between fragments
   * - TPK_STR_SIZE isn't really the write_room limiting factor, because
24b4b67d1   Samo Pogacnik   add ttyprintk driver
36
37
38
39
   *   it is emptied on the fly during preformatting.
   */
  #define TPK_STR_SIZE 508 /* should be bigger then max expected line length */
  #define TPK_MAX_ROOM 4096 /* we could assume 4K for instance */
24b4b67d1   Samo Pogacnik   add ttyprintk driver
40
  static int tpk_curr;
0b3191d4e   Joe Perches   ttyprintk: Neaten...
41
42
43
44
45
46
47
48
49
50
51
  static char tpk_buffer[TPK_STR_SIZE + 4];
  
  static void tpk_flush(void)
  {
  	if (tpk_curr > 0) {
  		tpk_buffer[tpk_curr] = '\0';
  		pr_info("[U] %s
  ", tpk_buffer);
  		tpk_curr = 0;
  	}
  }
24b4b67d1   Samo Pogacnik   add ttyprintk driver
52
53
  static int tpk_printk(const unsigned char *buf, int count)
  {
24b4b67d1   Samo Pogacnik   add ttyprintk driver
54
55
56
  	int i = tpk_curr;
  
  	if (buf == NULL) {
0b3191d4e   Joe Perches   ttyprintk: Neaten...
57
  		tpk_flush();
24b4b67d1   Samo Pogacnik   add ttyprintk driver
58
59
60
61
  		return i;
  	}
  
  	for (i = 0; i < count; i++) {
0b3191d4e   Joe Perches   ttyprintk: Neaten...
62
  		if (tpk_curr >= TPK_STR_SIZE) {
24b4b67d1   Samo Pogacnik   add ttyprintk driver
63
  			/* end of tmp buffer reached: cut the message in two */
0b3191d4e   Joe Perches   ttyprintk: Neaten...
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
  			tpk_buffer[tpk_curr++] = '\\';
  			tpk_flush();
  		}
  
  		switch (buf[i]) {
  		case '\r':
  			tpk_flush();
  			if ((i + 1) < count && buf[i + 1] == '
  ')
  				i++;
  			break;
  		case '
  ':
  			tpk_flush();
  			break;
  		default:
  			tpk_buffer[tpk_curr++] = buf[i];
  			break;
24b4b67d1   Samo Pogacnik   add ttyprintk driver
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
  		}
  	}
  
  	return count;
  }
  
  /*
   * TTY operations open function.
   */
  static int tpk_open(struct tty_struct *tty, struct file *filp)
  {
  	tty->driver_data = &tpk_port;
  
  	return tty_port_open(&tpk_port.port, tty, filp);
  }
  
  /*
   * TTY operations close function.
   */
  static void tpk_close(struct tty_struct *tty, struct file *filp)
  {
  	struct ttyprintk_port *tpkp = tty->driver_data;
  
  	mutex_lock(&tpkp->port_write_mutex);
  	/* flush tpk_printk buffer */
  	tpk_printk(NULL, 0);
  	mutex_unlock(&tpkp->port_write_mutex);
  
  	tty_port_close(&tpkp->port, tty, filp);
  }
  
  /*
   * TTY operations write function.
   */
  static int tpk_write(struct tty_struct *tty,
  		const unsigned char *buf, int count)
  {
  	struct ttyprintk_port *tpkp = tty->driver_data;
  	int ret;
  
  
  	/* exclusive use of tpk_printk within this tty */
  	mutex_lock(&tpkp->port_write_mutex);
  	ret = tpk_printk(buf, count);
  	mutex_unlock(&tpkp->port_write_mutex);
  
  	return ret;
  }
  
  /*
   * TTY operations write_room function.
   */
  static int tpk_write_room(struct tty_struct *tty)
  {
  	return TPK_MAX_ROOM;
  }
  
  /*
   * TTY operations ioctl function.
   */
6caa76b77   Alan Cox   tty: now phase ou...
142
  static int tpk_ioctl(struct tty_struct *tty,
24b4b67d1   Samo Pogacnik   add ttyprintk driver
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
  			unsigned int cmd, unsigned long arg)
  {
  	struct ttyprintk_port *tpkp = tty->driver_data;
  
  	if (!tpkp)
  		return -EINVAL;
  
  	switch (cmd) {
  	/* Stop TIOCCONS */
  	case TIOCCONS:
  		return -EOPNOTSUPP;
  	default:
  		return -ENOIOCTLCMD;
  	}
  	return 0;
  }
  
  static const struct tty_operations ttyprintk_ops = {
  	.open = tpk_open,
  	.close = tpk_close,
  	.write = tpk_write,
  	.write_room = tpk_write_room,
  	.ioctl = tpk_ioctl,
  };
9b95d95d5   Aya Mahfouz   char: constify tt...
167
  static const struct tty_port_operations null_ops = { };
24b4b67d1   Samo Pogacnik   add ttyprintk driver
168
169
170
171
172
173
  
  static struct tty_driver *ttyprintk_driver;
  
  static int __init ttyprintk_init(void)
  {
  	int ret = -ENOMEM;
24b4b67d1   Samo Pogacnik   add ttyprintk driver
174

536a3440a   Jiri Slaby   TTY: ttyprintk, i...
175
  	mutex_init(&tpk_port.port_write_mutex);
0019b4089   Jiri Slaby   TTY: add support ...
176
177
178
179
  	ttyprintk_driver = tty_alloc_driver(1,
  			TTY_DRIVER_RESET_TERMIOS |
  			TTY_DRIVER_REAL_RAW |
  			TTY_DRIVER_UNNUMBERED_NODE);
c3a6344ae   Dan Carpenter   TTY: tty_alloc_dr...
180
181
  	if (IS_ERR(ttyprintk_driver))
  		return PTR_ERR(ttyprintk_driver);
24b4b67d1   Samo Pogacnik   add ttyprintk driver
182

191c5f102   Jiri Slaby   TTY: call tty_por...
183
  	tty_port_init(&tpk_port.port);
b5325a02a   Darrick J. Wong   ttyprintk: Fix NU...
184
  	tpk_port.port.ops = &null_ops;
191c5f102   Jiri Slaby   TTY: call tty_por...
185

24b4b67d1   Samo Pogacnik   add ttyprintk driver
186
187
188
189
  	ttyprintk_driver->driver_name = "ttyprintk";
  	ttyprintk_driver->name = "ttyprintk";
  	ttyprintk_driver->major = TTYAUX_MAJOR;
  	ttyprintk_driver->minor_start = 3;
24b4b67d1   Samo Pogacnik   add ttyprintk driver
190
191
192
  	ttyprintk_driver->type = TTY_DRIVER_TYPE_CONSOLE;
  	ttyprintk_driver->init_termios = tty_std_termios;
  	ttyprintk_driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET;
24b4b67d1   Samo Pogacnik   add ttyprintk driver
193
  	tty_set_operations(ttyprintk_driver, &ttyprintk_ops);
b19e2ca77   Jiri Slaby   TTY: use tty_port...
194
  	tty_port_link_device(&tpk_port.port, ttyprintk_driver, 0);
24b4b67d1   Samo Pogacnik   add ttyprintk driver
195
196
197
198
199
200
201
  
  	ret = tty_register_driver(ttyprintk_driver);
  	if (ret < 0) {
  		printk(KERN_ERR "Couldn't register ttyprintk driver
  ");
  		goto error;
  	}
24b4b67d1   Samo Pogacnik   add ttyprintk driver
202
203
204
205
  	return 0;
  
  error:
  	put_tty_driver(ttyprintk_driver);
191c5f102   Jiri Slaby   TTY: call tty_por...
206
  	tty_port_destroy(&tpk_port.port);
24b4b67d1   Samo Pogacnik   add ttyprintk driver
207
208
  	return ret;
  }
b24313a82   Takashi Iwai   ttyprintk: Allow ...
209
210
211
212
213
214
215
  
  static void __exit ttyprintk_exit(void)
  {
  	tty_unregister_driver(ttyprintk_driver);
  	put_tty_driver(ttyprintk_driver);
  	tty_port_destroy(&tpk_port.port);
  }
db50d2f65   Paul Gortmaker   drivers/char: don...
216
  device_initcall(ttyprintk_init);
b24313a82   Takashi Iwai   ttyprintk: Allow ...
217
218
219
  module_exit(ttyprintk_exit);
  
  MODULE_LICENSE("GPL");