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 misc_registered;
11 static int dev_opened;
12 
13 static ssize_t speakup_file_write(struct file *fp, const char __user *buffer,
14 				  size_t nbytes, loff_t *ppos)
15 {
16 	size_t count = nbytes;
17 	const char __user *ptr = buffer;
18 	size_t bytes;
19 	unsigned long flags;
20 	u_char buf[256];
21 
22 	if (!synth)
23 		return -ENODEV;
24 	while (count > 0) {
25 		bytes = min(count, sizeof(buf));
26 		if (copy_from_user(buf, ptr, bytes))
27 			return -EFAULT;
28 		count -= bytes;
29 		ptr += bytes;
30 		spin_lock_irqsave(&speakup_info.spinlock, flags);
31 		synth_write(buf, bytes);
32 		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
33 	}
34 	return (ssize_t)nbytes;
35 }
36 
37 static ssize_t speakup_file_read(struct file *fp, char __user *buf,
38 				 size_t nbytes, loff_t *ppos)
39 {
40 	return 0;
41 }
42 
43 static int speakup_file_open(struct inode *ip, struct file *fp)
44 {
45 	if (!synth)
46 		return -ENODEV;
47 	if (xchg(&dev_opened, 1))
48 		return -EBUSY;
49 	return 0;
50 }
51 
52 static int speakup_file_release(struct inode *ip, struct file *fp)
53 {
54 	dev_opened = 0;
55 	return 0;
56 }
57 
58 static const struct file_operations synth_fops = {
59 	.read = speakup_file_read,
60 	.write = speakup_file_write,
61 	.open = speakup_file_open,
62 	.release = speakup_file_release,
63 };
64 
65 static struct miscdevice synth_device = {
66 	.minor = MISC_DYNAMIC_MINOR,
67 	.name = "synth",
68 	.fops = &synth_fops,
69 };
70 
71 void speakup_register_devsynth(void)
72 {
73 	if (misc_registered != 0)
74 		return;
75 /* zero it so if register fails, deregister will not ref invalid ptrs */
76 	if (misc_register(&synth_device)) {
77 		pr_warn("Couldn't initialize miscdevice /dev/synth.\n");
78 	} else {
79 		pr_info("initialized device: /dev/synth, node (MAJOR %d, MINOR %d)\n",
80 			MISC_MAJOR, synth_device.minor);
81 		misc_registered = 1;
82 	}
83 }
84 
85 void speakup_unregister_devsynth(void)
86 {
87 	if (!misc_registered)
88 		return;
89 	pr_info("speakup: unregistering synth device /dev/synth\n");
90 	misc_deregister(&synth_device);
91 	misc_registered = 0;
92 }
93