diff options
Diffstat (limited to 'drivers/staging/speakup')
-rw-r--r-- | drivers/staging/speakup/speakup_soft.c | 93 |
1 files changed, 81 insertions, 12 deletions
diff --git a/drivers/staging/speakup/speakup_soft.c b/drivers/staging/speakup/speakup_soft.c index 2529c4629c53..1f99ce912ab9 100644 --- a/drivers/staging/speakup/speakup_soft.c +++ b/drivers/staging/speakup/speakup_soft.c @@ -29,6 +29,7 @@ #define DRV_VERSION "2.6" #define SOFTSYNTH_MINOR 26 /* might as well give it one more than /dev/synth */ +#define SOFTSYNTHU_MINOR 27 /* might as well give it one more than /dev/synth */ #define PROCSPEECH 0x0d #define CLEAR_SYNTH 0x18 @@ -37,7 +38,7 @@ static void softsynth_release(void); static int softsynth_is_alive(struct spk_synth *synth); static unsigned char get_index(void); -static struct miscdevice synth_device; +static struct miscdevice synth_device, synthu_device; static int init_pos; static int misc_registered; @@ -199,13 +200,13 @@ static int softsynth_close(struct inode *inode, struct file *fp) return 0; } -static ssize_t softsynth_read(struct file *fp, char __user *buf, size_t count, - loff_t *pos) +static ssize_t softsynthx_read(struct file *fp, char __user *buf, size_t count, + loff_t *pos, int unicode) { int chars_sent = 0; char __user *cp; char *init; - char ch; + u16 ch; int empty; unsigned long flags; DEFINE_WAIT(wait); @@ -213,7 +214,8 @@ static ssize_t softsynth_read(struct file *fp, char __user *buf, size_t count, spin_lock_irqsave(&speakup_info.spinlock, flags); while (1) { prepare_to_wait(&speakup_event, &wait, TASK_INTERRUPTIBLE); - synth_buffer_skip_nonlatin1(); + if (!unicode) + synth_buffer_skip_nonlatin1(); if (!synth_buffer_empty() || speakup_info.flushing) break; spin_unlock_irqrestore(&speakup_info.spinlock, flags); @@ -232,23 +234,57 @@ static ssize_t softsynth_read(struct file *fp, char __user *buf, size_t count, cp = buf; init = get_initstring(); - while (chars_sent < count) { + + /* Keep 3 bytes available for a 16bit UTF-8-encoded character */ + while (chars_sent <= count - 3) { if (speakup_info.flushing) { speakup_info.flushing = 0; ch = '\x18'; - } else if (synth_buffer_empty()) { - break; } else if (init[init_pos]) { ch = init[init_pos++]; } else { + if (!unicode) + synth_buffer_skip_nonlatin1(); + if (synth_buffer_empty()) + break; ch = synth_buffer_getc(); } spin_unlock_irqrestore(&speakup_info.spinlock, flags); - if (copy_to_user(cp, &ch, 1)) - return -EFAULT; + + if ((!unicode && ch < 0x100) || (unicode && ch < 0x80)) { + u_char c = ch; + + if (copy_to_user(cp, &c, 1)) + return -EFAULT; + + chars_sent++; + cp++; + } else if (unicode && ch < 0x800) { + u_char s[2] = { + 0xc0 | (ch >> 6), + 0x80 | (ch & 0x3f) + }; + + if (copy_to_user(cp, s, sizeof(s))) + return -EFAULT; + + chars_sent += sizeof(s); + cp += sizeof(s); + } else if (unicode) { + u_char s[3] = { + 0xe0 | (ch >> 12), + 0x80 | ((ch >> 6) & 0x3f), + 0x80 | (ch & 0x3f) + }; + + if (copy_to_user(cp, s, sizeof(s))) + return -EFAULT; + + chars_sent += sizeof(s); + cp += sizeof(s); + } + spin_lock_irqsave(&speakup_info.spinlock, flags); - chars_sent++; - cp++; } *pos += chars_sent; empty = synth_buffer_empty(); @@ -260,6 +296,18 @@ static ssize_t softsynth_read(struct file *fp, char __user *buf, size_t count, return chars_sent; } +static ssize_t softsynth_read(struct file *fp, char __user *buf, size_t count, + loff_t *pos) +{ + return softsynthx_read(fp, buf, count, pos, 0); +} + +static ssize_t softsynthu_read(struct file *fp, char __user *buf, size_t count, + loff_t *pos) +{ + return softsynthx_read(fp, buf, count, pos, 1); +} + static int last_index; static ssize_t softsynth_write(struct file *fp, const char __user *buf, @@ -309,6 +357,15 @@ static const struct file_operations softsynth_fops = { .release = softsynth_close, }; +static const struct file_operations softsynthu_fops = { + .owner = THIS_MODULE, + .poll = softsynth_poll, + .read = softsynthu_read, + .write = softsynth_write, + .open = softsynth_open, + .release = softsynth_close, +}; + static int softsynth_probe(struct spk_synth *synth) { if (misc_registered != 0) @@ -322,16 +379,28 @@ static int softsynth_probe(struct spk_synth *synth) return -ENODEV; } + memset(&synthu_device, 0, sizeof(synthu_device)); + synthu_device.minor = SOFTSYNTHU_MINOR; + synthu_device.name = "softsynthu"; + synthu_device.fops = &softsynthu_fops; + if (misc_register(&synthu_device)) { + pr_warn("Couldn't initialize miscdevice /dev/softsynth.\n"); + return -ENODEV; + } + misc_registered = 1; pr_info("initialized device: /dev/softsynth, node (MAJOR 10, MINOR 26)\n"); + pr_info("initialized device: /dev/softsynthu, node (MAJOR 10, MINOR 27)\n"); return 0; } static void softsynth_release(void) { misc_deregister(&synth_device); + misc_deregister(&synthu_device); misc_registered = 0; pr_info("unregistered /dev/softsynth\n"); + pr_info("unregistered /dev/softsynthu\n"); } static int softsynth_is_alive(struct spk_synth *synth) |