179 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <linux/errno.h>3#include <linux/miscdevice.h> /* for misc_register, and MISC_DYNAMIC_MINOR */4#include <linux/types.h>5#include <linux/uaccess.h>6 7#include "speakup.h"8#include "spk_priv.h"9 10static int synth_registered, synthu_registered;11static int dev_opened;12 13/* Latin1 version */14static ssize_t speakup_file_write(struct file *fp, const char __user *buffer,15 size_t nbytes, loff_t *ppos)16{17 size_t count = nbytes;18 const char __user *ptr = buffer;19 size_t bytes;20 unsigned long flags;21 u_char buf[256];22 23 if (!synth)24 return -ENODEV;25 while (count > 0) {26 bytes = min(count, sizeof(buf));27 if (copy_from_user(buf, ptr, bytes))28 return -EFAULT;29 count -= bytes;30 ptr += bytes;31 spin_lock_irqsave(&speakup_info.spinlock, flags);32 synth_write(buf, bytes);33 spin_unlock_irqrestore(&speakup_info.spinlock, flags);34 }35 return (ssize_t)nbytes;36}37 38/* UTF-8 version */39static ssize_t speakup_file_writeu(struct file *fp, const char __user *buffer,40 size_t nbytes, loff_t *ppos)41{42 size_t count = nbytes, consumed, want;43 const char __user *ptr = buffer;44 size_t bytes;45 unsigned long flags;46 unsigned char buf[256];47 u16 ubuf[256];48 size_t in, out;49 50 if (!synth)51 return -ENODEV;52 53 want = 1;54 while (count >= want) {55 /* Copy some UTF-8 piece from userland */56 bytes = min(count, sizeof(buf));57 if (copy_from_user(buf, ptr, bytes))58 return -EFAULT;59 60 /* Convert to u16 */61 for (in = 0, out = 0; in < bytes; in += consumed) {62 s32 value;63 64 value = synth_utf8_get(buf + in, bytes - in, &consumed, &want);65 if (value == -1) {66 /* Invalid or incomplete */67 68 if (want > bytes - in)69 /* We don't have it all yet, stop here70 * and wait for the rest71 */72 bytes = in;73 74 continue;75 }76 77 if (value < 0x10000)78 ubuf[out++] = value;79 }80 81 count -= bytes;82 ptr += bytes;83 84 /* And speak this up */85 if (out) {86 spin_lock_irqsave(&speakup_info.spinlock, flags);87 for (in = 0; in < out; in++)88 synth_buffer_add(ubuf[in]);89 synth_start();90 spin_unlock_irqrestore(&speakup_info.spinlock, flags);91 }92 }93 94 return (ssize_t)(nbytes - count);95}96 97static ssize_t speakup_file_read(struct file *fp, char __user *buf,98 size_t nbytes, loff_t *ppos)99{100 return 0;101}102 103static int speakup_file_open(struct inode *ip, struct file *fp)104{105 if (!synth)106 return -ENODEV;107 if (xchg(&dev_opened, 1))108 return -EBUSY;109 return 0;110}111 112static int speakup_file_release(struct inode *ip, struct file *fp)113{114 dev_opened = 0;115 return 0;116}117 118static const struct file_operations synth_fops = {119 .read = speakup_file_read,120 .write = speakup_file_write,121 .open = speakup_file_open,122 .release = speakup_file_release,123};124 125static const struct file_operations synthu_fops = {126 .read = speakup_file_read,127 .write = speakup_file_writeu,128 .open = speakup_file_open,129 .release = speakup_file_release,130};131 132static struct miscdevice synth_device = {133 .minor = MISC_DYNAMIC_MINOR,134 .name = "synth",135 .fops = &synth_fops,136};137 138static struct miscdevice synthu_device = {139 .minor = MISC_DYNAMIC_MINOR,140 .name = "synthu",141 .fops = &synthu_fops,142};143 144void speakup_register_devsynth(void)145{146 if (!synth_registered) {147 if (misc_register(&synth_device)) {148 pr_warn("Couldn't initialize miscdevice /dev/synth.\n");149 } else {150 pr_info("initialized device: /dev/synth, node (MAJOR %d, MINOR %d)\n",151 MISC_MAJOR, synth_device.minor);152 synth_registered = 1;153 }154 }155 if (!synthu_registered) {156 if (misc_register(&synthu_device)) {157 pr_warn("Couldn't initialize miscdevice /dev/synthu.\n");158 } else {159 pr_info("initialized device: /dev/synthu, node (MAJOR %d, MINOR %d)\n",160 MISC_MAJOR, synthu_device.minor);161 synthu_registered = 1;162 }163 }164}165 166void speakup_unregister_devsynth(void)167{168 if (synth_registered) {169 pr_info("speakup: unregistering synth device /dev/synth\n");170 misc_deregister(&synth_device);171 synth_registered = 0;172 }173 if (synthu_registered) {174 pr_info("speakup: unregistering synth device /dev/synthu\n");175 misc_deregister(&synthu_device);176 synthu_registered = 0;177 }178}179