Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0
2 : : /*
3 : : * Provide access to virtual console memory.
4 : : * /dev/vcs: the screen as it is being viewed right now (possibly scrolled)
5 : : * /dev/vcsN: the screen of /dev/ttyN (1 <= N <= 63)
6 : : * [minor: N]
7 : : *
8 : : * /dev/vcsaN: idem, but including attributes, and prefixed with
9 : : * the 4 bytes lines,columns,x,y (as screendump used to give).
10 : : * Attribute/character pair is in native endianity.
11 : : * [minor: N+128]
12 : : *
13 : : * /dev/vcsuN: similar to /dev/vcsaN but using 4-byte unicode values
14 : : * instead of 1-byte screen glyph values.
15 : : * [minor: N+64]
16 : : *
17 : : * /dev/vcsuaN: same idea as /dev/vcsaN for unicode (not yet implemented).
18 : : *
19 : : * This replaces screendump and part of selection, so that the system
20 : : * administrator can control access using file system permissions.
21 : : *
22 : : * aeb@cwi.nl - efter Friedas begravelse - 950211
23 : : *
24 : : * machek@k332.feld.cvut.cz - modified not to send characters to wrong console
25 : : * - fixed some fatal off-by-one bugs (0-- no longer == -1 -> looping and looping and looping...)
26 : : * - making it shorter - scr_readw are macros which expand in PRETTY long code
27 : : */
28 : :
29 : : #include <linux/kernel.h>
30 : : #include <linux/major.h>
31 : : #include <linux/errno.h>
32 : : #include <linux/export.h>
33 : : #include <linux/tty.h>
34 : : #include <linux/interrupt.h>
35 : : #include <linux/mm.h>
36 : : #include <linux/init.h>
37 : : #include <linux/vt_kern.h>
38 : : #include <linux/selection.h>
39 : : #include <linux/kbd_kern.h>
40 : : #include <linux/console.h>
41 : : #include <linux/device.h>
42 : : #include <linux/sched.h>
43 : : #include <linux/fs.h>
44 : : #include <linux/poll.h>
45 : : #include <linux/signal.h>
46 : : #include <linux/slab.h>
47 : : #include <linux/notifier.h>
48 : :
49 : : #include <linux/uaccess.h>
50 : : #include <asm/byteorder.h>
51 : : #include <asm/unaligned.h>
52 : :
53 : : #undef attr
54 : : #undef org
55 : : #undef addr
56 : : #define HEADER_SIZE 4
57 : :
58 : : #define CON_BUF_SIZE (CONFIG_BASE_SMALL ? 256 : PAGE_SIZE)
59 : :
60 : : /*
61 : : * Our minor space:
62 : : *
63 : : * 0 ... 63 glyph mode without attributes
64 : : * 64 ... 127 unicode mode without attributes
65 : : * 128 ... 191 glyph mode with attributes
66 : : * 192 ... 255 unused (reserved for unicode with attributes)
67 : : *
68 : : * This relies on MAX_NR_CONSOLES being <= 63, meaning 63 actual consoles
69 : : * with minors 0, 64, 128 and 192 being proxies for the foreground console.
70 : : */
71 : : #if MAX_NR_CONSOLES > 63
72 : : #warning "/dev/vcs* devices may not accommodate more than 63 consoles"
73 : : #endif
74 : :
75 : : #define console(inode) (iminor(inode) & 63)
76 : : #define use_unicode(inode) (iminor(inode) & 64)
77 : : #define use_attributes(inode) (iminor(inode) & 128)
78 : :
79 : :
80 : : struct vcs_poll_data {
81 : : struct notifier_block notifier;
82 : : unsigned int cons_num;
83 : : int event;
84 : : wait_queue_head_t waitq;
85 : : struct fasync_struct *fasync;
86 : : };
87 : :
88 : : static int
89 : 0 : vcs_notifier(struct notifier_block *nb, unsigned long code, void *_param)
90 : : {
91 : : struct vt_notifier_param *param = _param;
92 : 0 : struct vc_data *vc = param->vc;
93 : : struct vcs_poll_data *poll =
94 : : container_of(nb, struct vcs_poll_data, notifier);
95 : 0 : int currcons = poll->cons_num;
96 : : int fa_band;
97 : :
98 [ # # # ]: 0 : switch (code) {
99 : : case VT_UPDATE:
100 : : fa_band = POLL_PRI;
101 : : break;
102 : : case VT_DEALLOCATE:
103 : : fa_band = POLL_HUP;
104 : 0 : break;
105 : : default:
106 : : return NOTIFY_DONE;
107 : : }
108 : :
109 [ # # ]: 0 : if (currcons == 0)
110 : 0 : currcons = fg_console;
111 : : else
112 : 0 : currcons--;
113 [ # # ]: 0 : if (currcons != vc->vc_num)
114 : : return NOTIFY_DONE;
115 : :
116 : 0 : poll->event = code;
117 : 0 : wake_up_interruptible(&poll->waitq);
118 : 0 : kill_fasync(&poll->fasync, SIGIO, fa_band);
119 : 0 : return NOTIFY_OK;
120 : : }
121 : :
122 : : static void
123 : : vcs_poll_data_free(struct vcs_poll_data *poll)
124 : : {
125 : 0 : unregister_vt_notifier(&poll->notifier);
126 : 0 : kfree(poll);
127 : : }
128 : :
129 : : static struct vcs_poll_data *
130 : 0 : vcs_poll_data_get(struct file *file)
131 : : {
132 : 0 : struct vcs_poll_data *poll = file->private_data, *kill = NULL;
133 : :
134 [ # # ]: 0 : if (poll)
135 : : return poll;
136 : :
137 : 0 : poll = kzalloc(sizeof(*poll), GFP_KERNEL);
138 [ # # ]: 0 : if (!poll)
139 : : return NULL;
140 : 0 : poll->cons_num = console(file_inode(file));
141 : 0 : init_waitqueue_head(&poll->waitq);
142 : 0 : poll->notifier.notifier_call = vcs_notifier;
143 : : /*
144 : : * In order not to lose any update event, we must pretend one might
145 : : * have occurred before we have a chance to register our notifier.
146 : : * This is also how user space has come to detect which kernels
147 : : * support POLLPRI on /dev/vcs* devices i.e. using poll() with
148 : : * POLLPRI and a zero timeout.
149 : : */
150 : 0 : poll->event = VT_UPDATE;
151 : :
152 [ # # ]: 0 : if (register_vt_notifier(&poll->notifier) != 0) {
153 : 0 : kfree(poll);
154 : 0 : return NULL;
155 : : }
156 : :
157 : : /*
158 : : * This code may be called either through ->poll() or ->fasync().
159 : : * If we have two threads using the same file descriptor, they could
160 : : * both enter this function, both notice that the structure hasn't
161 : : * been allocated yet and go ahead allocating it in parallel, but
162 : : * only one of them must survive and be shared otherwise we'd leak
163 : : * memory with a dangling notifier callback.
164 : : */
165 : : spin_lock(&file->f_lock);
166 [ # # ]: 0 : if (!file->private_data) {
167 : 0 : file->private_data = poll;
168 : : } else {
169 : : /* someone else raced ahead of us */
170 : : kill = poll;
171 : : poll = file->private_data;
172 : : }
173 : : spin_unlock(&file->f_lock);
174 [ # # ]: 0 : if (kill)
175 : : vcs_poll_data_free(kill);
176 : :
177 : 0 : return poll;
178 : : }
179 : :
180 : : /*
181 : : * Returns VC for inode.
182 : : * Must be called with console_lock.
183 : : */
184 : : static struct vc_data*
185 : 0 : vcs_vc(struct inode *inode, int *viewed)
186 : : {
187 : 0 : unsigned int currcons = console(inode);
188 : :
189 [ # # # # : 0 : WARN_CONSOLE_UNLOCKED();
# # # # ]
190 : :
191 [ # # ]: 0 : if (currcons == 0) {
192 : 0 : currcons = fg_console;
193 [ # # ]: 0 : if (viewed)
194 : 0 : *viewed = 1;
195 : : } else {
196 : 0 : currcons--;
197 [ # # ]: 0 : if (viewed)
198 : 0 : *viewed = 0;
199 : : }
200 : 0 : return vc_cons[currcons].d;
201 : : }
202 : :
203 : : /*
204 : : * Returns size for VC carried by inode.
205 : : * Must be called with console_lock.
206 : : */
207 : : static int
208 : 0 : vcs_size(struct inode *inode)
209 : : {
210 : : int size;
211 : : struct vc_data *vc;
212 : :
213 [ # # # # : 0 : WARN_CONSOLE_UNLOCKED();
# # # # ]
214 : :
215 : 0 : vc = vcs_vc(inode, NULL);
216 [ # # ]: 0 : if (!vc)
217 : : return -ENXIO;
218 : :
219 : 0 : size = vc->vc_rows * vc->vc_cols;
220 : :
221 [ # # ]: 0 : if (use_attributes(inode)) {
222 [ # # ]: 0 : if (use_unicode(inode))
223 : : return -EOPNOTSUPP;
224 : 0 : size = 2*size + HEADER_SIZE;
225 [ # # ]: 0 : } else if (use_unicode(inode))
226 : 0 : size *= 4;
227 : 0 : return size;
228 : : }
229 : :
230 : 0 : static loff_t vcs_lseek(struct file *file, loff_t offset, int orig)
231 : : {
232 : : int size;
233 : :
234 : 0 : console_lock();
235 : 0 : size = vcs_size(file_inode(file));
236 : 0 : console_unlock();
237 [ # # ]: 0 : if (size < 0)
238 : 0 : return size;
239 : 0 : return fixed_size_llseek(file, offset, orig, size);
240 : : }
241 : :
242 : :
243 : : static ssize_t
244 : 0 : vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
245 : : {
246 : : struct inode *inode = file_inode(file);
247 : : struct vc_data *vc;
248 : : struct vcs_poll_data *poll;
249 : : long pos, read;
250 : : int attr, uni_mode, row, col, maxcol, viewed;
251 : : unsigned short *org = NULL;
252 : : ssize_t ret;
253 : : char *con_buf;
254 : :
255 : 0 : con_buf = (char *) __get_free_page(GFP_KERNEL);
256 [ # # ]: 0 : if (!con_buf)
257 : : return -ENOMEM;
258 : :
259 : 0 : pos = *ppos;
260 : :
261 : : /* Select the proper current console and verify
262 : : * sanity of the situation under the console lock.
263 : : */
264 : 0 : console_lock();
265 : :
266 : 0 : uni_mode = use_unicode(inode);
267 : 0 : attr = use_attributes(inode);
268 : : ret = -ENXIO;
269 : 0 : vc = vcs_vc(inode, &viewed);
270 [ # # ]: 0 : if (!vc)
271 : : goto unlock_out;
272 : :
273 : : ret = -EINVAL;
274 [ # # ]: 0 : if (pos < 0)
275 : : goto unlock_out;
276 : : /* we enforce 32-bit alignment for pos and count in unicode mode */
277 [ # # # # ]: 0 : if (uni_mode && (pos | count) & 3)
278 : : goto unlock_out;
279 : :
280 : 0 : poll = file->private_data;
281 [ # # ]: 0 : if (count && poll)
282 : 0 : poll->event = 0;
283 : : read = 0;
284 : : ret = 0;
285 [ # # ]: 0 : while (count) {
286 : : char *con_buf0, *con_buf_start;
287 : : long this_round, size;
288 : : ssize_t orig_count;
289 : : long p = pos;
290 : :
291 : : /* Check whether we are above size each round,
292 : : * as copy_to_user at the end of this loop
293 : : * could sleep.
294 : : */
295 : 0 : size = vcs_size(inode);
296 [ # # ]: 0 : if (size < 0) {
297 [ # # ]: 0 : if (read)
298 : : break;
299 : 0 : ret = size;
300 : 0 : goto unlock_out;
301 : : }
302 [ # # ]: 0 : if (pos >= size)
303 : : break;
304 [ # # ]: 0 : if (count > size - pos)
305 : : count = size - pos;
306 : :
307 : 0 : this_round = count;
308 [ # # ]: 0 : if (this_round > CON_BUF_SIZE)
309 : : this_round = CON_BUF_SIZE;
310 : :
311 : : /* Perform the whole read into the local con_buf.
312 : : * Then we can drop the console spinlock and safely
313 : : * attempt to move it to userspace.
314 : : */
315 : :
316 : : con_buf_start = con_buf0 = con_buf;
317 : : orig_count = this_round;
318 : 0 : maxcol = vc->vc_cols;
319 [ # # ]: 0 : if (uni_mode) {
320 : : unsigned int nr;
321 : :
322 : 0 : ret = vc_uniscr_check(vc);
323 [ # # ]: 0 : if (ret)
324 : : break;
325 : 0 : p /= 4;
326 : 0 : row = p / vc->vc_cols;
327 : 0 : col = p % maxcol;
328 : 0 : nr = maxcol - col;
329 : : do {
330 [ # # ]: 0 : if (nr > this_round/4)
331 : : nr = this_round/4;
332 : 0 : vc_uniscr_copy_line(vc, con_buf0, viewed,
333 : : row, col, nr);
334 : 0 : con_buf0 += nr * 4;
335 : 0 : this_round -= nr * 4;
336 : 0 : row++;
337 : : col = 0;
338 : : nr = maxcol;
339 [ # # ]: 0 : } while (this_round);
340 [ # # ]: 0 : } else if (!attr) {
341 : 0 : org = screen_pos(vc, p, viewed);
342 : 0 : col = p % maxcol;
343 : 0 : p += maxcol - col;
344 [ # # ]: 0 : while (this_round-- > 0) {
345 : 0 : *con_buf0++ = (vcs_scr_readw(vc, org++) & 0xff);
346 [ # # ]: 0 : if (++col == maxcol) {
347 : 0 : org = screen_pos(vc, p, viewed);
348 : : col = 0;
349 : 0 : p += maxcol;
350 : : }
351 : : }
352 : : } else {
353 [ # # ]: 0 : if (p < HEADER_SIZE) {
354 : : size_t tmp_count;
355 : :
356 : : /* clamp header values if they don't fit */
357 : 0 : con_buf0[0] = min(vc->vc_rows, 0xFFu);
358 : 0 : con_buf0[1] = min(vc->vc_cols, 0xFFu);
359 : 0 : getconsxy(vc, con_buf0 + 2);
360 : :
361 : 0 : con_buf_start += p;
362 : 0 : this_round += p;
363 [ # # ]: 0 : if (this_round > CON_BUF_SIZE) {
364 : : this_round = CON_BUF_SIZE;
365 : 0 : orig_count = this_round - p;
366 : : }
367 : :
368 : : tmp_count = HEADER_SIZE;
369 [ # # ]: 0 : if (tmp_count > this_round)
370 : : tmp_count = this_round;
371 : :
372 : : /* Advance state pointers and move on. */
373 : 0 : this_round -= tmp_count;
374 : : p = HEADER_SIZE;
375 : 0 : con_buf0 = con_buf + HEADER_SIZE;
376 : : /* If this_round >= 0, then p is even... */
377 [ # # ]: 0 : } else if (p & 1) {
378 : : /* Skip first byte for output if start address is odd
379 : : * Update region sizes up/down depending on free
380 : : * space in buffer.
381 : : */
382 : 0 : con_buf_start++;
383 [ # # ]: 0 : if (this_round < CON_BUF_SIZE)
384 : 0 : this_round++;
385 : : else
386 : 0 : orig_count--;
387 : : }
388 [ # # ]: 0 : if (this_round > 0) {
389 : : unsigned short *tmp_buf = (unsigned short *)con_buf0;
390 : :
391 : 0 : p -= HEADER_SIZE;
392 : 0 : p /= 2;
393 : 0 : col = p % maxcol;
394 : :
395 : 0 : org = screen_pos(vc, p, viewed);
396 : 0 : p += maxcol - col;
397 : :
398 : : /* Buffer has even length, so we can always copy
399 : : * character + attribute. We do not copy last byte
400 : : * to userspace if this_round is odd.
401 : : */
402 : 0 : this_round = (this_round + 1) >> 1;
403 : :
404 [ # # ]: 0 : while (this_round) {
405 : 0 : *tmp_buf++ = vcs_scr_readw(vc, org++);
406 : 0 : this_round --;
407 [ # # ]: 0 : if (++col == maxcol) {
408 : 0 : org = screen_pos(vc, p, viewed);
409 : : col = 0;
410 : 0 : p += maxcol;
411 : : }
412 : : }
413 : : }
414 : : }
415 : :
416 : : /* Finally, release the console semaphore while we push
417 : : * all the data to userspace from our temporary buffer.
418 : : *
419 : : * AKPM: Even though it's a semaphore, we should drop it because
420 : : * the pagefault handling code may want to call printk().
421 : : */
422 : :
423 : 0 : console_unlock();
424 : 0 : ret = copy_to_user(buf, con_buf_start, orig_count);
425 : 0 : console_lock();
426 : :
427 [ # # ]: 0 : if (ret) {
428 : 0 : read += (orig_count - ret);
429 : : ret = -EFAULT;
430 : 0 : break;
431 : : }
432 : 0 : buf += orig_count;
433 : 0 : pos += orig_count;
434 : 0 : read += orig_count;
435 : 0 : count -= orig_count;
436 : : }
437 : 0 : *ppos += read;
438 [ # # ]: 0 : if (read)
439 : : ret = read;
440 : : unlock_out:
441 : 0 : console_unlock();
442 : 0 : free_page((unsigned long) con_buf);
443 : 0 : return ret;
444 : : }
445 : :
446 : : static ssize_t
447 : 0 : vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
448 : : {
449 : : struct inode *inode = file_inode(file);
450 : : struct vc_data *vc;
451 : : long pos;
452 : : long attr, size, written;
453 : : char *con_buf0;
454 : : int col, maxcol, viewed;
455 : : u16 *org0 = NULL, *org = NULL;
456 : : size_t ret;
457 : : char *con_buf;
458 : :
459 [ # # ]: 0 : if (use_unicode(inode))
460 : : return -EOPNOTSUPP;
461 : :
462 : 0 : con_buf = (char *) __get_free_page(GFP_KERNEL);
463 [ # # ]: 0 : if (!con_buf)
464 : : return -ENOMEM;
465 : :
466 : 0 : pos = *ppos;
467 : :
468 : : /* Select the proper current console and verify
469 : : * sanity of the situation under the console lock.
470 : : */
471 : 0 : console_lock();
472 : :
473 : 0 : attr = use_attributes(inode);
474 : : ret = -ENXIO;
475 : 0 : vc = vcs_vc(inode, &viewed);
476 [ # # ]: 0 : if (!vc)
477 : : goto unlock_out;
478 : :
479 : 0 : size = vcs_size(inode);
480 : : ret = -EINVAL;
481 [ # # ]: 0 : if (pos < 0 || pos > size)
482 : : goto unlock_out;
483 [ # # ]: 0 : if (count > size - pos)
484 : : count = size - pos;
485 : : written = 0;
486 [ # # ]: 0 : while (count) {
487 : 0 : long this_round = count;
488 : : size_t orig_count;
489 : : long p;
490 : :
491 [ # # ]: 0 : if (this_round > CON_BUF_SIZE)
492 : : this_round = CON_BUF_SIZE;
493 : :
494 : : /* Temporarily drop the console lock so that we can read
495 : : * in the write data from userspace safely.
496 : : */
497 : 0 : console_unlock();
498 : 0 : ret = copy_from_user(con_buf, buf, this_round);
499 : 0 : console_lock();
500 : :
501 [ # # ]: 0 : if (ret) {
502 : 0 : this_round -= ret;
503 [ # # ]: 0 : if (!this_round) {
504 : : /* Abort loop if no data were copied. Otherwise
505 : : * fail with -EFAULT.
506 : : */
507 [ # # ]: 0 : if (written)
508 : : break;
509 : : ret = -EFAULT;
510 : : goto unlock_out;
511 : : }
512 : : }
513 : :
514 : : /* The vcs_size might have changed while we slept to grab
515 : : * the user buffer, so recheck.
516 : : * Return data written up to now on failure.
517 : : */
518 : 0 : size = vcs_size(inode);
519 [ # # ]: 0 : if (size < 0) {
520 [ # # ]: 0 : if (written)
521 : : break;
522 : 0 : ret = size;
523 : 0 : goto unlock_out;
524 : : }
525 [ # # ]: 0 : if (pos >= size)
526 : : break;
527 [ # # ]: 0 : if (this_round > size - pos)
528 : : this_round = size - pos;
529 : :
530 : : /* OK, now actually push the write to the console
531 : : * under the lock using the local kernel buffer.
532 : : */
533 : :
534 : : con_buf0 = con_buf;
535 : 0 : orig_count = this_round;
536 : 0 : maxcol = vc->vc_cols;
537 : : p = pos;
538 [ # # ]: 0 : if (!attr) {
539 : 0 : org0 = org = screen_pos(vc, p, viewed);
540 : 0 : col = p % maxcol;
541 : 0 : p += maxcol - col;
542 : :
543 [ # # ]: 0 : while (this_round > 0) {
544 : 0 : unsigned char c = *con_buf0++;
545 : :
546 : 0 : this_round--;
547 : 0 : vcs_scr_writew(vc,
548 : 0 : (vcs_scr_readw(vc, org) & 0xff00) | c, org);
549 : 0 : org++;
550 [ # # ]: 0 : if (++col == maxcol) {
551 : 0 : org = screen_pos(vc, p, viewed);
552 : : col = 0;
553 : 0 : p += maxcol;
554 : : }
555 : : }
556 : : } else {
557 [ # # ]: 0 : if (p < HEADER_SIZE) {
558 : : char header[HEADER_SIZE];
559 : :
560 : 0 : getconsxy(vc, header + 2);
561 [ # # ]: 0 : while (p < HEADER_SIZE && this_round > 0) {
562 : 0 : this_round--;
563 : 0 : header[p++] = *con_buf0++;
564 : : }
565 [ # # ]: 0 : if (!viewed)
566 : 0 : putconsxy(vc, header + 2);
567 : : }
568 : 0 : p -= HEADER_SIZE;
569 : 0 : col = (p/2) % maxcol;
570 [ # # ]: 0 : if (this_round > 0) {
571 : 0 : org0 = org = screen_pos(vc, p/2, viewed);
572 [ # # # # ]: 0 : if ((p & 1) && this_round > 0) {
573 : : char c;
574 : :
575 : 0 : this_round--;
576 : 0 : c = *con_buf0++;
577 : : #ifdef __BIG_ENDIAN
578 : : vcs_scr_writew(vc, c |
579 : : (vcs_scr_readw(vc, org) & 0xff00), org);
580 : : #else
581 : 0 : vcs_scr_writew(vc, (c << 8) |
582 : 0 : (vcs_scr_readw(vc, org) & 0xff), org);
583 : : #endif
584 : 0 : org++;
585 : 0 : p++;
586 [ # # ]: 0 : if (++col == maxcol) {
587 : 0 : org = screen_pos(vc, p/2, viewed);
588 : : col = 0;
589 : : }
590 : : }
591 : 0 : p /= 2;
592 : 0 : p += maxcol - col;
593 : : }
594 [ # # ]: 0 : while (this_round > 1) {
595 : : unsigned short w;
596 : :
597 : : w = get_unaligned(((unsigned short *)con_buf0));
598 : 0 : vcs_scr_writew(vc, w, org++);
599 : 0 : con_buf0 += 2;
600 : 0 : this_round -= 2;
601 [ # # ]: 0 : if (++col == maxcol) {
602 : 0 : org = screen_pos(vc, p, viewed);
603 : : col = 0;
604 : 0 : p += maxcol;
605 : : }
606 : : }
607 [ # # ]: 0 : if (this_round > 0) {
608 : : unsigned char c;
609 : :
610 : 0 : c = *con_buf0++;
611 : : #ifdef __BIG_ENDIAN
612 : : vcs_scr_writew(vc, (vcs_scr_readw(vc, org) & 0xff) | (c << 8), org);
613 : : #else
614 : 0 : vcs_scr_writew(vc, (vcs_scr_readw(vc, org) & 0xff00) | c, org);
615 : : #endif
616 : : }
617 : : }
618 : 0 : count -= orig_count;
619 : 0 : written += orig_count;
620 : 0 : buf += orig_count;
621 : 0 : pos += orig_count;
622 [ # # ]: 0 : if (org0)
623 : 0 : update_region(vc, (unsigned long)(org0), org - org0);
624 : : }
625 : 0 : *ppos += written;
626 : 0 : ret = written;
627 [ # # ]: 0 : if (written)
628 : 0 : vcs_scr_updated(vc);
629 : :
630 : : unlock_out:
631 : 0 : console_unlock();
632 : 0 : free_page((unsigned long) con_buf);
633 : 0 : return ret;
634 : : }
635 : :
636 : : static __poll_t
637 : 0 : vcs_poll(struct file *file, poll_table *wait)
638 : : {
639 : 0 : struct vcs_poll_data *poll = vcs_poll_data_get(file);
640 : : __poll_t ret = DEFAULT_POLLMASK|EPOLLERR;
641 : :
642 [ # # ]: 0 : if (poll) {
643 : 0 : poll_wait(file, &poll->waitq, wait);
644 [ # # # # ]: 0 : switch (poll->event) {
645 : : case VT_UPDATE:
646 : : ret = DEFAULT_POLLMASK|EPOLLPRI;
647 : 0 : break;
648 : : case VT_DEALLOCATE:
649 : : ret = DEFAULT_POLLMASK|EPOLLHUP|EPOLLERR;
650 : 0 : break;
651 : : case 0:
652 : : ret = DEFAULT_POLLMASK;
653 : 0 : break;
654 : : }
655 : : }
656 : 0 : return ret;
657 : : }
658 : :
659 : : static int
660 : 0 : vcs_fasync(int fd, struct file *file, int on)
661 : : {
662 : 0 : struct vcs_poll_data *poll = file->private_data;
663 : :
664 [ # # ]: 0 : if (!poll) {
665 : : /* don't allocate anything if all we want is disable fasync */
666 [ # # ]: 0 : if (!on)
667 : : return 0;
668 : 0 : poll = vcs_poll_data_get(file);
669 [ # # ]: 0 : if (!poll)
670 : : return -ENOMEM;
671 : : }
672 : :
673 : 0 : return fasync_helper(fd, file, on, &poll->fasync);
674 : : }
675 : :
676 : : static int
677 : 0 : vcs_open(struct inode *inode, struct file *filp)
678 : : {
679 : 0 : unsigned int currcons = console(inode);
680 : 0 : bool attr = use_attributes(inode);
681 : 0 : bool uni_mode = use_unicode(inode);
682 : : int ret = 0;
683 : :
684 : : /* we currently don't support attributes in unicode mode */
685 [ # # ]: 0 : if (attr && uni_mode)
686 : : return -EOPNOTSUPP;
687 : :
688 : 0 : console_lock();
689 [ # # # # ]: 0 : if(currcons && !vc_cons_allocated(currcons-1))
690 : : ret = -ENXIO;
691 : 0 : console_unlock();
692 : 0 : return ret;
693 : : }
694 : :
695 : 0 : static int vcs_release(struct inode *inode, struct file *file)
696 : : {
697 : 0 : struct vcs_poll_data *poll = file->private_data;
698 : :
699 [ # # ]: 0 : if (poll)
700 : : vcs_poll_data_free(poll);
701 : 0 : return 0;
702 : : }
703 : :
704 : : static const struct file_operations vcs_fops = {
705 : : .llseek = vcs_lseek,
706 : : .read = vcs_read,
707 : : .write = vcs_write,
708 : : .poll = vcs_poll,
709 : : .fasync = vcs_fasync,
710 : : .open = vcs_open,
711 : : .release = vcs_release,
712 : : };
713 : :
714 : : static struct class *vc_class;
715 : :
716 : 2828 : void vcs_make_sysfs(int index)
717 : : {
718 : 2828 : device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 1), NULL,
719 : : "vcs%u", index + 1);
720 : 2828 : device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 65), NULL,
721 : : "vcsu%u", index + 1);
722 : 2828 : device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 129), NULL,
723 : : "vcsa%u", index + 1);
724 : 2828 : }
725 : :
726 : 0 : void vcs_remove_sysfs(int index)
727 : : {
728 : 0 : device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 1));
729 : 0 : device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 65));
730 : 0 : device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 129));
731 : 0 : }
732 : :
733 : 404 : int __init vcs_init(void)
734 : : {
735 : : unsigned int i;
736 : :
737 [ - + ]: 404 : if (register_chrdev(VCS_MAJOR, "vcs", &vcs_fops))
738 : 0 : panic("unable to get major %d for vcs device", VCS_MAJOR);
739 : 404 : vc_class = class_create(THIS_MODULE, "vc");
740 : :
741 : 404 : device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 0), NULL, "vcs");
742 : 404 : device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 64), NULL, "vcsu");
743 : 404 : device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 128), NULL, "vcsa");
744 [ + + ]: 808 : for (i = 0; i < MIN_NR_CONSOLES; i++)
745 : 404 : vcs_make_sysfs(i);
746 : 404 : return 0;
747 : : }
|