Commit b293d758 authored by Samuel Thibault's avatar Samuel Thibault Committed by Linus Torvalds

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: default avatarSamuel Thibault <samuel.thibault@ens-lyon.org>
Cc: Dmitry Torokhov <dtor@mail.ru>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent fe9d4f57
...@@ -99,6 +99,7 @@ ...@@ -99,6 +99,7 @@
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/font.h> #include <linux/font.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/notifier.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/system.h> #include <asm/system.h>
...@@ -222,6 +223,35 @@ enum { ...@@ -222,6 +223,35 @@ enum {
blank_vesa_wait, blank_vesa_wait,
}; };
/*
* Notifier list for console events.
*/
static ATOMIC_NOTIFIER_HEAD(vt_notifier_list);
int register_vt_notifier(struct notifier_block *nb)
{
return atomic_notifier_chain_register(&vt_notifier_list, nb);
}
EXPORT_SYMBOL_GPL(register_vt_notifier);
int unregister_vt_notifier(struct notifier_block *nb)
{
return atomic_notifier_chain_unregister(&vt_notifier_list, nb);
}
EXPORT_SYMBOL_GPL(unregister_vt_notifier);
static void notify_write(struct vc_data *vc, unsigned int unicode)
{
struct vt_notifier_param param = { .vc = vc, unicode = unicode };
atomic_notifier_call_chain(&vt_notifier_list, VT_WRITE, &param);
}
static void notify_update(struct vc_data *vc)
{
struct vt_notifier_param param = { .vc = vc };
atomic_notifier_call_chain(&vt_notifier_list, VT_UPDATE, &param);
}
/* /*
* Low-Level Functions * Low-Level Functions
*/ */
...@@ -718,6 +748,7 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ ...@@ -718,6 +748,7 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */
return -ENXIO; return -ENXIO;
if (!vc_cons[currcons].d) { if (!vc_cons[currcons].d) {
struct vc_data *vc; struct vc_data *vc;
struct vt_notifier_param param;
/* prevent users from taking too much memory */ /* prevent users from taking too much memory */
if (currcons >= MAX_NR_USER_CONSOLES && !capable(CAP_SYS_RESOURCE)) if (currcons >= MAX_NR_USER_CONSOLES && !capable(CAP_SYS_RESOURCE))
...@@ -729,7 +760,7 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ ...@@ -729,7 +760,7 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */
/* although the numbers above are not valid since long ago, the /* although the numbers above are not valid since long ago, the
point is still up-to-date and the comment still has its value point is still up-to-date and the comment still has its value
even if only as a historical artifact. --mj, July 1998 */ even if only as a historical artifact. --mj, July 1998 */
vc = kzalloc(sizeof(struct vc_data), GFP_KERNEL); param.vc = vc = kzalloc(sizeof(struct vc_data), GFP_KERNEL);
if (!vc) if (!vc)
return -ENOMEM; return -ENOMEM;
vc_cons[currcons].d = vc; vc_cons[currcons].d = vc;
...@@ -746,6 +777,7 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ ...@@ -746,6 +777,7 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */
} }
vc->vc_kmalloced = 1; vc->vc_kmalloced = 1;
vc_init(vc, vc->vc_rows, vc->vc_cols, 1); vc_init(vc, vc->vc_rows, vc->vc_cols, 1);
atomic_notifier_call_chain(&vt_notifier_list, VT_ALLOCATE, &param);
} }
return 0; return 0;
} }
...@@ -907,6 +939,8 @@ void vc_deallocate(unsigned int currcons) ...@@ -907,6 +939,8 @@ void vc_deallocate(unsigned int currcons)
if (vc_cons_allocated(currcons)) { if (vc_cons_allocated(currcons)) {
struct vc_data *vc = vc_cons[currcons].d; struct vc_data *vc = vc_cons[currcons].d;
struct vt_notifier_param param = { .vc = vc };
atomic_notifier_call_chain(&vt_notifier_list, VT_DEALLOCATE, &param);
vc->vc_sw->con_deinit(vc); vc->vc_sw->con_deinit(vc);
put_pid(vc->vt_pid); put_pid(vc->vt_pid);
module_put(vc->vc_sw->owner); module_put(vc->vc_sw->owner);
...@@ -1019,6 +1053,7 @@ static void lf(struct vc_data *vc) ...@@ -1019,6 +1053,7 @@ static void lf(struct vc_data *vc)
vc->vc_pos += vc->vc_size_row; vc->vc_pos += vc->vc_size_row;
} }
vc->vc_need_wrap = 0; vc->vc_need_wrap = 0;
notify_write(vc, '\n');
} }
static void ri(struct vc_data *vc) static void ri(struct vc_data *vc)
...@@ -1039,6 +1074,7 @@ static inline void cr(struct vc_data *vc) ...@@ -1039,6 +1074,7 @@ static inline void cr(struct vc_data *vc)
{ {
vc->vc_pos -= vc->vc_x << 1; vc->vc_pos -= vc->vc_x << 1;
vc->vc_need_wrap = vc->vc_x = 0; vc->vc_need_wrap = vc->vc_x = 0;
notify_write(vc, '\r');
} }
static inline void bs(struct vc_data *vc) static inline void bs(struct vc_data *vc)
...@@ -1047,6 +1083,7 @@ static inline void bs(struct vc_data *vc) ...@@ -1047,6 +1083,7 @@ static inline void bs(struct vc_data *vc)
vc->vc_pos -= 2; vc->vc_pos -= 2;
vc->vc_x--; vc->vc_x--;
vc->vc_need_wrap = 0; vc->vc_need_wrap = 0;
notify_write(vc, '\b');
} }
} }
...@@ -1593,6 +1630,7 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c) ...@@ -1593,6 +1630,7 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
break; break;
} }
vc->vc_pos += (vc->vc_x << 1); vc->vc_pos += (vc->vc_x << 1);
notify_write(vc, '\t');
return; return;
case 10: case 11: case 12: case 10: case 11: case 12:
lf(vc); lf(vc);
...@@ -2252,6 +2290,7 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co ...@@ -2252,6 +2290,7 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co
tc = conv_uni_to_pc(vc, ' '); /* A space is printed in the second column */ tc = conv_uni_to_pc(vc, ' '); /* A space is printed in the second column */
if (tc < 0) tc = ' '; if (tc < 0) tc = ' ';
} }
notify_write(vc, c);
if (inverse) { if (inverse) {
FLUSH FLUSH
...@@ -2274,6 +2313,7 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co ...@@ -2274,6 +2313,7 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co
release_console_sem(); release_console_sem();
out: out:
notify_update(vc);
return n; return n;
#undef FLUSH #undef FLUSH
} }
...@@ -2317,6 +2357,7 @@ static void console_callback(struct work_struct *ignored) ...@@ -2317,6 +2357,7 @@ static void console_callback(struct work_struct *ignored)
do_blank_screen(0); do_blank_screen(0);
blank_timer_expired = 0; blank_timer_expired = 0;
} }
notify_update(vc_cons[fg_console].d);
release_console_sem(); release_console_sem();
} }
...@@ -2418,6 +2459,7 @@ static void vt_console_print(struct console *co, const char *b, unsigned count) ...@@ -2418,6 +2459,7 @@ static void vt_console_print(struct console *co, const char *b, unsigned count)
continue; continue;
} }
scr_writew((vc->vc_attr << 8) + c, (unsigned short *)vc->vc_pos); scr_writew((vc->vc_attr << 8) + c, (unsigned short *)vc->vc_pos);
notify_write(vc, c);
cnt++; cnt++;
if (myx == vc->vc_cols - 1) { if (myx == vc->vc_cols - 1) {
vc->vc_need_wrap = 1; vc->vc_need_wrap = 1;
...@@ -2436,6 +2478,7 @@ static void vt_console_print(struct console *co, const char *b, unsigned count) ...@@ -2436,6 +2478,7 @@ static void vt_console_print(struct console *co, const char *b, unsigned count)
} }
} }
set_cursor(vc); set_cursor(vc);
notify_update(vc);
quit: quit:
clear_bit(0, &printing); clear_bit(0, &printing);
......
...@@ -242,5 +242,11 @@ static inline int notifier_to_errno(int ret) ...@@ -242,5 +242,11 @@ static inline int notifier_to_errno(int ret)
extern struct blocking_notifier_head reboot_notifier_list; extern struct blocking_notifier_head reboot_notifier_list;
/* Virtual Terminal events. */
#define VT_ALLOCATE 0x0001 /* Console got allocated */
#define VT_DEALLOCATE 0x0002 /* Console will be deallocated */
#define VT_WRITE 0x0003 /* A char got output */
#define VT_UPDATE 0x0004 /* A bigger update occurred */
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* _LINUX_NOTIFIER_H */ #endif /* _LINUX_NOTIFIER_H */
#ifndef _LINUX_VT_H #ifndef _LINUX_VT_H
#define _LINUX_VT_H #define _LINUX_VT_H
#ifdef __KERNEL__
struct notifier_block;
struct vt_notifier_param {
struct vc_data *vc; /* VC on which the update happened */
unsigned int c; /* Printed char */
};
extern int register_vt_notifier(struct notifier_block *nb);
extern int unregister_vt_notifier(struct notifier_block *nb);
#endif
/* /*
* These constants are also useful for user-level apps (e.g., VC * These constants are also useful for user-level apps (e.g., VC
* resizing). * resizing).
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment