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;