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