18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/errno.h>
38c2ecf20Sopenharmony_ci#include <linux/miscdevice.h>	/* for misc_register, and MISC_DYNAMIC_MINOR */
48c2ecf20Sopenharmony_ci#include <linux/types.h>
58c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include "speakup.h"
88c2ecf20Sopenharmony_ci#include "spk_priv.h"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_cistatic int misc_registered;
118c2ecf20Sopenharmony_cistatic int dev_opened;
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistatic ssize_t speakup_file_write(struct file *fp, const char __user *buffer,
148c2ecf20Sopenharmony_ci				  size_t nbytes, loff_t *ppos)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	size_t count = nbytes;
178c2ecf20Sopenharmony_ci	const char __user *ptr = buffer;
188c2ecf20Sopenharmony_ci	size_t bytes;
198c2ecf20Sopenharmony_ci	unsigned long flags;
208c2ecf20Sopenharmony_ci	u_char buf[256];
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	if (!synth)
238c2ecf20Sopenharmony_ci		return -ENODEV;
248c2ecf20Sopenharmony_ci	while (count > 0) {
258c2ecf20Sopenharmony_ci		bytes = min(count, sizeof(buf));
268c2ecf20Sopenharmony_ci		if (copy_from_user(buf, ptr, bytes))
278c2ecf20Sopenharmony_ci			return -EFAULT;
288c2ecf20Sopenharmony_ci		count -= bytes;
298c2ecf20Sopenharmony_ci		ptr += bytes;
308c2ecf20Sopenharmony_ci		spin_lock_irqsave(&speakup_info.spinlock, flags);
318c2ecf20Sopenharmony_ci		synth_write(buf, bytes);
328c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
338c2ecf20Sopenharmony_ci	}
348c2ecf20Sopenharmony_ci	return (ssize_t)nbytes;
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic ssize_t speakup_file_read(struct file *fp, char __user *buf,
388c2ecf20Sopenharmony_ci				 size_t nbytes, loff_t *ppos)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	return 0;
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic int speakup_file_open(struct inode *ip, struct file *fp)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	if (!synth)
468c2ecf20Sopenharmony_ci		return -ENODEV;
478c2ecf20Sopenharmony_ci	if (xchg(&dev_opened, 1))
488c2ecf20Sopenharmony_ci		return -EBUSY;
498c2ecf20Sopenharmony_ci	return 0;
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic int speakup_file_release(struct inode *ip, struct file *fp)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	dev_opened = 0;
558c2ecf20Sopenharmony_ci	return 0;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic const struct file_operations synth_fops = {
598c2ecf20Sopenharmony_ci	.read = speakup_file_read,
608c2ecf20Sopenharmony_ci	.write = speakup_file_write,
618c2ecf20Sopenharmony_ci	.open = speakup_file_open,
628c2ecf20Sopenharmony_ci	.release = speakup_file_release,
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic struct miscdevice synth_device = {
668c2ecf20Sopenharmony_ci	.minor = MISC_DYNAMIC_MINOR,
678c2ecf20Sopenharmony_ci	.name = "synth",
688c2ecf20Sopenharmony_ci	.fops = &synth_fops,
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_civoid speakup_register_devsynth(void)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	if (misc_registered != 0)
748c2ecf20Sopenharmony_ci		return;
758c2ecf20Sopenharmony_ci/* zero it so if register fails, deregister will not ref invalid ptrs */
768c2ecf20Sopenharmony_ci	if (misc_register(&synth_device)) {
778c2ecf20Sopenharmony_ci		pr_warn("Couldn't initialize miscdevice /dev/synth.\n");
788c2ecf20Sopenharmony_ci	} else {
798c2ecf20Sopenharmony_ci		pr_info("initialized device: /dev/synth, node (MAJOR %d, MINOR %d)\n",
808c2ecf20Sopenharmony_ci			MISC_MAJOR, synth_device.minor);
818c2ecf20Sopenharmony_ci		misc_registered = 1;
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_civoid speakup_unregister_devsynth(void)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	if (!misc_registered)
888c2ecf20Sopenharmony_ci		return;
898c2ecf20Sopenharmony_ci	pr_info("speakup: unregistering synth device /dev/synth\n");
908c2ecf20Sopenharmony_ci	misc_deregister(&synth_device);
918c2ecf20Sopenharmony_ci	misc_registered = 0;
928c2ecf20Sopenharmony_ci}
93