1 // SPDX-License-Identifier: GPL-2.0
2 #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>
10 static int synth_registered, synthu_registered;
11 static int dev_opened;
14 static ssize_t speakup_file_write(struct file *fp, const char __user *buffer,
15 size_t nbytes, loff_t *ppos)
17 size_t count = nbytes;
18 const char __user *ptr = buffer;
26 bytes = min(count, sizeof(buf));
27 if (copy_from_user(buf, ptr, bytes))
31 spin_lock_irqsave(&speakup_info.spinlock, flags);
32 synth_write(buf, bytes);
33 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
35 return (ssize_t)nbytes;
39 static ssize_t speakup_file_writeu(struct file *fp, const char __user *buffer,
40 size_t nbytes, loff_t *ppos)
42 size_t count = nbytes, consumed, want;
43 const char __user *ptr = buffer;
46 unsigned char buf[256];
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))
61 for (in = 0, out = 0; in < bytes; in += consumed) {
64 value = synth_utf8_get(buf + in, bytes - in, &consumed, &want);
66 /* Invalid or incomplete */
68 if (want > bytes - in)
69 /* We don't have it all yet, stop here
70 * and wait for the rest
84 /* And speak this up */
86 spin_lock_irqsave(&speakup_info.spinlock, flags);
87 for (in = 0; in < out; in++)
88 synth_buffer_add(ubuf[in]);
90 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
94 return (ssize_t)(nbytes - count);
97 static ssize_t speakup_file_read(struct file *fp, char __user *buf,
98 size_t nbytes, loff_t *ppos)
103 static int speakup_file_open(struct inode *ip, struct file *fp)
107 if (xchg(&dev_opened, 1))
112 static int speakup_file_release(struct inode *ip, struct file *fp)
118 static 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,
125 static 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,
132 static struct miscdevice synth_device = {
133 .minor = MISC_DYNAMIC_MINOR,
138 static struct miscdevice synthu_device = {
139 .minor = MISC_DYNAMIC_MINOR,
141 .fops = &synthu_fops,
144 void speakup_register_devsynth(void)
146 if (!synth_registered) {
147 if (misc_register(&synth_device)) {
148 pr_warn("Couldn't initialize miscdevice /dev/synth.\n");
150 pr_info("initialized device: /dev/synth, node (MAJOR %d, MINOR %d)\n",
151 MISC_MAJOR, synth_device.minor);
152 synth_registered = 1;
155 if (!synthu_registered) {
156 if (misc_register(&synthu_device)) {
157 pr_warn("Couldn't initialize miscdevice /dev/synthu.\n");
159 pr_info("initialized device: /dev/synthu, node (MAJOR %d, MINOR %d)\n",
160 MISC_MAJOR, synthu_device.minor);
161 synthu_registered = 1;
166 void speakup_unregister_devsynth(void)
168 if (synth_registered) {
169 pr_info("speakup: unregistering synth device /dev/synth\n");
170 misc_deregister(&synth_device);
171 synth_registered = 0;
173 if (synthu_registered) {
174 pr_info("speakup: unregistering synth device /dev/synthu\n");
175 misc_deregister(&synthu_device);
176 synthu_registered = 0;