162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/errno.h>
362306a36Sopenharmony_ci#include <linux/miscdevice.h>	/* for misc_register, and MISC_DYNAMIC_MINOR */
462306a36Sopenharmony_ci#include <linux/types.h>
562306a36Sopenharmony_ci#include <linux/uaccess.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "speakup.h"
862306a36Sopenharmony_ci#include "spk_priv.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_cistatic int misc_registered;
1162306a36Sopenharmony_cistatic int dev_opened;
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic ssize_t speakup_file_write(struct file *fp, const char __user *buffer,
1462306a36Sopenharmony_ci				  size_t nbytes, loff_t *ppos)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	size_t count = nbytes;
1762306a36Sopenharmony_ci	const char __user *ptr = buffer;
1862306a36Sopenharmony_ci	size_t bytes;
1962306a36Sopenharmony_ci	unsigned long flags;
2062306a36Sopenharmony_ci	u_char buf[256];
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	if (!synth)
2362306a36Sopenharmony_ci		return -ENODEV;
2462306a36Sopenharmony_ci	while (count > 0) {
2562306a36Sopenharmony_ci		bytes = min(count, sizeof(buf));
2662306a36Sopenharmony_ci		if (copy_from_user(buf, ptr, bytes))
2762306a36Sopenharmony_ci			return -EFAULT;
2862306a36Sopenharmony_ci		count -= bytes;
2962306a36Sopenharmony_ci		ptr += bytes;
3062306a36Sopenharmony_ci		spin_lock_irqsave(&speakup_info.spinlock, flags);
3162306a36Sopenharmony_ci		synth_write(buf, bytes);
3262306a36Sopenharmony_ci		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
3362306a36Sopenharmony_ci	}
3462306a36Sopenharmony_ci	return (ssize_t)nbytes;
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic ssize_t speakup_file_read(struct file *fp, char __user *buf,
3862306a36Sopenharmony_ci				 size_t nbytes, loff_t *ppos)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	return 0;
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic int speakup_file_open(struct inode *ip, struct file *fp)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	if (!synth)
4662306a36Sopenharmony_ci		return -ENODEV;
4762306a36Sopenharmony_ci	if (xchg(&dev_opened, 1))
4862306a36Sopenharmony_ci		return -EBUSY;
4962306a36Sopenharmony_ci	return 0;
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic int speakup_file_release(struct inode *ip, struct file *fp)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	dev_opened = 0;
5562306a36Sopenharmony_ci	return 0;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic const struct file_operations synth_fops = {
5962306a36Sopenharmony_ci	.read = speakup_file_read,
6062306a36Sopenharmony_ci	.write = speakup_file_write,
6162306a36Sopenharmony_ci	.open = speakup_file_open,
6262306a36Sopenharmony_ci	.release = speakup_file_release,
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic struct miscdevice synth_device = {
6662306a36Sopenharmony_ci	.minor = MISC_DYNAMIC_MINOR,
6762306a36Sopenharmony_ci	.name = "synth",
6862306a36Sopenharmony_ci	.fops = &synth_fops,
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_civoid speakup_register_devsynth(void)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	if (misc_registered != 0)
7462306a36Sopenharmony_ci		return;
7562306a36Sopenharmony_ci/* zero it so if register fails, deregister will not ref invalid ptrs */
7662306a36Sopenharmony_ci	if (misc_register(&synth_device)) {
7762306a36Sopenharmony_ci		pr_warn("Couldn't initialize miscdevice /dev/synth.\n");
7862306a36Sopenharmony_ci	} else {
7962306a36Sopenharmony_ci		pr_info("initialized device: /dev/synth, node (MAJOR %d, MINOR %d)\n",
8062306a36Sopenharmony_ci			MISC_MAJOR, synth_device.minor);
8162306a36Sopenharmony_ci		misc_registered = 1;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_civoid speakup_unregister_devsynth(void)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	if (!misc_registered)
8862306a36Sopenharmony_ci		return;
8962306a36Sopenharmony_ci	pr_info("speakup: unregistering synth device /dev/synth\n");
9062306a36Sopenharmony_ci	misc_deregister(&synth_device);
9162306a36Sopenharmony_ci	misc_registered = 0;
9262306a36Sopenharmony_ci}
93