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