Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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>
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 here
70 * and wait for the rest
71 */
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}