File Index
Files and directories
Sign in
FILE

devsynth.c

File preview with syntax highlighting.

linux-6.19.14/drivers/accessibility/speakup/devsynth.c 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
 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}
~/ Read-only file preview.