]> Git Repo - J-linux.git/blob - drivers/accessibility/speakup/devsynth.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / accessibility / speakup / devsynth.c
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
10 static int synth_registered, synthu_registered;
11 static int dev_opened;
12
13 /* Latin1 version */
14 static 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 */
39 static 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
97 static ssize_t speakup_file_read(struct file *fp, char __user *buf,
98                                  size_t nbytes, loff_t *ppos)
99 {
100         return 0;
101 }
102
103 static 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
112 static int speakup_file_release(struct inode *ip, struct file *fp)
113 {
114         dev_opened = 0;
115         return 0;
116 }
117
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,
123 };
124
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,
130 };
131
132 static struct miscdevice synth_device = {
133         .minor = MISC_DYNAMIC_MINOR,
134         .name = "synth",
135         .fops = &synth_fops,
136 };
137
138 static struct miscdevice synthu_device = {
139         .minor = MISC_DYNAMIC_MINOR,
140         .name = "synthu",
141         .fops = &synthu_fops,
142 };
143
144 void 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
166 void 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 }
This page took 0.037038 seconds and 4 git commands to generate.