Commit b293d758470e971253eec42b817bc9ef1213b228
Committed by
Linus Torvalds
1 parent
fe9d4f5763
Exists in
master
and in
39 other branches
Console events and accessibility
Some external modules like Speakup need to monitor console output. This adds a VT notifier that such modules can use to get console output events: allocation, deallocation, writes, other updates (cursor position, switch, etc.) [akpm@linux-foundation.org: fix headers_check] Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org> Cc: Dmitry Torokhov <dtor@mail.ru> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 3 changed files with 62 additions and 1 deletions Side-by-side Diff
drivers/char/vt.c
... | ... | @@ -99,6 +99,7 @@ |
99 | 99 | #include <linux/pm.h> |
100 | 100 | #include <linux/font.h> |
101 | 101 | #include <linux/bitops.h> |
102 | +#include <linux/notifier.h> | |
102 | 103 | |
103 | 104 | #include <asm/io.h> |
104 | 105 | #include <asm/system.h> |
... | ... | @@ -223,6 +224,35 @@ |
223 | 224 | }; |
224 | 225 | |
225 | 226 | /* |
227 | + * Notifier list for console events. | |
228 | + */ | |
229 | +static ATOMIC_NOTIFIER_HEAD(vt_notifier_list); | |
230 | + | |
231 | +int register_vt_notifier(struct notifier_block *nb) | |
232 | +{ | |
233 | + return atomic_notifier_chain_register(&vt_notifier_list, nb); | |
234 | +} | |
235 | +EXPORT_SYMBOL_GPL(register_vt_notifier); | |
236 | + | |
237 | +int unregister_vt_notifier(struct notifier_block *nb) | |
238 | +{ | |
239 | + return atomic_notifier_chain_unregister(&vt_notifier_list, nb); | |
240 | +} | |
241 | +EXPORT_SYMBOL_GPL(unregister_vt_notifier); | |
242 | + | |
243 | +static void notify_write(struct vc_data *vc, unsigned int unicode) | |
244 | +{ | |
245 | + struct vt_notifier_param param = { .vc = vc, unicode = unicode }; | |
246 | + atomic_notifier_call_chain(&vt_notifier_list, VT_WRITE, ¶m); | |
247 | +} | |
248 | + | |
249 | +static void notify_update(struct vc_data *vc) | |
250 | +{ | |
251 | + struct vt_notifier_param param = { .vc = vc }; | |
252 | + atomic_notifier_call_chain(&vt_notifier_list, VT_UPDATE, ¶m); | |
253 | +} | |
254 | + | |
255 | +/* | |
226 | 256 | * Low-Level Functions |
227 | 257 | */ |
228 | 258 | |
... | ... | @@ -718,6 +748,7 @@ |
718 | 748 | return -ENXIO; |
719 | 749 | if (!vc_cons[currcons].d) { |
720 | 750 | struct vc_data *vc; |
751 | + struct vt_notifier_param param; | |
721 | 752 | |
722 | 753 | /* prevent users from taking too much memory */ |
723 | 754 | if (currcons >= MAX_NR_USER_CONSOLES && !capable(CAP_SYS_RESOURCE)) |
... | ... | @@ -729,7 +760,7 @@ |
729 | 760 | /* although the numbers above are not valid since long ago, the |
730 | 761 | point is still up-to-date and the comment still has its value |
731 | 762 | even if only as a historical artifact. --mj, July 1998 */ |
732 | - vc = kzalloc(sizeof(struct vc_data), GFP_KERNEL); | |
763 | + param.vc = vc = kzalloc(sizeof(struct vc_data), GFP_KERNEL); | |
733 | 764 | if (!vc) |
734 | 765 | return -ENOMEM; |
735 | 766 | vc_cons[currcons].d = vc; |
... | ... | @@ -746,6 +777,7 @@ |
746 | 777 | } |
747 | 778 | vc->vc_kmalloced = 1; |
748 | 779 | vc_init(vc, vc->vc_rows, vc->vc_cols, 1); |
780 | + atomic_notifier_call_chain(&vt_notifier_list, VT_ALLOCATE, ¶m); | |
749 | 781 | } |
750 | 782 | return 0; |
751 | 783 | } |
... | ... | @@ -907,6 +939,8 @@ |
907 | 939 | |
908 | 940 | if (vc_cons_allocated(currcons)) { |
909 | 941 | struct vc_data *vc = vc_cons[currcons].d; |
942 | + struct vt_notifier_param param = { .vc = vc }; | |
943 | + atomic_notifier_call_chain(&vt_notifier_list, VT_DEALLOCATE, ¶m); | |
910 | 944 | vc->vc_sw->con_deinit(vc); |
911 | 945 | put_pid(vc->vt_pid); |
912 | 946 | module_put(vc->vc_sw->owner); |
... | ... | @@ -1019,6 +1053,7 @@ |
1019 | 1053 | vc->vc_pos += vc->vc_size_row; |
1020 | 1054 | } |
1021 | 1055 | vc->vc_need_wrap = 0; |
1056 | + notify_write(vc, '\n'); | |
1022 | 1057 | } |
1023 | 1058 | |
1024 | 1059 | static void ri(struct vc_data *vc) |
... | ... | @@ -1039,6 +1074,7 @@ |
1039 | 1074 | { |
1040 | 1075 | vc->vc_pos -= vc->vc_x << 1; |
1041 | 1076 | vc->vc_need_wrap = vc->vc_x = 0; |
1077 | + notify_write(vc, '\r'); | |
1042 | 1078 | } |
1043 | 1079 | |
1044 | 1080 | static inline void bs(struct vc_data *vc) |
... | ... | @@ -1047,6 +1083,7 @@ |
1047 | 1083 | vc->vc_pos -= 2; |
1048 | 1084 | vc->vc_x--; |
1049 | 1085 | vc->vc_need_wrap = 0; |
1086 | + notify_write(vc, '\b'); | |
1050 | 1087 | } |
1051 | 1088 | } |
1052 | 1089 | |
... | ... | @@ -1593,6 +1630,7 @@ |
1593 | 1630 | break; |
1594 | 1631 | } |
1595 | 1632 | vc->vc_pos += (vc->vc_x << 1); |
1633 | + notify_write(vc, '\t'); | |
1596 | 1634 | return; |
1597 | 1635 | case 10: case 11: case 12: |
1598 | 1636 | lf(vc); |
... | ... | @@ -2252,6 +2290,7 @@ |
2252 | 2290 | tc = conv_uni_to_pc(vc, ' '); /* A space is printed in the second column */ |
2253 | 2291 | if (tc < 0) tc = ' '; |
2254 | 2292 | } |
2293 | + notify_write(vc, c); | |
2255 | 2294 | |
2256 | 2295 | if (inverse) { |
2257 | 2296 | FLUSH |
... | ... | @@ -2274,6 +2313,7 @@ |
2274 | 2313 | release_console_sem(); |
2275 | 2314 | |
2276 | 2315 | out: |
2316 | + notify_update(vc); | |
2277 | 2317 | return n; |
2278 | 2318 | #undef FLUSH |
2279 | 2319 | } |
... | ... | @@ -2317,6 +2357,7 @@ |
2317 | 2357 | do_blank_screen(0); |
2318 | 2358 | blank_timer_expired = 0; |
2319 | 2359 | } |
2360 | + notify_update(vc_cons[fg_console].d); | |
2320 | 2361 | |
2321 | 2362 | release_console_sem(); |
2322 | 2363 | } |
... | ... | @@ -2418,6 +2459,7 @@ |
2418 | 2459 | continue; |
2419 | 2460 | } |
2420 | 2461 | scr_writew((vc->vc_attr << 8) + c, (unsigned short *)vc->vc_pos); |
2462 | + notify_write(vc, c); | |
2421 | 2463 | cnt++; |
2422 | 2464 | if (myx == vc->vc_cols - 1) { |
2423 | 2465 | vc->vc_need_wrap = 1; |
... | ... | @@ -2436,6 +2478,7 @@ |
2436 | 2478 | } |
2437 | 2479 | } |
2438 | 2480 | set_cursor(vc); |
2481 | + notify_update(vc); | |
2439 | 2482 | |
2440 | 2483 | quit: |
2441 | 2484 | clear_bit(0, &printing); |
include/linux/notifier.h
... | ... | @@ -242,6 +242,12 @@ |
242 | 242 | |
243 | 243 | extern struct blocking_notifier_head reboot_notifier_list; |
244 | 244 | |
245 | +/* Virtual Terminal events. */ | |
246 | +#define VT_ALLOCATE 0x0001 /* Console got allocated */ | |
247 | +#define VT_DEALLOCATE 0x0002 /* Console will be deallocated */ | |
248 | +#define VT_WRITE 0x0003 /* A char got output */ | |
249 | +#define VT_UPDATE 0x0004 /* A bigger update occurred */ | |
250 | + | |
245 | 251 | #endif /* __KERNEL__ */ |
246 | 252 | #endif /* _LINUX_NOTIFIER_H */ |
include/linux/vt.h
1 | 1 | #ifndef _LINUX_VT_H |
2 | 2 | #define _LINUX_VT_H |
3 | 3 | |
4 | +#ifdef __KERNEL__ | |
5 | +struct notifier_block; | |
6 | + | |
7 | +struct vt_notifier_param { | |
8 | + struct vc_data *vc; /* VC on which the update happened */ | |
9 | + unsigned int c; /* Printed char */ | |
10 | +}; | |
11 | + | |
12 | +extern int register_vt_notifier(struct notifier_block *nb); | |
13 | +extern int unregister_vt_notifier(struct notifier_block *nb); | |
14 | +#endif | |
15 | + | |
4 | 16 | /* |
5 | 17 | * These constants are also useful for user-level apps (e.g., VC |
6 | 18 | * resizing). |