Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Samuel Thibault and committed by
Greg Kroah-Hartman
80797726 b6c8dafc

+133 -15
+133 -15
drivers/accessibility/speakup/devsynth.c
··· 7 7 #include "speakup.h" 8 8 #include "spk_priv.h" 9 9 10 - static int misc_registered; 10 + static int synth_registered, synthu_registered; 11 11 static int dev_opened; 12 12 13 + /* Latin1 version */ 13 14 static ssize_t speakup_file_write(struct file *fp, const char __user *buffer, 14 15 size_t nbytes, loff_t *ppos) 15 16 { ··· 33 32 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 34 33 } 35 34 return (ssize_t)nbytes; 35 + } 36 + 37 + /* UTF-8 version */ 38 + static ssize_t speakup_file_writeu(struct file *fp, const char __user *buffer, 39 + size_t nbytes, loff_t *ppos) 40 + { 41 + size_t count = nbytes, want; 42 + const char __user *ptr = buffer; 43 + size_t bytes; 44 + unsigned long flags; 45 + unsigned char buf[256]; 46 + u16 ubuf[256]; 47 + size_t in, in2, out; 48 + 49 + if (!synth) 50 + return -ENODEV; 51 + 52 + want = 1; 53 + while (count >= want) { 54 + /* Copy some UTF-8 piece from userland */ 55 + bytes = min(count, sizeof(buf)); 56 + if (copy_from_user(buf, ptr, bytes)) 57 + return -EFAULT; 58 + 59 + /* Convert to u16 */ 60 + for (in = 0, out = 0; in < bytes; in++) { 61 + unsigned char c = buf[in]; 62 + int nbytes = 8 - fls(c ^ 0xff); 63 + u32 value; 64 + 65 + switch (nbytes) { 66 + case 8: /* 0xff */ 67 + case 7: /* 0xfe */ 68 + case 1: /* 0x80 */ 69 + /* Invalid, drop */ 70 + goto drop; 71 + 72 + case 0: 73 + /* ASCII, copy */ 74 + ubuf[out++] = c; 75 + continue; 76 + 77 + default: 78 + /* 2..6-byte UTF-8 */ 79 + 80 + if (bytes - in < nbytes) { 81 + /* We don't have it all yet, stop here 82 + * and wait for the rest 83 + */ 84 + bytes = in; 85 + want = nbytes; 86 + continue; 87 + } 88 + 89 + /* First byte */ 90 + value = c & ((1u << (7 - nbytes)) - 1); 91 + 92 + /* Other bytes */ 93 + for (in2 = 2; in2 <= nbytes; in2++) { 94 + c = buf[in + 1]; 95 + if ((c & 0xc0) != 0x80) { 96 + /* Invalid, drop the head */ 97 + want = 1; 98 + goto drop; 99 + } 100 + value = (value << 6) | (c & 0x3f); 101 + in++; 102 + } 103 + 104 + if (value < 0x10000) 105 + ubuf[out++] = value; 106 + want = 1; 107 + break; 108 + } 109 + drop: 110 + } 111 + 112 + count -= bytes; 113 + ptr += bytes; 114 + 115 + /* And speak this up */ 116 + if (out) { 117 + spin_lock_irqsave(&speakup_info.spinlock, flags); 118 + for (in = 0; in < out; in++) 119 + synth_buffer_add(ubuf[in]); 120 + synth_start(); 121 + spin_unlock_irqrestore(&speakup_info.spinlock, flags); 122 + } 123 + } 124 + 125 + return (ssize_t)(nbytes - count); 36 126 } 37 127 38 128 static ssize_t speakup_file_read(struct file *fp, char __user *buf, ··· 154 62 .release = speakup_file_release, 155 63 }; 156 64 65 + static const struct file_operations synthu_fops = { 66 + .read = speakup_file_read, 67 + .write = speakup_file_writeu, 68 + .open = speakup_file_open, 69 + .release = speakup_file_release, 70 + }; 71 + 157 72 static struct miscdevice synth_device = { 158 73 .minor = MISC_DYNAMIC_MINOR, 159 74 .name = "synth", 160 75 .fops = &synth_fops, 161 76 }; 162 77 78 + static struct miscdevice synthu_device = { 79 + .minor = MISC_DYNAMIC_MINOR, 80 + .name = "synthu", 81 + .fops = &synthu_fops, 82 + }; 83 + 163 84 void speakup_register_devsynth(void) 164 85 { 165 - if (misc_registered != 0) 166 - return; 167 - /* zero it so if register fails, deregister will not ref invalid ptrs */ 168 - if (misc_register(&synth_device)) { 169 - pr_warn("Couldn't initialize miscdevice /dev/synth.\n"); 170 - } else { 171 - pr_info("initialized device: /dev/synth, node (MAJOR %d, MINOR %d)\n", 172 - MISC_MAJOR, synth_device.minor); 173 - misc_registered = 1; 86 + if (!synth_registered) { 87 + if (misc_register(&synth_device)) { 88 + pr_warn("Couldn't initialize miscdevice /dev/synth.\n"); 89 + } else { 90 + pr_info("initialized device: /dev/synth, node (MAJOR %d, MINOR %d)\n", 91 + MISC_MAJOR, synth_device.minor); 92 + synth_registered = 1; 93 + } 94 + } 95 + if (!synthu_registered) { 96 + if (misc_register(&synthu_device)) { 97 + pr_warn("Couldn't initialize miscdevice /dev/synthu.\n"); 98 + } else { 99 + pr_info("initialized device: /dev/synthu, node (MAJOR %d, MINOR %d)\n", 100 + MISC_MAJOR, synthu_device.minor); 101 + synthu_registered = 1; 102 + } 174 103 } 175 104 } 176 105 177 106 void speakup_unregister_devsynth(void) 178 107 { 179 - if (!misc_registered) 180 - return; 181 - pr_info("speakup: unregistering synth device /dev/synth\n"); 182 - misc_deregister(&synth_device); 183 - misc_registered = 0; 108 + if (synth_registered) { 109 + pr_info("speakup: unregistering synth device /dev/synth\n"); 110 + misc_deregister(&synth_device); 111 + synth_registered = 0; 112 + } 113 + if (synthu_registered) { 114 + pr_info("speakup: unregistering synth device /dev/synthu\n"); 115 + misc_deregister(&synthu_device); 116 + synthu_registered = 0; 117 + } 184 118 }