devsynth.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  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. #include "speakup.h"
  7. #include "spk_priv.h"
  8. static int synth_registered, synthu_registered;
  9. static int dev_opened;
  10. /* Latin1 version */
  11. static ssize_t speakup_file_write(struct file *fp, const char __user *buffer,
  12. size_t nbytes, loff_t *ppos)
  13. {
  14. size_t count = nbytes;
  15. const char __user *ptr = buffer;
  16. size_t bytes;
  17. unsigned long flags;
  18. u_char buf[256];
  19. if (!synth)
  20. return -ENODEV;
  21. while (count > 0) {
  22. bytes = min(count, sizeof(buf));
  23. if (copy_from_user(buf, ptr, bytes))
  24. return -EFAULT;
  25. count -= bytes;
  26. ptr += bytes;
  27. spin_lock_irqsave(&speakup_info.spinlock, flags);
  28. synth_write(buf, bytes);
  29. spin_unlock_irqrestore(&speakup_info.spinlock, flags);
  30. }
  31. return (ssize_t)nbytes;
  32. }
  33. /* UTF-8 version */
  34. static ssize_t speakup_file_writeu(struct file *fp, const char __user *buffer,
  35. size_t nbytes, loff_t *ppos)
  36. {
  37. size_t count = nbytes, consumed, want;
  38. const char __user *ptr = buffer;
  39. size_t bytes;
  40. unsigned long flags;
  41. unsigned char buf[256];
  42. u16 ubuf[256];
  43. size_t in, out;
  44. if (!synth)
  45. return -ENODEV;
  46. want = 1;
  47. while (count >= want) {
  48. /* Copy some UTF-8 piece from userland */
  49. bytes = min(count, sizeof(buf));
  50. if (copy_from_user(buf, ptr, bytes))
  51. return -EFAULT;
  52. /* Convert to u16 */
  53. for (in = 0, out = 0; in < bytes; in += consumed) {
  54. s32 value;
  55. value = synth_utf8_get(buf + in, bytes - in, &consumed, &want);
  56. if (value == -1) {
  57. /* Invalid or incomplete */
  58. if (want > bytes - in)
  59. /* We don't have it all yet, stop here
  60. * and wait for the rest
  61. */
  62. bytes = in;
  63. continue;
  64. }
  65. if (value < 0x10000)
  66. ubuf[out++] = value;
  67. }
  68. count -= bytes;
  69. ptr += bytes;
  70. /* And speak this up */
  71. if (out) {
  72. spin_lock_irqsave(&speakup_info.spinlock, flags);
  73. for (in = 0; in < out; in++)
  74. synth_buffer_add(ubuf[in]);
  75. synth_start();
  76. spin_unlock_irqrestore(&speakup_info.spinlock, flags);
  77. }
  78. }
  79. return (ssize_t)(nbytes - count);
  80. }
  81. static ssize_t speakup_file_read(struct file *fp, char __user *buf,
  82. size_t nbytes, loff_t *ppos)
  83. {
  84. return 0;
  85. }
  86. static int speakup_file_open(struct inode *ip, struct file *fp)
  87. {
  88. if (!synth)
  89. return -ENODEV;
  90. if (xchg(&dev_opened, 1))
  91. return -EBUSY;
  92. return 0;
  93. }
  94. static int speakup_file_release(struct inode *ip, struct file *fp)
  95. {
  96. dev_opened = 0;
  97. return 0;
  98. }
  99. static const struct file_operations synth_fops = {
  100. .read = speakup_file_read,
  101. .write = speakup_file_write,
  102. .open = speakup_file_open,
  103. .release = speakup_file_release,
  104. };
  105. static const struct file_operations synthu_fops = {
  106. .read = speakup_file_read,
  107. .write = speakup_file_writeu,
  108. .open = speakup_file_open,
  109. .release = speakup_file_release,
  110. };
  111. static struct miscdevice synth_device = {
  112. .minor = MISC_DYNAMIC_MINOR,
  113. .name = "synth",
  114. .fops = &synth_fops,
  115. };
  116. static struct miscdevice synthu_device = {
  117. .minor = MISC_DYNAMIC_MINOR,
  118. .name = "synthu",
  119. .fops = &synthu_fops,
  120. };
  121. void speakup_register_devsynth(void)
  122. {
  123. if (!synth_registered) {
  124. if (misc_register(&synth_device)) {
  125. pr_warn("Couldn't initialize miscdevice /dev/synth.\n");
  126. } else {
  127. pr_info("initialized device: /dev/synth, node (MAJOR %d, MINOR %d)\n",
  128. MISC_MAJOR, synth_device.minor);
  129. synth_registered = 1;
  130. }
  131. }
  132. if (!synthu_registered) {
  133. if (misc_register(&synthu_device)) {
  134. pr_warn("Couldn't initialize miscdevice /dev/synthu.\n");
  135. } else {
  136. pr_info("initialized device: /dev/synthu, node (MAJOR %d, MINOR %d)\n",
  137. MISC_MAJOR, synthu_device.minor);
  138. synthu_registered = 1;
  139. }
  140. }
  141. }
  142. void speakup_unregister_devsynth(void)
  143. {
  144. if (synth_registered) {
  145. pr_info("speakup: unregistering synth device /dev/synth\n");
  146. misc_deregister(&synth_device);
  147. synth_registered = 0;
  148. }
  149. if (synthu_registered) {
  150. pr_info("speakup: unregistering synth device /dev/synthu\n");
  151. misc_deregister(&synthu_device);
  152. synthu_registered = 0;
  153. }
  154. }