xref: /linux/drivers/char/dtlk.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*                                              -*- linux-c -*-
3  * dtlk.c - DoubleTalk PC driver for Linux
4  *
5  * Original author: Chris Pallotta <chris@allmedia.com>
6  * Current maintainer: Jim Van Zandt <jrv@vanzandt.mv.com>
7  *
8  * 2000-03-18 Jim Van Zandt: Fix polling.
9  *  Eliminate dtlk_timer_active flag and separate dtlk_stop_timer
10  *  function.  Don't restart timer in dtlk_timer_tick.  Restart timer
11  *  in dtlk_poll after every poll.  dtlk_poll returns mask (duh).
12  *  Eliminate unused function dtlk_write_byte.  Misc. code cleanups.
13  */
14 
15 /* This driver is for the DoubleTalk PC, a speech synthesizer
16    manufactured by RC Systems (http://www.rcsys.com/).  It was written
17    based on documentation in their User's Manual file and Developer's
18    Tools disk.
19 
20    The DoubleTalk PC contains four voice synthesizers: text-to-speech
21    (TTS), linear predictive coding (LPC), PCM/ADPCM, and CVSD.  It
22    also has a tone generator.  Output data for LPC are written to the
23    LPC port, and output data for the other modes are written to the
24    TTS port.
25 
26    Two kinds of data can be read from the DoubleTalk: status
27    information (in response to the "\001?" interrogation command) is
28    read from the TTS port, and index markers (which mark the progress
29    of the speech) are read from the LPC port.  Not all models of the
30    DoubleTalk PC implement index markers.  Both the TTS and LPC ports
31    can also display status flags.
32 
33    The DoubleTalk PC generates no interrupts.
34 
35    These characteristics are mapped into the Unix stream I/O model as
36    follows:
37 
38    "write" sends bytes to the TTS port.  It is the responsibility of
39    the user program to switch modes among TTS, PCM/ADPCM, and CVSD.
40    This driver was written for use with the text-to-speech
41    synthesizer.  If LPC output is needed some day, other minor device
42    numbers can be used to select among output modes.
43 
44    "read" gets index markers from the LPC port.  If the device does
45    not implement index markers, the read will fail with error EINVAL.
46 
47    Status information is available using the DTLK_INTERROGATE ioctl.
48 
49  */
50 
51 #include <linux/module.h>
52 
53 #define KERNEL
54 #include <linux/types.h>
55 #include <linux/fs.h>
56 #include <linux/mm.h>
57 #include <linux/errno.h>	/* for -EBUSY */
58 #include <linux/ioport.h>	/* for request_region */
59 #include <linux/delay.h>	/* for loops_per_jiffy */
60 #include <linux/sched.h>
61 #include <linux/mutex.h>
62 #include <asm/io.h>		/* for inb_p, outb_p, inb, outb, etc. */
63 #include <linux/uaccess.h>	/* for get_user, etc. */
64 #include <linux/wait.h>		/* for wait_queue */
65 #include <linux/init.h>		/* for __init, module_{init,exit} */
66 #include <linux/poll.h>		/* for EPOLLIN, etc. */
67 #include <linux/dtlk.h>		/* local header file for DoubleTalk values */
68 
69 #ifdef TRACING
70 #define TRACE_TEXT(str) printk(str);
71 #define TRACE_RET printk(")")
72 #else				/* !TRACING */
73 #define TRACE_TEXT(str) ((void) 0)
74 #define TRACE_RET ((void) 0)
75 #endif				/* TRACING */
76 
77 static DEFINE_MUTEX(dtlk_mutex);
78 static void dtlk_timer_tick(struct timer_list *unused);
79 
80 static int dtlk_major;
81 static int dtlk_port_lpc;
82 static int dtlk_port_tts;
83 static int dtlk_busy;
84 static int dtlk_has_indexing;
85 static unsigned int dtlk_portlist[] =
86 {0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0};
87 static wait_queue_head_t dtlk_process_list;
88 static DEFINE_TIMER(dtlk_timer, dtlk_timer_tick);
89 
90 /* prototypes for file_operations struct */
91 static ssize_t dtlk_read(struct file *, char __user *,
92 			 size_t nbytes, loff_t * ppos);
93 static ssize_t dtlk_write(struct file *, const char __user *,
94 			  size_t nbytes, loff_t * ppos);
95 static __poll_t dtlk_poll(struct file *, poll_table *);
96 static int dtlk_open(struct inode *, struct file *);
97 static int dtlk_release(struct inode *, struct file *);
98 static long dtlk_ioctl(struct file *file,
99 		       unsigned int cmd, unsigned long arg);
100 
101 static const struct file_operations dtlk_fops =
102 {
103 	.owner		= THIS_MODULE,
104 	.read		= dtlk_read,
105 	.write		= dtlk_write,
106 	.poll		= dtlk_poll,
107 	.unlocked_ioctl	= dtlk_ioctl,
108 	.open		= dtlk_open,
109 	.release	= dtlk_release,
110 };
111 
112 /* local prototypes */
113 static int dtlk_dev_probe(void);
114 static struct dtlk_settings *dtlk_interrogate(void);
115 static int dtlk_readable(void);
116 static char dtlk_read_lpc(void);
117 static char dtlk_read_tts(void);
118 static int dtlk_writeable(void);
119 static char dtlk_write_bytes(const char *buf, int n);
120 static char dtlk_write_tts(char);
121 /*
122    static void dtlk_handle_error(char, char, unsigned int);
123  */
124 
125 static ssize_t dtlk_read(struct file *file, char __user *buf,
126 			 size_t count, loff_t * ppos)
127 {
128 	unsigned int minor = iminor(file_inode(file));
129 	char ch;
130 	int i = 0, retries;
131 
132 	TRACE_TEXT("(dtlk_read");
133 	/*  printk("DoubleTalk PC - dtlk_read()\n"); */
134 
135 	if (minor != DTLK_MINOR || !dtlk_has_indexing)
136 		return -EINVAL;
137 
138 	for (retries = 0; retries < loops_per_jiffy; retries++) {
139 		while (i < count && dtlk_readable()) {
140 			ch = dtlk_read_lpc();
141 			/*        printk("dtlk_read() reads 0x%02x\n", ch); */
142 			if (put_user(ch, buf++))
143 				return -EFAULT;
144 			i++;
145 		}
146 		if (i)
147 			return i;
148 		if (file->f_flags & O_NONBLOCK)
149 			break;
150 		msleep_interruptible(100);
151 	}
152 	if (retries == loops_per_jiffy)
153 		printk(KERN_ERR "dtlk_read times out\n");
154 	TRACE_RET;
155 	return -EAGAIN;
156 }
157 
158 static ssize_t dtlk_write(struct file *file, const char __user *buf,
159 			  size_t count, loff_t * ppos)
160 {
161 	int i = 0, retries = 0, ch;
162 
163 	TRACE_TEXT("(dtlk_write");
164 #ifdef TRACING
165 	printk(" \"");
166 	{
167 		int i, ch;
168 		for (i = 0; i < count; i++) {
169 			if (get_user(ch, buf + i))
170 				return -EFAULT;
171 			if (' ' <= ch && ch <= '~')
172 				printk("%c", ch);
173 			else
174 				printk("\\%03o", ch);
175 		}
176 		printk("\"");
177 	}
178 #endif
179 
180 	if (iminor(file_inode(file)) != DTLK_MINOR)
181 		return -EINVAL;
182 
183 	while (1) {
184 		while (i < count && !get_user(ch, buf) &&
185 		       (ch == DTLK_CLEAR || dtlk_writeable())) {
186 			dtlk_write_tts(ch);
187 			buf++;
188 			i++;
189 			if (i % 5 == 0)
190 				/* We yield our time until scheduled
191 				   again.  This reduces the transfer
192 				   rate to 500 bytes/sec, but that's
193 				   still enough to keep up with the
194 				   speech synthesizer. */
195 				msleep_interruptible(1);
196 			else {
197 				/* the RDY bit goes zero 2-3 usec
198 				   after writing, and goes 1 again
199 				   180-190 usec later.  Here, we wait
200 				   up to 250 usec for the RDY bit to
201 				   go nonzero. */
202 				for (retries = 0;
203 				     retries < loops_per_jiffy / (4000/HZ);
204 				     retries++)
205 					if (inb_p(dtlk_port_tts) &
206 					    TTS_WRITABLE)
207 						break;
208 			}
209 			retries = 0;
210 		}
211 		if (i == count)
212 			return i;
213 		if (file->f_flags & O_NONBLOCK)
214 			break;
215 
216 		msleep_interruptible(1);
217 
218 		if (++retries > 10 * HZ) { /* wait no more than 10 sec
219 					      from last write */
220 			printk("dtlk: write timeout.  "
221 			       "inb_p(dtlk_port_tts) = 0x%02x\n",
222 			       inb_p(dtlk_port_tts));
223 			TRACE_RET;
224 			return -EBUSY;
225 		}
226 	}
227 	TRACE_RET;
228 	return -EAGAIN;
229 }
230 
231 static __poll_t dtlk_poll(struct file *file, poll_table * wait)
232 {
233 	__poll_t mask = 0;
234 	unsigned long expires;
235 
236 	TRACE_TEXT(" dtlk_poll");
237 	/*
238 	   static long int j;
239 	   printk(".");
240 	   printk("<%ld>", jiffies-j);
241 	   j=jiffies;
242 	 */
243 	poll_wait(file, &dtlk_process_list, wait);
244 
245 	if (dtlk_has_indexing && dtlk_readable()) {
246 	        del_timer(&dtlk_timer);
247 		mask = EPOLLIN | EPOLLRDNORM;
248 	}
249 	if (dtlk_writeable()) {
250 	        del_timer(&dtlk_timer);
251 		mask |= EPOLLOUT | EPOLLWRNORM;
252 	}
253 	/* there are no exception conditions */
254 
255 	/* There won't be any interrupts, so we set a timer instead. */
256 	expires = jiffies + 3*HZ / 100;
257 	mod_timer(&dtlk_timer, expires);
258 
259 	return mask;
260 }
261 
262 static void dtlk_timer_tick(struct timer_list *unused)
263 {
264 	TRACE_TEXT(" dtlk_timer_tick");
265 	wake_up_interruptible(&dtlk_process_list);
266 }
267 
268 static long dtlk_ioctl(struct file *file,
269 		       unsigned int cmd,
270 		       unsigned long arg)
271 {
272 	char __user *argp = (char __user *)arg;
273 	struct dtlk_settings *sp;
274 	char portval;
275 	TRACE_TEXT(" dtlk_ioctl");
276 
277 	switch (cmd) {
278 
279 	case DTLK_INTERROGATE:
280 		mutex_lock(&dtlk_mutex);
281 		sp = dtlk_interrogate();
282 		mutex_unlock(&dtlk_mutex);
283 		if (copy_to_user(argp, sp, sizeof(struct dtlk_settings)))
284 			return -EINVAL;
285 		return 0;
286 
287 	case DTLK_STATUS:
288 		portval = inb_p(dtlk_port_tts);
289 		return put_user(portval, argp);
290 
291 	default:
292 		return -EINVAL;
293 	}
294 }
295 
296 /* Note that nobody ever sets dtlk_busy... */
297 static int dtlk_open(struct inode *inode, struct file *file)
298 {
299 	TRACE_TEXT("(dtlk_open");
300 
301 	switch (iminor(inode)) {
302 	case DTLK_MINOR:
303 		if (dtlk_busy)
304 			return -EBUSY;
305 		return stream_open(inode, file);
306 
307 	default:
308 		return -ENXIO;
309 	}
310 }
311 
312 static int dtlk_release(struct inode *inode, struct file *file)
313 {
314 	TRACE_TEXT("(dtlk_release");
315 
316 	switch (iminor(inode)) {
317 	case DTLK_MINOR:
318 		break;
319 
320 	default:
321 		break;
322 	}
323 	TRACE_RET;
324 
325 	del_timer_sync(&dtlk_timer);
326 
327 	return 0;
328 }
329 
330 static int __init dtlk_init(void)
331 {
332 	int err;
333 
334 	dtlk_port_lpc = 0;
335 	dtlk_port_tts = 0;
336 	dtlk_busy = 0;
337 	dtlk_major = register_chrdev(0, "dtlk", &dtlk_fops);
338 	if (dtlk_major < 0) {
339 		printk(KERN_ERR "DoubleTalk PC - cannot register device\n");
340 		return dtlk_major;
341 	}
342 	err = dtlk_dev_probe();
343 	if (err) {
344 		unregister_chrdev(dtlk_major, "dtlk");
345 		return err;
346 	}
347 	printk(", MAJOR %d\n", dtlk_major);
348 
349 	init_waitqueue_head(&dtlk_process_list);
350 
351 	return 0;
352 }
353 
354 static void __exit dtlk_cleanup (void)
355 {
356 	dtlk_write_bytes("goodbye", 8);
357 	msleep_interruptible(500);		/* nap 0.50 sec but
358 						   could be awakened
359 						   earlier by
360 						   signals... */
361 
362 	dtlk_write_tts(DTLK_CLEAR);
363 	unregister_chrdev(dtlk_major, "dtlk");
364 	release_region(dtlk_port_lpc, DTLK_IO_EXTENT);
365 }
366 
367 module_init(dtlk_init);
368 module_exit(dtlk_cleanup);
369 
370 /* ------------------------------------------------------------------------ */
371 
372 static int dtlk_readable(void)
373 {
374 #ifdef TRACING
375 	printk(" dtlk_readable=%u@%u", inb_p(dtlk_port_lpc) != 0x7f, jiffies);
376 #endif
377 	return inb_p(dtlk_port_lpc) != 0x7f;
378 }
379 
380 static int dtlk_writeable(void)
381 {
382 	/* TRACE_TEXT(" dtlk_writeable"); */
383 #ifdef TRACINGMORE
384 	printk(" dtlk_writeable=%u", (inb_p(dtlk_port_tts) & TTS_WRITABLE)!=0);
385 #endif
386 	return inb_p(dtlk_port_tts) & TTS_WRITABLE;
387 }
388 
389 static int __init dtlk_dev_probe(void)
390 {
391 	unsigned int testval = 0;
392 	int i = 0;
393 	struct dtlk_settings *sp;
394 
395 	if (dtlk_port_lpc | dtlk_port_tts)
396 		return -EBUSY;
397 
398 	for (i = 0; dtlk_portlist[i]; i++) {
399 #if 0
400 		printk("DoubleTalk PC - Port %03x = %04x\n",
401 		       dtlk_portlist[i], (testval = inw_p(dtlk_portlist[i])));
402 #endif
403 
404 		if (!request_region(dtlk_portlist[i], DTLK_IO_EXTENT,
405 			       "dtlk"))
406 			continue;
407 		testval = inw_p(dtlk_portlist[i]);
408 		if ((testval &= 0xfbff) == 0x107f) {
409 			dtlk_port_lpc = dtlk_portlist[i];
410 			dtlk_port_tts = dtlk_port_lpc + 1;
411 
412 			sp = dtlk_interrogate();
413 			printk("DoubleTalk PC at %03x-%03x, "
414 			       "ROM version %s, serial number %u",
415 			       dtlk_portlist[i], dtlk_portlist[i] +
416 			       DTLK_IO_EXTENT - 1,
417 			       sp->rom_version, sp->serial_number);
418 
419                         /* put LPC port into known state, so
420 			   dtlk_readable() gives valid result */
421 			outb_p(0xff, dtlk_port_lpc);
422 
423                         /* INIT string and index marker */
424 			dtlk_write_bytes("\036\1@\0\0012I\r", 8);
425 			/* posting an index takes 18 msec.  Here, we
426 			   wait up to 100 msec to see whether it
427 			   appears. */
428 			msleep_interruptible(100);
429 			dtlk_has_indexing = dtlk_readable();
430 #ifdef TRACING
431 			printk(", indexing %d\n", dtlk_has_indexing);
432 #endif
433 #ifdef INSCOPE
434 			{
435 /* This macro records ten samples read from the LPC port, for later display */
436 #define LOOK					\
437 for (i = 0; i < 10; i++)			\
438   {						\
439     buffer[b++] = inb_p(dtlk_port_lpc);		\
440     __delay(loops_per_jiffy/(1000000/HZ));             \
441   }
442 				char buffer[1000];
443 				int b = 0, i, j;
444 
445 				LOOK
446 				outb_p(0xff, dtlk_port_lpc);
447 				buffer[b++] = 0;
448 				LOOK
449 				dtlk_write_bytes("\0012I\r", 4);
450 				buffer[b++] = 0;
451 				__delay(50 * loops_per_jiffy / (1000/HZ));
452 				outb_p(0xff, dtlk_port_lpc);
453 				buffer[b++] = 0;
454 				LOOK
455 
456 				printk("\n");
457 				for (j = 0; j < b; j++)
458 					printk(" %02x", buffer[j]);
459 				printk("\n");
460 			}
461 #endif				/* INSCOPE */
462 
463 #ifdef OUTSCOPE
464 			{
465 /* This macro records ten samples read from the TTS port, for later display */
466 #define LOOK					\
467 for (i = 0; i < 10; i++)			\
468   {						\
469     buffer[b++] = inb_p(dtlk_port_tts);		\
470     __delay(loops_per_jiffy/(1000000/HZ));  /* 1 us */ \
471   }
472 				char buffer[1000];
473 				int b = 0, i, j;
474 
475 				mdelay(10);	/* 10 ms */
476 				LOOK
477 				outb_p(0x03, dtlk_port_tts);
478 				buffer[b++] = 0;
479 				LOOK
480 				LOOK
481 
482 				printk("\n");
483 				for (j = 0; j < b; j++)
484 					printk(" %02x", buffer[j]);
485 				printk("\n");
486 			}
487 #endif				/* OUTSCOPE */
488 
489 			dtlk_write_bytes("Double Talk found", 18);
490 
491 			return 0;
492 		}
493 		release_region(dtlk_portlist[i], DTLK_IO_EXTENT);
494 	}
495 
496 	printk(KERN_INFO "DoubleTalk PC - not found\n");
497 	return -ENODEV;
498 }
499 
500 /*
501    static void dtlk_handle_error(char op, char rc, unsigned int minor)
502    {
503    printk(KERN_INFO"\nDoubleTalk PC - MINOR: %d, OPCODE: %d, ERROR: %d\n",
504    minor, op, rc);
505    return;
506    }
507  */
508 
509 /* interrogate the DoubleTalk PC and return its settings */
510 static struct dtlk_settings *dtlk_interrogate(void)
511 {
512 	unsigned char *t;
513 	static char buf[sizeof(struct dtlk_settings) + 1];
514 	int total, i;
515 	static struct dtlk_settings status;
516 	TRACE_TEXT("(dtlk_interrogate");
517 	dtlk_write_bytes("\030\001?", 3);
518 	for (total = 0, i = 0; i < 50; i++) {
519 		buf[total] = dtlk_read_tts();
520 		if (total > 2 && buf[total] == 0x7f)
521 			break;
522 		if (total < sizeof(struct dtlk_settings))
523 			total++;
524 	}
525 	/*
526 	   if (i==50) printk("interrogate() read overrun\n");
527 	   for (i=0; i<sizeof(buf); i++)
528 	   printk(" %02x", buf[i]);
529 	   printk("\n");
530 	 */
531 	t = buf;
532 	status.serial_number = t[0] + t[1] * 256; /* serial number is
533 						     little endian */
534 	t += 2;
535 
536 	i = 0;
537 	while (*t != '\r') {
538 		status.rom_version[i] = *t;
539 		if (i < sizeof(status.rom_version) - 1)
540 			i++;
541 		t++;
542 	}
543 	status.rom_version[i] = 0;
544 	t++;
545 
546 	status.mode = *t++;
547 	status.punc_level = *t++;
548 	status.formant_freq = *t++;
549 	status.pitch = *t++;
550 	status.speed = *t++;
551 	status.volume = *t++;
552 	status.tone = *t++;
553 	status.expression = *t++;
554 	status.ext_dict_loaded = *t++;
555 	status.ext_dict_status = *t++;
556 	status.free_ram = *t++;
557 	status.articulation = *t++;
558 	status.reverb = *t++;
559 	status.eob = *t++;
560 	status.has_indexing = dtlk_has_indexing;
561 	TRACE_RET;
562 	return &status;
563 }
564 
565 static char dtlk_read_tts(void)
566 {
567 	int portval, retries = 0;
568 	char ch;
569 	TRACE_TEXT("(dtlk_read_tts");
570 
571 	/* verify DT is ready, read char, wait for ACK */
572 	do {
573 		portval = inb_p(dtlk_port_tts);
574 	} while ((portval & TTS_READABLE) == 0 &&
575 		 retries++ < DTLK_MAX_RETRIES);
576 	if (retries > DTLK_MAX_RETRIES)
577 		printk(KERN_ERR "dtlk_read_tts() timeout\n");
578 
579 	ch = inb_p(dtlk_port_tts);	/* input from TTS port */
580 	ch &= 0x7f;
581 	outb_p(ch, dtlk_port_tts);
582 
583 	retries = 0;
584 	do {
585 		portval = inb_p(dtlk_port_tts);
586 	} while ((portval & TTS_READABLE) != 0 &&
587 		 retries++ < DTLK_MAX_RETRIES);
588 	if (retries > DTLK_MAX_RETRIES)
589 		printk(KERN_ERR "dtlk_read_tts() timeout\n");
590 
591 	TRACE_RET;
592 	return ch;
593 }
594 
595 static char dtlk_read_lpc(void)
596 {
597 	int retries = 0;
598 	char ch;
599 	TRACE_TEXT("(dtlk_read_lpc");
600 
601 	/* no need to test -- this is only called when the port is readable */
602 
603 	ch = inb_p(dtlk_port_lpc);	/* input from LPC port */
604 
605 	outb_p(0xff, dtlk_port_lpc);
606 
607 	/* acknowledging a read takes 3-4
608 	   usec.  Here, we wait up to 20 usec
609 	   for the acknowledgement */
610 	retries = (loops_per_jiffy * 20) / (1000000/HZ);
611 	while (inb_p(dtlk_port_lpc) != 0x7f && --retries > 0);
612 	if (retries == 0)
613 		printk(KERN_ERR "dtlk_read_lpc() timeout\n");
614 
615 	TRACE_RET;
616 	return ch;
617 }
618 
619 /* write n bytes to tts port */
620 static char dtlk_write_bytes(const char *buf, int n)
621 {
622 	char val = 0;
623 	/*  printk("dtlk_write_bytes(\"%-*s\", %d)\n", n, buf, n); */
624 	TRACE_TEXT("(dtlk_write_bytes");
625 	while (n-- > 0)
626 		val = dtlk_write_tts(*buf++);
627 	TRACE_RET;
628 	return val;
629 }
630 
631 static char dtlk_write_tts(char ch)
632 {
633 	int retries = 0;
634 #ifdef TRACINGMORE
635 	printk("  dtlk_write_tts(");
636 	if (' ' <= ch && ch <= '~')
637 		printk("'%c'", ch);
638 	else
639 		printk("0x%02x", ch);
640 #endif
641 	if (ch != DTLK_CLEAR)	/* no flow control for CLEAR command */
642 		while ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0 &&
643 		       retries++ < DTLK_MAX_RETRIES)	/* DT ready? */
644 			;
645 	if (retries > DTLK_MAX_RETRIES)
646 		printk(KERN_ERR "dtlk_write_tts() timeout\n");
647 
648 	outb_p(ch, dtlk_port_tts);	/* output to TTS port */
649 	/* the RDY bit goes zero 2-3 usec after writing, and goes
650 	   1 again 180-190 usec later.  Here, we wait up to 10
651 	   usec for the RDY bit to go zero. */
652 	for (retries = 0; retries < loops_per_jiffy / (100000/HZ); retries++)
653 		if ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0)
654 			break;
655 
656 #ifdef TRACINGMORE
657 	printk(")\n");
658 #endif
659 	return 0;
660 }
661 
662 MODULE_DESCRIPTION("RC Systems DoubleTalk PC speech card driver");
663 MODULE_LICENSE("GPL");
664