Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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, 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, in2, 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++) {
 62			unsigned char c = buf[in];
 63			int nbytes = 8 - fls(c ^ 0xff);
 64			u32 value;
 65
 66			switch (nbytes) {
 67			case 8: /* 0xff */
 68			case 7: /* 0xfe */
 69			case 1: /* 0x80 */
 70				/* Invalid, drop */
 71				goto drop;
 72
 73			case 0:
 74				/* ASCII, copy */
 75				ubuf[out++] = c;
 76				continue;
 77
 78			default:
 79				/* 2..6-byte UTF-8 */
 80
 81				if (bytes - in < nbytes) {
 82					/* We don't have it all yet, stop here
 83					 * and wait for the rest
 84					 */
 85					bytes = in;
 86					want = nbytes;
 87					continue;
 88				}
 89
 90				/* First byte */
 91				value = c & ((1u << (7 - nbytes)) - 1);
 92
 93				/* Other bytes */
 94				for (in2 = 2; in2 <= nbytes; in2++) {
 95					c = buf[in + 1];
 96					if ((c & 0xc0) != 0x80)	{
 97						/* Invalid, drop the head */
 98						want = 1;
 99						goto drop;
100					}
101					value = (value << 6) | (c & 0x3f);
102					in++;
103				}
104
105				if (value < 0x10000)
106					ubuf[out++] = value;
107				want = 1;
108				break;
109			}
110drop:
111			/* empty statement */;
112		}
113
114		count -= bytes;
115		ptr += bytes;
116
117		/* And speak this up */
118		if (out) {
119			spin_lock_irqsave(&speakup_info.spinlock, flags);
120			for (in = 0; in < out; in++)
121				synth_buffer_add(ubuf[in]);
122			synth_start();
123			spin_unlock_irqrestore(&speakup_info.spinlock, flags);
124		}
125	}
126
127	return (ssize_t)(nbytes - count);
128}
129
130static ssize_t speakup_file_read(struct file *fp, char __user *buf,
131				 size_t nbytes, loff_t *ppos)
132{
133	return 0;
134}
135
136static int speakup_file_open(struct inode *ip, struct file *fp)
137{
138	if (!synth)
139		return -ENODEV;
140	if (xchg(&dev_opened, 1))
141		return -EBUSY;
142	return 0;
143}
144
145static int speakup_file_release(struct inode *ip, struct file *fp)
146{
147	dev_opened = 0;
148	return 0;
149}
150
151static const struct file_operations synth_fops = {
152	.read = speakup_file_read,
153	.write = speakup_file_write,
154	.open = speakup_file_open,
155	.release = speakup_file_release,
156};
157
158static const struct file_operations synthu_fops = {
159	.read = speakup_file_read,
160	.write = speakup_file_writeu,
161	.open = speakup_file_open,
162	.release = speakup_file_release,
163};
164
165static struct miscdevice synth_device = {
166	.minor = MISC_DYNAMIC_MINOR,
167	.name = "synth",
168	.fops = &synth_fops,
169};
170
171static struct miscdevice synthu_device = {
172	.minor = MISC_DYNAMIC_MINOR,
173	.name = "synthu",
174	.fops = &synthu_fops,
175};
176
177void speakup_register_devsynth(void)
178{
179	if (!synth_registered) {
180		if (misc_register(&synth_device)) {
181			pr_warn("Couldn't initialize miscdevice /dev/synth.\n");
182		} else {
183			pr_info("initialized device: /dev/synth, node (MAJOR %d, MINOR %d)\n",
184				MISC_MAJOR, synth_device.minor);
185			synth_registered = 1;
186		}
187	}
188	if (!synthu_registered) {
189		if (misc_register(&synthu_device)) {
190			pr_warn("Couldn't initialize miscdevice /dev/synthu.\n");
191		} else {
192			pr_info("initialized device: /dev/synthu, node (MAJOR %d, MINOR %d)\n",
193				MISC_MAJOR, synthu_device.minor);
194			synthu_registered = 1;
195		}
196	}
197}
198
199void speakup_unregister_devsynth(void)
200{
201	if (synth_registered) {
202		pr_info("speakup: unregistering synth device /dev/synth\n");
203		misc_deregister(&synth_device);
204		synth_registered = 0;
205	}
206	if (synthu_registered) {
207		pr_info("speakup: unregistering synth device /dev/synthu\n");
208		misc_deregister(&synthu_device);
209		synthu_registered = 0;
210	}
211}