diff options
Diffstat (limited to 'drivers/accessibility')
-rw-r--r-- | drivers/accessibility/speakup/Makefile | 4 | ||||
-rw-r--r-- | drivers/accessibility/speakup/devsynth.c | 116 | ||||
-rw-r--r-- | drivers/accessibility/speakup/genmap.c | 1 | ||||
-rw-r--r-- | drivers/accessibility/speakup/main.c | 2 | ||||
-rw-r--r-- | drivers/accessibility/speakup/makemapdata.c | 1 | ||||
-rw-r--r-- | drivers/accessibility/speakup/speakup.h | 2 | ||||
-rw-r--r-- | drivers/accessibility/speakup/synth.c | 96 |
7 files changed, 195 insertions, 27 deletions
diff --git a/drivers/accessibility/speakup/Makefile b/drivers/accessibility/speakup/Makefile index 6f6a83565c0d..14ba1cca87f4 100644 --- a/drivers/accessibility/speakup/Makefile +++ b/drivers/accessibility/speakup/Makefile @@ -40,9 +40,7 @@ hostprogs += makemapdata makemapdata-objs := makemapdata.o quiet_cmd_mkmap = MKMAP $@ - cmd_mkmap = TOPDIR=$(srctree) \ - SPKDIR=$(if $(KBUILD_EXTMOD),$(KBUILD_EXTMOD),$(srctree)/drivers/accessibility/speakup) \ - $(obj)/makemapdata > $@ + cmd_mkmap = TOPDIR=$(srctree) SPKDIR=$(src) $(obj)/makemapdata > $@ $(obj)/mapdata.h: $(obj)/makemapdata $(call cmd,mkmap) diff --git a/drivers/accessibility/speakup/devsynth.c b/drivers/accessibility/speakup/devsynth.c index d30571663585..e3d909bd0480 100644 --- a/drivers/accessibility/speakup/devsynth.c +++ b/drivers/accessibility/speakup/devsynth.c @@ -7,9 +7,10 @@ #include "speakup.h" #include "spk_priv.h" -static int misc_registered; +static int synth_registered, synthu_registered; static int dev_opened; +/* Latin1 version */ static ssize_t speakup_file_write(struct file *fp, const char __user *buffer, size_t nbytes, loff_t *ppos) { @@ -34,6 +35,65 @@ static ssize_t speakup_file_write(struct file *fp, const char __user *buffer, return (ssize_t)nbytes; } +/* UTF-8 version */ +static ssize_t speakup_file_writeu(struct file *fp, const char __user *buffer, + size_t nbytes, loff_t *ppos) +{ + size_t count = nbytes, consumed, want; + const char __user *ptr = buffer; + size_t bytes; + unsigned long flags; + unsigned char buf[256]; + u16 ubuf[256]; + size_t in, out; + + if (!synth) + return -ENODEV; + + want = 1; + while (count >= want) { + /* Copy some UTF-8 piece from userland */ + bytes = min(count, sizeof(buf)); + if (copy_from_user(buf, ptr, bytes)) + return -EFAULT; + + /* Convert to u16 */ + for (in = 0, out = 0; in < bytes; in += consumed) { + s32 value; + + value = synth_utf8_get(buf + in, bytes - in, &consumed, &want); + if (value == -1) { + /* Invalid or incomplete */ + + if (want > bytes - in) + /* We don't have it all yet, stop here + * and wait for the rest + */ + bytes = in; + + continue; + } + + if (value < 0x10000) + ubuf[out++] = value; + } + + count -= bytes; + ptr += bytes; + + /* And speak this up */ + if (out) { + spin_lock_irqsave(&speakup_info.spinlock, flags); + for (in = 0; in < out; in++) + synth_buffer_add(ubuf[in]); + synth_start(); + spin_unlock_irqrestore(&speakup_info.spinlock, flags); + } + } + + return (ssize_t)(nbytes - count); +} + static ssize_t speakup_file_read(struct file *fp, char __user *buf, size_t nbytes, loff_t *ppos) { @@ -62,31 +122,57 @@ static const struct file_operations synth_fops = { .release = speakup_file_release, }; +static const struct file_operations synthu_fops = { + .read = speakup_file_read, + .write = speakup_file_writeu, + .open = speakup_file_open, + .release = speakup_file_release, +}; + static struct miscdevice synth_device = { .minor = MISC_DYNAMIC_MINOR, .name = "synth", .fops = &synth_fops, }; +static struct miscdevice synthu_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "synthu", + .fops = &synthu_fops, +}; + void speakup_register_devsynth(void) { - if (misc_registered != 0) - return; -/* zero it so if register fails, deregister will not ref invalid ptrs */ - if (misc_register(&synth_device)) { - pr_warn("Couldn't initialize miscdevice /dev/synth.\n"); - } else { - pr_info("initialized device: /dev/synth, node (MAJOR %d, MINOR %d)\n", - MISC_MAJOR, synth_device.minor); - misc_registered = 1; + if (!synth_registered) { + if (misc_register(&synth_device)) { + pr_warn("Couldn't initialize miscdevice /dev/synth.\n"); + } else { + pr_info("initialized device: /dev/synth, node (MAJOR %d, MINOR %d)\n", + MISC_MAJOR, synth_device.minor); + synth_registered = 1; + } + } + if (!synthu_registered) { + if (misc_register(&synthu_device)) { + pr_warn("Couldn't initialize miscdevice /dev/synthu.\n"); + } else { + pr_info("initialized device: /dev/synthu, node (MAJOR %d, MINOR %d)\n", + MISC_MAJOR, synthu_device.minor); + synthu_registered = 1; + } } } void speakup_unregister_devsynth(void) { - if (!misc_registered) - return; - pr_info("speakup: unregistering synth device /dev/synth\n"); - misc_deregister(&synth_device); - misc_registered = 0; + if (synth_registered) { + pr_info("speakup: unregistering synth device /dev/synth\n"); + misc_deregister(&synth_device); + synth_registered = 0; + } + if (synthu_registered) { + pr_info("speakup: unregistering synth device /dev/synthu\n"); + misc_deregister(&synthu_device); + synthu_registered = 0; + } } diff --git a/drivers/accessibility/speakup/genmap.c b/drivers/accessibility/speakup/genmap.c index 0125000e00d9..0882bab10fb8 100644 --- a/drivers/accessibility/speakup/genmap.c +++ b/drivers/accessibility/speakup/genmap.c @@ -10,7 +10,6 @@ #include <stdio.h> #include <libgen.h> #include <string.h> -#include <linux/version.h> #include <ctype.h> #include "utils.h" diff --git a/drivers/accessibility/speakup/main.c b/drivers/accessibility/speakup/main.c index 1fbc9b921c4f..f677ad2177c2 100644 --- a/drivers/accessibility/speakup/main.c +++ b/drivers/accessibility/speakup/main.c @@ -574,7 +574,7 @@ static u_long get_word(struct vc_data *vc) } attr_ch = get_char(vc, (u_short *)tmp_pos, &spk_attr); buf[cnt++] = attr_ch; - while (tmpx < vc->vc_cols - 1) { + while (tmpx < vc->vc_cols - 1 && cnt < ARRAY_SIZE(buf) - 1) { tmp_pos += 2; tmpx++; ch = get_char(vc, (u_short *)tmp_pos, &temp); diff --git a/drivers/accessibility/speakup/makemapdata.c b/drivers/accessibility/speakup/makemapdata.c index d7d41bb9b05f..55e4ef8a93dc 100644 --- a/drivers/accessibility/speakup/makemapdata.c +++ b/drivers/accessibility/speakup/makemapdata.c @@ -10,7 +10,6 @@ #include <stdio.h> #include <libgen.h> #include <string.h> -#include <linux/version.h> #include <ctype.h> #include "utils.h" diff --git a/drivers/accessibility/speakup/speakup.h b/drivers/accessibility/speakup/speakup.h index 364fde99749e..54f1226ea061 100644 --- a/drivers/accessibility/speakup/speakup.h +++ b/drivers/accessibility/speakup/speakup.h @@ -76,7 +76,9 @@ int speakup_paste_selection(struct tty_struct *tty); void speakup_cancel_paste(void); void speakup_register_devsynth(void); void speakup_unregister_devsynth(void); +s32 synth_utf8_get(const char *buf, size_t count, size_t *consumed, size_t *want); void synth_write(const char *buf, size_t count); +void synth_writeu(const char *buf, size_t count); int synth_supports_indexing(void); extern struct vc_data *spk_sel_cons; diff --git a/drivers/accessibility/speakup/synth.c b/drivers/accessibility/speakup/synth.c index eea2a2fa4f01..85062e605d79 100644 --- a/drivers/accessibility/speakup/synth.c +++ b/drivers/accessibility/speakup/synth.c @@ -208,17 +208,104 @@ void spk_do_flush(void) wake_up_process(speakup_task); } -void synth_write(const char *buf, size_t count) +void synth_write(const char *_buf, size_t count) { + const unsigned char *buf = (const unsigned char *) _buf; + while (count--) synth_buffer_add(*buf++); synth_start(); } +/* Consume one utf-8 character from buf (that contains up to count bytes), + * returns the unicode codepoint if valid, -1 otherwise. + * In all cases, returns the number of consumed bytes in *consumed, + * and the minimum number of bytes that would be needed for the next character + * in *want. + */ +s32 synth_utf8_get(const char *buf, size_t count, size_t *consumed, size_t *want) +{ + unsigned char c = buf[0]; + int nbytes = 8 - fls(c ^ 0xff); + u32 value; + size_t i; + + switch (nbytes) { + case 8: /* 0xff */ + case 7: /* 0xfe */ + case 1: /* 0x80 */ + /* Invalid, drop */ + *consumed = 1; + *want = 1; + return -1; + + case 0: + /* ASCII, take as such */ + *consumed = 1; + *want = 1; + return c; + + default: + /* 2..6-byte UTF-8 */ + + if (count < nbytes) { + /* We don't have it all */ + *consumed = 0; + *want = nbytes; + return -1; + } + + /* First byte */ + value = c & ((1u << (7 - nbytes)) - 1); + + /* Other bytes */ + for (i = 1; i < nbytes; i++) { + c = buf[i]; + if ((c & 0xc0) != 0x80) { + /* Invalid, drop the head */ + *consumed = i; + *want = 1; + return -1; + } + value = (value << 6) | (c & 0x3f); + } + + *consumed = nbytes; + *want = 1; + return value; + } +} + +void synth_writeu(const char *buf, size_t count) +{ + size_t i, consumed, want; + + /* Convert to u16 */ + for (i = 0; i < count; i++) { + s32 value; + + value = synth_utf8_get(buf + i, count - i, &consumed, &want); + if (value == -1) { + /* Invalid or incomplete */ + + if (want > count - i) + /* We don't have it all, stop */ + count = i; + + continue; + } + + if (value < 0x10000) + synth_buffer_add(value); + } + + synth_start(); +} + void synth_printf(const char *fmt, ...) { va_list args; - unsigned char buf[160], *p; + unsigned char buf[160]; int r; va_start(args, fmt); @@ -227,10 +314,7 @@ void synth_printf(const char *fmt, ...) if (r > sizeof(buf) - 1) r = sizeof(buf) - 1; - p = buf; - while (r--) - synth_buffer_add(*p++); - synth_start(); + synth_writeu(buf, r); } EXPORT_SYMBOL_GPL(synth_printf); |