speakup_acntpc.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * written by: Kirk Reiser <kirk@braille.uwo.ca>
  4. * this version considerably modified by David Borowski, david575@rogers.com
  5. *
  6. * Copyright (C) 1998-99 Kirk Reiser.
  7. * Copyright (C) 2003 David Borowski.
  8. *
  9. * this code is specifically written as a driver for the speakup screenreview
  10. * package and is not a general device driver.
  11. * This driver is for the Aicom Acent PC internal synthesizer.
  12. */
  13. #include <linux/jiffies.h>
  14. #include <linux/sched.h>
  15. #include <linux/timer.h>
  16. #include <linux/kthread.h>
  17. #include "spk_priv.h"
  18. #include "serialio.h"
  19. #include "speakup.h"
  20. #include "speakup_acnt.h" /* local header file for Accent values */
  21. #define DRV_VERSION "2.10"
  22. #define PROCSPEECH '\r'
  23. static int synth_probe(struct spk_synth *synth);
  24. static void accent_release(struct spk_synth *synth);
  25. static const char *synth_immediate(struct spk_synth *synth, const char *buf);
  26. static void do_catch_up(struct spk_synth *synth);
  27. static void synth_flush(struct spk_synth *synth);
  28. static int synth_port_control;
  29. static int port_forced;
  30. static unsigned int synth_portlist[] = { 0x2a8, 0 };
  31. enum default_vars_id {
  32. CAPS_START_ID = 0, CAPS_STOP_ID,
  33. RATE_ID, PITCH_ID,
  34. VOL_ID, TONE_ID,
  35. DIRECT_ID, V_LAST_VAR_ID,
  36. NB_ID
  37. };
  38. static struct var_t vars[NB_ID] = {
  39. [CAPS_START_ID] = { CAPS_START, .u.s = {"\033P8" } },
  40. [CAPS_STOP_ID] = { CAPS_STOP, .u.s = {"\033P5" } },
  41. [RATE_ID] = { RATE, .u.n = {"\033R%c", 9, 0, 17, 0, 0, "0123456789abcdefgh" } },
  42. [PITCH_ID] = { PITCH, .u.n = {"\033P%d", 5, 0, 9, 0, 0, NULL } },
  43. [VOL_ID] = { VOL, .u.n = {"\033A%d", 5, 0, 9, 0, 0, NULL } },
  44. [TONE_ID] = { TONE, .u.n = {"\033V%d", 5, 0, 9, 0, 0, NULL } },
  45. [DIRECT_ID] = { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
  46. V_LAST_VAR
  47. };
  48. /*
  49. * These attributes will appear in /sys/accessibility/speakup/acntpc.
  50. */
  51. static struct kobj_attribute caps_start_attribute =
  52. __ATTR(caps_start, 0644, spk_var_show, spk_var_store);
  53. static struct kobj_attribute caps_stop_attribute =
  54. __ATTR(caps_stop, 0644, spk_var_show, spk_var_store);
  55. static struct kobj_attribute pitch_attribute =
  56. __ATTR(pitch, 0644, spk_var_show, spk_var_store);
  57. static struct kobj_attribute rate_attribute =
  58. __ATTR(rate, 0644, spk_var_show, spk_var_store);
  59. static struct kobj_attribute tone_attribute =
  60. __ATTR(tone, 0644, spk_var_show, spk_var_store);
  61. static struct kobj_attribute vol_attribute =
  62. __ATTR(vol, 0644, spk_var_show, spk_var_store);
  63. static struct kobj_attribute delay_time_attribute =
  64. __ATTR(delay_time, 0644, spk_var_show, spk_var_store);
  65. static struct kobj_attribute direct_attribute =
  66. __ATTR(direct, 0644, spk_var_show, spk_var_store);
  67. static struct kobj_attribute full_time_attribute =
  68. __ATTR(full_time, 0644, spk_var_show, spk_var_store);
  69. static struct kobj_attribute jiffy_delta_attribute =
  70. __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store);
  71. static struct kobj_attribute trigger_time_attribute =
  72. __ATTR(trigger_time, 0644, spk_var_show, spk_var_store);
  73. /*
  74. * Create a group of attributes so that we can create and destroy them all
  75. * at once.
  76. */
  77. static struct attribute *synth_attrs[] = {
  78. &caps_start_attribute.attr,
  79. &caps_stop_attribute.attr,
  80. &pitch_attribute.attr,
  81. &rate_attribute.attr,
  82. &tone_attribute.attr,
  83. &vol_attribute.attr,
  84. &delay_time_attribute.attr,
  85. &direct_attribute.attr,
  86. &full_time_attribute.attr,
  87. &jiffy_delta_attribute.attr,
  88. &trigger_time_attribute.attr,
  89. NULL, /* need to NULL terminate the list of attributes */
  90. };
  91. static struct spk_synth synth_acntpc = {
  92. .name = "acntpc",
  93. .version = DRV_VERSION,
  94. .long_name = "Accent PC",
  95. .init = "\033=X \033Oi\033T2\033=M\033N1\n",
  96. .procspeech = PROCSPEECH,
  97. .clear = SYNTH_CLEAR,
  98. .delay = 500,
  99. .trigger = 50,
  100. .jiffies = 50,
  101. .full = 1000,
  102. .startup = SYNTH_START,
  103. .checkval = SYNTH_CHECK,
  104. .vars = vars,
  105. .io_ops = &spk_serial_io_ops,
  106. .probe = synth_probe,
  107. .release = accent_release,
  108. .synth_immediate = synth_immediate,
  109. .catch_up = do_catch_up,
  110. .flush = synth_flush,
  111. .is_alive = spk_synth_is_alive_nop,
  112. .synth_adjust = NULL,
  113. .read_buff_add = NULL,
  114. .get_index = NULL,
  115. .indexing = {
  116. .command = NULL,
  117. .lowindex = 0,
  118. .highindex = 0,
  119. .currindex = 0,
  120. },
  121. .attributes = {
  122. .attrs = synth_attrs,
  123. .name = "acntpc",
  124. },
  125. };
  126. static inline bool synth_writable(void)
  127. {
  128. return inb_p(synth_port_control) & SYNTH_WRITABLE;
  129. }
  130. static inline bool synth_full(void)
  131. {
  132. return inb_p(speakup_info.port_tts + UART_RX) == 'F';
  133. }
  134. static const char *synth_immediate(struct spk_synth *synth, const char *buf)
  135. {
  136. u_char ch;
  137. while ((ch = *buf)) {
  138. int timeout = SPK_XMITR_TIMEOUT;
  139. if (ch == '\n')
  140. ch = PROCSPEECH;
  141. if (synth_full())
  142. return buf;
  143. while (synth_writable()) {
  144. if (!--timeout)
  145. return buf;
  146. udelay(1);
  147. }
  148. outb_p(ch, speakup_info.port_tts);
  149. buf++;
  150. }
  151. return NULL;
  152. }
  153. static void do_catch_up(struct spk_synth *synth)
  154. {
  155. u_char ch;
  156. unsigned long flags;
  157. unsigned long jiff_max;
  158. int timeout;
  159. int delay_time_val;
  160. int jiffy_delta_val;
  161. int full_time_val;
  162. struct var_t *delay_time;
  163. struct var_t *full_time;
  164. struct var_t *jiffy_delta;
  165. jiffy_delta = spk_get_var(JIFFY);
  166. delay_time = spk_get_var(DELAY);
  167. full_time = spk_get_var(FULL);
  168. spin_lock_irqsave(&speakup_info.spinlock, flags);
  169. jiffy_delta_val = jiffy_delta->u.n.value;
  170. spin_unlock_irqrestore(&speakup_info.spinlock, flags);
  171. jiff_max = jiffies + jiffy_delta_val;
  172. while (!kthread_should_stop()) {
  173. spin_lock_irqsave(&speakup_info.spinlock, flags);
  174. if (speakup_info.flushing) {
  175. speakup_info.flushing = 0;
  176. spin_unlock_irqrestore(&speakup_info.spinlock, flags);
  177. synth->flush(synth);
  178. continue;
  179. }
  180. synth_buffer_skip_nonlatin1();
  181. if (synth_buffer_empty()) {
  182. spin_unlock_irqrestore(&speakup_info.spinlock, flags);
  183. break;
  184. }
  185. set_current_state(TASK_INTERRUPTIBLE);
  186. full_time_val = full_time->u.n.value;
  187. spin_unlock_irqrestore(&speakup_info.spinlock, flags);
  188. if (synth_full()) {
  189. schedule_timeout(msecs_to_jiffies(full_time_val));
  190. continue;
  191. }
  192. set_current_state(TASK_RUNNING);
  193. timeout = SPK_XMITR_TIMEOUT;
  194. while (synth_writable()) {
  195. if (!--timeout)
  196. break;
  197. udelay(1);
  198. }
  199. spin_lock_irqsave(&speakup_info.spinlock, flags);
  200. ch = synth_buffer_getc();
  201. spin_unlock_irqrestore(&speakup_info.spinlock, flags);
  202. if (ch == '\n')
  203. ch = PROCSPEECH;
  204. outb_p(ch, speakup_info.port_tts);
  205. if (time_after_eq(jiffies, jiff_max) && ch == SPACE) {
  206. timeout = SPK_XMITR_TIMEOUT;
  207. while (synth_writable()) {
  208. if (!--timeout)
  209. break;
  210. udelay(1);
  211. }
  212. outb_p(PROCSPEECH, speakup_info.port_tts);
  213. spin_lock_irqsave(&speakup_info.spinlock, flags);
  214. jiffy_delta_val = jiffy_delta->u.n.value;
  215. delay_time_val = delay_time->u.n.value;
  216. spin_unlock_irqrestore(&speakup_info.spinlock, flags);
  217. schedule_timeout(msecs_to_jiffies(delay_time_val));
  218. jiff_max = jiffies + jiffy_delta_val;
  219. }
  220. }
  221. timeout = SPK_XMITR_TIMEOUT;
  222. while (synth_writable()) {
  223. if (!--timeout)
  224. break;
  225. udelay(1);
  226. }
  227. outb_p(PROCSPEECH, speakup_info.port_tts);
  228. }
  229. static void synth_flush(struct spk_synth *synth)
  230. {
  231. outb_p(SYNTH_CLEAR, speakup_info.port_tts);
  232. }
  233. static int synth_probe(struct spk_synth *synth)
  234. {
  235. unsigned int port_val = 0;
  236. int i;
  237. pr_info("Probing for %s.\n", synth->long_name);
  238. if (port_forced) {
  239. speakup_info.port_tts = port_forced;
  240. pr_info("probe forced to %x by kernel command line\n",
  241. speakup_info.port_tts);
  242. if (synth_request_region(speakup_info.port_tts - 1,
  243. SYNTH_IO_EXTENT)) {
  244. pr_warn("sorry, port already reserved\n");
  245. return -EBUSY;
  246. }
  247. port_val = inw(speakup_info.port_tts - 1);
  248. synth_port_control = speakup_info.port_tts - 1;
  249. } else {
  250. for (i = 0; synth_portlist[i]; i++) {
  251. if (synth_request_region(synth_portlist[i],
  252. SYNTH_IO_EXTENT)) {
  253. pr_warn
  254. ("request_region: failed with 0x%x, %d\n",
  255. synth_portlist[i], SYNTH_IO_EXTENT);
  256. continue;
  257. }
  258. port_val = inw(synth_portlist[i]) & 0xfffc;
  259. if (port_val == 0x53fc) {
  260. /* 'S' and out&input bits */
  261. synth_port_control = synth_portlist[i];
  262. speakup_info.port_tts = synth_port_control + 1;
  263. break;
  264. }
  265. }
  266. }
  267. port_val &= 0xfffc;
  268. if (port_val != 0x53fc) {
  269. /* 'S' and out&input bits */
  270. pr_info("%s: not found\n", synth->long_name);
  271. synth_release_region(synth_port_control, SYNTH_IO_EXTENT);
  272. synth_port_control = 0;
  273. return -ENODEV;
  274. }
  275. pr_info("%s: %03x-%03x, driver version %s,\n", synth->long_name,
  276. synth_port_control, synth_port_control + SYNTH_IO_EXTENT - 1,
  277. synth->version);
  278. synth->alive = 1;
  279. return 0;
  280. }
  281. static void accent_release(struct spk_synth *synth)
  282. {
  283. spk_stop_serial_interrupt();
  284. if (speakup_info.port_tts)
  285. synth_release_region(speakup_info.port_tts - 1,
  286. SYNTH_IO_EXTENT);
  287. speakup_info.port_tts = 0;
  288. }
  289. module_param_hw_named(port, port_forced, int, ioport, 0444);
  290. module_param_named(start, synth_acntpc.startup, short, 0444);
  291. module_param_named(rate, vars[RATE_ID].u.n.default_val, int, 0444);
  292. module_param_named(pitch, vars[PITCH_ID].u.n.default_val, int, 0444);
  293. module_param_named(vol, vars[VOL_ID].u.n.default_val, int, 0444);
  294. module_param_named(tone, vars[TONE_ID].u.n.default_val, int, 0444);
  295. module_param_named(direct, vars[DIRECT_ID].u.n.default_val, int, 0444);
  296. MODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing).");
  297. MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
  298. MODULE_PARM_DESC(rate, "Set the rate variable on load.");
  299. MODULE_PARM_DESC(pitch, "Set the pitch variable on load.");
  300. MODULE_PARM_DESC(vol, "Set the vol variable on load.");
  301. MODULE_PARM_DESC(tone, "Set the tone variable on load.");
  302. MODULE_PARM_DESC(direct, "Set the direct variable on load.");
  303. module_spk_synth(synth_acntpc);
  304. MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
  305. MODULE_AUTHOR("David Borowski");
  306. MODULE_DESCRIPTION("Speakup support for Accent PC synthesizer");
  307. MODULE_LICENSE("GPL");
  308. MODULE_VERSION(DRV_VERSION);