speakup: Add /dev/synthu device
/dev/synth has always been 8bit, but applications nowadays mostly expect to be using utf-8 encoding. This adds /dev/synthu to be able to synthesize non-latin1 characters. This however remains limited to 16bit unicode like the rest of speakup. Any odd input or input beyond 16bit is just discarded. Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org> =================================================================== Link: https://lore.kernel.org/r/20240204155825.ditstifsbqndnce3@begin Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
b6c8dafc9d
commit
807977260a
|
@ -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,97 @@ 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, want;
|
||||
const char __user *ptr = buffer;
|
||||
size_t bytes;
|
||||
unsigned long flags;
|
||||
unsigned char buf[256];
|
||||
u16 ubuf[256];
|
||||
size_t in, in2, 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++) {
|
||||
unsigned char c = buf[in];
|
||||
int nbytes = 8 - fls(c ^ 0xff);
|
||||
u32 value;
|
||||
|
||||
switch (nbytes) {
|
||||
case 8: /* 0xff */
|
||||
case 7: /* 0xfe */
|
||||
case 1: /* 0x80 */
|
||||
/* Invalid, drop */
|
||||
goto drop;
|
||||
|
||||
case 0:
|
||||
/* ASCII, copy */
|
||||
ubuf[out++] = c;
|
||||
continue;
|
||||
|
||||
default:
|
||||
/* 2..6-byte UTF-8 */
|
||||
|
||||
if (bytes - in < nbytes) {
|
||||
/* We don't have it all yet, stop here
|
||||
* and wait for the rest
|
||||
*/
|
||||
bytes = in;
|
||||
want = nbytes;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* First byte */
|
||||
value = c & ((1u << (7 - nbytes)) - 1);
|
||||
|
||||
/* Other bytes */
|
||||
for (in2 = 2; in2 <= nbytes; in2++) {
|
||||
c = buf[in + 1];
|
||||
if ((c & 0xc0) != 0x80) {
|
||||
/* Invalid, drop the head */
|
||||
want = 1;
|
||||
goto drop;
|
||||
}
|
||||
value = (value << 6) | (c & 0x3f);
|
||||
in++;
|
||||
}
|
||||
|
||||
if (value < 0x10000)
|
||||
ubuf[out++] = value;
|
||||
want = 1;
|
||||
break;
|
||||
}
|
||||
drop:
|
||||
}
|
||||
|
||||
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 +154,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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue