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