xref: /openbmc/linux/drivers/char/dtlk.c (revision 22246614)
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 
66 #ifdef TRACING
67 #define TRACE_TEXT(str) printk(str);
68 #define TRACE_RET printk(")")
69 #else				/* !TRACING */
70 #define TRACE_TEXT(str) ((void) 0)
71 #define TRACE_RET ((void) 0)
72 #endif				/* TRACING */
73 
74 static void dtlk_timer_tick(unsigned long data);
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 DEFINE_TIMER(dtlk_timer, dtlk_timer_tick, 0, 0);
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 
121 static ssize_t dtlk_read(struct file *file, char __user *buf,
122 			 size_t count, loff_t * ppos)
123 {
124 	unsigned int minor = iminor(file->f_path.dentry->d_inode);
125 	char ch;
126 	int i = 0, retries;
127 
128 	TRACE_TEXT("(dtlk_read");
129 	/*  printk("DoubleTalk PC - dtlk_read()\n"); */
130 
131 	if (minor != DTLK_MINOR || !dtlk_has_indexing)
132 		return -EINVAL;
133 
134 	for (retries = 0; retries < loops_per_jiffy; retries++) {
135 		while (i < count && dtlk_readable()) {
136 			ch = dtlk_read_lpc();
137 			/*        printk("dtlk_read() reads 0x%02x\n", ch); */
138 			if (put_user(ch, buf++))
139 				return -EFAULT;
140 			i++;
141 		}
142 		if (i)
143 			return i;
144 		if (file->f_flags & O_NONBLOCK)
145 			break;
146 		msleep_interruptible(100);
147 	}
148 	if (retries == loops_per_jiffy)
149 		printk(KERN_ERR "dtlk_read times out\n");
150 	TRACE_RET;
151 	return -EAGAIN;
152 }
153 
154 static ssize_t dtlk_write(struct file *file, const char __user *buf,
155 			  size_t count, loff_t * ppos)
156 {
157 	int i = 0, retries = 0, ch;
158 
159 	TRACE_TEXT("(dtlk_write");
160 #ifdef TRACING
161 	printk(" \"");
162 	{
163 		int i, ch;
164 		for (i = 0; i < count; i++) {
165 			if (get_user(ch, buf + i))
166 				return -EFAULT;
167 			if (' ' <= ch && ch <= '~')
168 				printk("%c", ch);
169 			else
170 				printk("\\%03o", ch);
171 		}
172 		printk("\"");
173 	}
174 #endif
175 
176 	if (iminor(file->f_path.dentry->d_inode) != DTLK_MINOR)
177 		return -EINVAL;
178 
179 	while (1) {
180 		while (i < count && !get_user(ch, buf) &&
181 		       (ch == DTLK_CLEAR || dtlk_writeable())) {
182 			dtlk_write_tts(ch);
183 			buf++;
184 			i++;
185 			if (i % 5 == 0)
186 				/* We yield our time until scheduled
187 				   again.  This reduces the transfer
188 				   rate to 500 bytes/sec, but that's
189 				   still enough to keep up with the
190 				   speech synthesizer. */
191 				msleep_interruptible(1);
192 			else {
193 				/* the RDY bit goes zero 2-3 usec
194 				   after writing, and goes 1 again
195 				   180-190 usec later.  Here, we wait
196 				   up to 250 usec for the RDY bit to
197 				   go nonzero. */
198 				for (retries = 0;
199 				     retries < loops_per_jiffy / (4000/HZ);
200 				     retries++)
201 					if (inb_p(dtlk_port_tts) &
202 					    TTS_WRITABLE)
203 						break;
204 			}
205 			retries = 0;
206 		}
207 		if (i == count)
208 			return i;
209 		if (file->f_flags & O_NONBLOCK)
210 			break;
211 
212 		msleep_interruptible(1);
213 
214 		if (++retries > 10 * HZ) { /* wait no more than 10 sec
215 					      from last write */
216 			printk("dtlk: write timeout.  "
217 			       "inb_p(dtlk_port_tts) = 0x%02x\n",
218 			       inb_p(dtlk_port_tts));
219 			TRACE_RET;
220 			return -EBUSY;
221 		}
222 	}
223 	TRACE_RET;
224 	return -EAGAIN;
225 }
226 
227 static unsigned int dtlk_poll(struct file *file, poll_table * wait)
228 {
229 	int mask = 0;
230 	unsigned long expires;
231 
232 	TRACE_TEXT(" dtlk_poll");
233 	/*
234 	   static long int j;
235 	   printk(".");
236 	   printk("<%ld>", jiffies-j);
237 	   j=jiffies;
238 	 */
239 	poll_wait(file, &dtlk_process_list, wait);
240 
241 	if (dtlk_has_indexing && dtlk_readable()) {
242 	        del_timer(&dtlk_timer);
243 		mask = POLLIN | POLLRDNORM;
244 	}
245 	if (dtlk_writeable()) {
246 	        del_timer(&dtlk_timer);
247 		mask |= POLLOUT | POLLWRNORM;
248 	}
249 	/* there are no exception conditions */
250 
251 	/* There won't be any interrupts, so we set a timer instead. */
252 	expires = jiffies + 3*HZ / 100;
253 	mod_timer(&dtlk_timer, expires);
254 
255 	return mask;
256 }
257 
258 static void dtlk_timer_tick(unsigned long data)
259 {
260 	TRACE_TEXT(" dtlk_timer_tick");
261 	wake_up_interruptible(&dtlk_process_list);
262 }
263 
264 static int dtlk_ioctl(struct inode *inode,
265 		      struct file *file,
266 		      unsigned int cmd,
267 		      unsigned long arg)
268 {
269 	char __user *argp = (char __user *)arg;
270 	struct dtlk_settings *sp;
271 	char portval;
272 	TRACE_TEXT(" dtlk_ioctl");
273 
274 	switch (cmd) {
275 
276 	case DTLK_INTERROGATE:
277 		sp = dtlk_interrogate();
278 		if (copy_to_user(argp, sp, sizeof(struct dtlk_settings)))
279 			return -EINVAL;
280 		return 0;
281 
282 	case DTLK_STATUS:
283 		portval = inb_p(dtlk_port_tts);
284 		return put_user(portval, argp);
285 
286 	default:
287 		return -EINVAL;
288 	}
289 }
290 
291 static int dtlk_open(struct inode *inode, struct file *file)
292 {
293 	TRACE_TEXT("(dtlk_open");
294 
295 	nonseekable_open(inode, file);
296 	switch (iminor(inode)) {
297 	case DTLK_MINOR:
298 		if (dtlk_busy)
299 			return -EBUSY;
300 		return nonseekable_open(inode, file);
301 
302 	default:
303 		return -ENXIO;
304 	}
305 }
306 
307 static int dtlk_release(struct inode *inode, struct file *file)
308 {
309 	TRACE_TEXT("(dtlk_release");
310 
311 	switch (iminor(inode)) {
312 	case DTLK_MINOR:
313 		break;
314 
315 	default:
316 		break;
317 	}
318 	TRACE_RET;
319 
320 	del_timer_sync(&dtlk_timer);
321 
322 	return 0;
323 }
324 
325 static int __init dtlk_init(void)
326 {
327 	int err;
328 
329 	dtlk_port_lpc = 0;
330 	dtlk_port_tts = 0;
331 	dtlk_busy = 0;
332 	dtlk_major = register_chrdev(0, "dtlk", &dtlk_fops);
333 	if (dtlk_major < 0) {
334 		printk(KERN_ERR "DoubleTalk PC - cannot register device\n");
335 		return dtlk_major;
336 	}
337 	err = dtlk_dev_probe();
338 	if (err) {
339 		unregister_chrdev(dtlk_major, "dtlk");
340 		return err;
341 	}
342 	printk(", MAJOR %d\n", dtlk_major);
343 
344 	init_waitqueue_head(&dtlk_process_list);
345 
346 	return 0;
347 }
348 
349 static void __exit dtlk_cleanup (void)
350 {
351 	dtlk_write_bytes("goodbye", 8);
352 	msleep_interruptible(500);		/* nap 0.50 sec but
353 						   could be awakened
354 						   earlier by
355 						   signals... */
356 
357 	dtlk_write_tts(DTLK_CLEAR);
358 	unregister_chrdev(dtlk_major, "dtlk");
359 	release_region(dtlk_port_lpc, DTLK_IO_EXTENT);
360 }
361 
362 module_init(dtlk_init);
363 module_exit(dtlk_cleanup);
364 
365 /* ------------------------------------------------------------------------ */
366 
367 static int dtlk_readable(void)
368 {
369 #ifdef TRACING
370 	printk(" dtlk_readable=%u@%u", inb_p(dtlk_port_lpc) != 0x7f, jiffies);
371 #endif
372 	return inb_p(dtlk_port_lpc) != 0x7f;
373 }
374 
375 static int dtlk_writeable(void)
376 {
377 	/* TRACE_TEXT(" dtlk_writeable"); */
378 #ifdef TRACINGMORE
379 	printk(" dtlk_writeable=%u", (inb_p(dtlk_port_tts) & TTS_WRITABLE)!=0);
380 #endif
381 	return inb_p(dtlk_port_tts) & TTS_WRITABLE;
382 }
383 
384 static int __init dtlk_dev_probe(void)
385 {
386 	unsigned int testval = 0;
387 	int i = 0;
388 	struct dtlk_settings *sp;
389 
390 	if (dtlk_port_lpc | dtlk_port_tts)
391 		return -EBUSY;
392 
393 	for (i = 0; dtlk_portlist[i]; i++) {
394 #if 0
395 		printk("DoubleTalk PC - Port %03x = %04x\n",
396 		       dtlk_portlist[i], (testval = inw_p(dtlk_portlist[i])));
397 #endif
398 
399 		if (!request_region(dtlk_portlist[i], DTLK_IO_EXTENT,
400 			       "dtlk"))
401 			continue;
402 		testval = inw_p(dtlk_portlist[i]);
403 		if ((testval &= 0xfbff) == 0x107f) {
404 			dtlk_port_lpc = dtlk_portlist[i];
405 			dtlk_port_tts = dtlk_port_lpc + 1;
406 
407 			sp = dtlk_interrogate();
408 			printk("DoubleTalk PC at %03x-%03x, "
409 			       "ROM version %s, serial number %u",
410 			       dtlk_portlist[i], dtlk_portlist[i] +
411 			       DTLK_IO_EXTENT - 1,
412 			       sp->rom_version, sp->serial_number);
413 
414                         /* put LPC port into known state, so
415 			   dtlk_readable() gives valid result */
416 			outb_p(0xff, dtlk_port_lpc);
417 
418                         /* INIT string and index marker */
419 			dtlk_write_bytes("\036\1@\0\0012I\r", 8);
420 			/* posting an index takes 18 msec.  Here, we
421 			   wait up to 100 msec to see whether it
422 			   appears. */
423 			msleep_interruptible(100);
424 			dtlk_has_indexing = dtlk_readable();
425 #ifdef TRACING
426 			printk(", indexing %d\n", dtlk_has_indexing);
427 #endif
428 #ifdef INSCOPE
429 			{
430 /* This macro records ten samples read from the LPC port, for later display */
431 #define LOOK					\
432 for (i = 0; i < 10; i++)			\
433   {						\
434     buffer[b++] = inb_p(dtlk_port_lpc);		\
435     __delay(loops_per_jiffy/(1000000/HZ));             \
436   }
437 				char buffer[1000];
438 				int b = 0, i, j;
439 
440 				LOOK
441 				outb_p(0xff, dtlk_port_lpc);
442 				buffer[b++] = 0;
443 				LOOK
444 				dtlk_write_bytes("\0012I\r", 4);
445 				buffer[b++] = 0;
446 				__delay(50 * loops_per_jiffy / (1000/HZ));
447 				outb_p(0xff, dtlk_port_lpc);
448 				buffer[b++] = 0;
449 				LOOK
450 
451 				printk("\n");
452 				for (j = 0; j < b; j++)
453 					printk(" %02x", buffer[j]);
454 				printk("\n");
455 			}
456 #endif				/* INSCOPE */
457 
458 #ifdef OUTSCOPE
459 			{
460 /* This macro records ten samples read from the TTS port, for later display */
461 #define LOOK					\
462 for (i = 0; i < 10; i++)			\
463   {						\
464     buffer[b++] = inb_p(dtlk_port_tts);		\
465     __delay(loops_per_jiffy/(1000000/HZ));  /* 1 us */ \
466   }
467 				char buffer[1000];
468 				int b = 0, i, j;
469 
470 				mdelay(10);	/* 10 ms */
471 				LOOK
472 				outb_p(0x03, dtlk_port_tts);
473 				buffer[b++] = 0;
474 				LOOK
475 				LOOK
476 
477 				printk("\n");
478 				for (j = 0; j < b; j++)
479 					printk(" %02x", buffer[j]);
480 				printk("\n");
481 			}
482 #endif				/* OUTSCOPE */
483 
484 			dtlk_write_bytes("Double Talk found", 18);
485 
486 			return 0;
487 		}
488 		release_region(dtlk_portlist[i], DTLK_IO_EXTENT);
489 	}
490 
491 	printk(KERN_INFO "DoubleTalk PC - not found\n");
492 	return -ENODEV;
493 }
494 
495 /*
496    static void dtlk_handle_error(char op, char rc, unsigned int minor)
497    {
498    printk(KERN_INFO"\nDoubleTalk PC - MINOR: %d, OPCODE: %d, ERROR: %d\n",
499    minor, op, rc);
500    return;
501    }
502  */
503 
504 /* interrogate the DoubleTalk PC and return its settings */
505 static struct dtlk_settings *dtlk_interrogate(void)
506 {
507 	unsigned char *t;
508 	static char buf[sizeof(struct dtlk_settings) + 1];
509 	int total, i;
510 	static struct dtlk_settings status;
511 	TRACE_TEXT("(dtlk_interrogate");
512 	dtlk_write_bytes("\030\001?", 3);
513 	for (total = 0, i = 0; i < 50; i++) {
514 		buf[total] = dtlk_read_tts();
515 		if (total > 2 && buf[total] == 0x7f)
516 			break;
517 		if (total < sizeof(struct dtlk_settings))
518 			total++;
519 	}
520 	/*
521 	   if (i==50) printk("interrogate() read overrun\n");
522 	   for (i=0; i<sizeof(buf); i++)
523 	   printk(" %02x", buf[i]);
524 	   printk("\n");
525 	 */
526 	t = buf;
527 	status.serial_number = t[0] + t[1] * 256; /* serial number is
528 						     little endian */
529 	t += 2;
530 
531 	i = 0;
532 	while (*t != '\r') {
533 		status.rom_version[i] = *t;
534 		if (i < sizeof(status.rom_version) - 1)
535 			i++;
536 		t++;
537 	}
538 	status.rom_version[i] = 0;
539 	t++;
540 
541 	status.mode = *t++;
542 	status.punc_level = *t++;
543 	status.formant_freq = *t++;
544 	status.pitch = *t++;
545 	status.speed = *t++;
546 	status.volume = *t++;
547 	status.tone = *t++;
548 	status.expression = *t++;
549 	status.ext_dict_loaded = *t++;
550 	status.ext_dict_status = *t++;
551 	status.free_ram = *t++;
552 	status.articulation = *t++;
553 	status.reverb = *t++;
554 	status.eob = *t++;
555 	status.has_indexing = dtlk_has_indexing;
556 	TRACE_RET;
557 	return &status;
558 }
559 
560 static char dtlk_read_tts(void)
561 {
562 	int portval, retries = 0;
563 	char ch;
564 	TRACE_TEXT("(dtlk_read_tts");
565 
566 	/* verify DT is ready, read char, wait for ACK */
567 	do {
568 		portval = inb_p(dtlk_port_tts);
569 	} while ((portval & TTS_READABLE) == 0 &&
570 		 retries++ < DTLK_MAX_RETRIES);
571 	if (retries == DTLK_MAX_RETRIES)
572 		printk(KERN_ERR "dtlk_read_tts() timeout\n");
573 
574 	ch = inb_p(dtlk_port_tts);	/* input from TTS port */
575 	ch &= 0x7f;
576 	outb_p(ch, dtlk_port_tts);
577 
578 	retries = 0;
579 	do {
580 		portval = inb_p(dtlk_port_tts);
581 	} while ((portval & TTS_READABLE) != 0 &&
582 		 retries++ < DTLK_MAX_RETRIES);
583 	if (retries == DTLK_MAX_RETRIES)
584 		printk(KERN_ERR "dtlk_read_tts() timeout\n");
585 
586 	TRACE_RET;
587 	return ch;
588 }
589 
590 static char dtlk_read_lpc(void)
591 {
592 	int retries = 0;
593 	char ch;
594 	TRACE_TEXT("(dtlk_read_lpc");
595 
596 	/* no need to test -- this is only called when the port is readable */
597 
598 	ch = inb_p(dtlk_port_lpc);	/* input from LPC port */
599 
600 	outb_p(0xff, dtlk_port_lpc);
601 
602 	/* acknowledging a read takes 3-4
603 	   usec.  Here, we wait up to 20 usec
604 	   for the acknowledgement */
605 	retries = (loops_per_jiffy * 20) / (1000000/HZ);
606 	while (inb_p(dtlk_port_lpc) != 0x7f && --retries > 0);
607 	if (retries == 0)
608 		printk(KERN_ERR "dtlk_read_lpc() timeout\n");
609 
610 	TRACE_RET;
611 	return ch;
612 }
613 
614 /* write n bytes to tts port */
615 static char dtlk_write_bytes(const char *buf, int n)
616 {
617 	char val = 0;
618 	/*  printk("dtlk_write_bytes(\"%-*s\", %d)\n", n, buf, n); */
619 	TRACE_TEXT("(dtlk_write_bytes");
620 	while (n-- > 0)
621 		val = dtlk_write_tts(*buf++);
622 	TRACE_RET;
623 	return val;
624 }
625 
626 static char dtlk_write_tts(char ch)
627 {
628 	int retries = 0;
629 #ifdef TRACINGMORE
630 	printk("  dtlk_write_tts(");
631 	if (' ' <= ch && ch <= '~')
632 		printk("'%c'", ch);
633 	else
634 		printk("0x%02x", ch);
635 #endif
636 	if (ch != DTLK_CLEAR)	/* no flow control for CLEAR command */
637 		while ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0 &&
638 		       retries++ < DTLK_MAX_RETRIES)	/* DT ready? */
639 			;
640 	if (retries == DTLK_MAX_RETRIES)
641 		printk(KERN_ERR "dtlk_write_tts() timeout\n");
642 
643 	outb_p(ch, dtlk_port_tts);	/* output to TTS port */
644 	/* the RDY bit goes zero 2-3 usec after writing, and goes
645 	   1 again 180-190 usec later.  Here, we wait up to 10
646 	   usec for the RDY bit to go zero. */
647 	for (retries = 0; retries < loops_per_jiffy / (100000/HZ); retries++)
648 		if ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0)
649 			break;
650 
651 #ifdef TRACINGMORE
652 	printk(")\n");
653 #endif
654 	return 0;
655 }
656 
657 MODULE_LICENSE("GPL");
658