xref: /openbmc/linux/drivers/char/dtlk.c (revision 8569c914)
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 <linux/smp_lock.h>	/* cycle_kernel_lock() */
60 #include <asm/io.h>		/* for inb_p, outb_p, inb, outb, etc. */
61 #include <asm/uaccess.h>	/* for get_user, etc. */
62 #include <linux/wait.h>		/* for wait_queue */
63 #include <linux/init.h>		/* for __init, module_{init,exit} */
64 #include <linux/poll.h>		/* for POLLIN, etc. */
65 #include <linux/dtlk.h>		/* local header file for DoubleTalk values */
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 static void dtlk_timer_tick(unsigned long data);
76 
77 static int dtlk_major;
78 static int dtlk_port_lpc;
79 static int dtlk_port_tts;
80 static int dtlk_busy;
81 static int dtlk_has_indexing;
82 static unsigned int dtlk_portlist[] =
83 {0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0};
84 static wait_queue_head_t dtlk_process_list;
85 static DEFINE_TIMER(dtlk_timer, dtlk_timer_tick, 0, 0);
86 
87 /* prototypes for file_operations struct */
88 static ssize_t dtlk_read(struct file *, char __user *,
89 			 size_t nbytes, loff_t * ppos);
90 static ssize_t dtlk_write(struct file *, const char __user *,
91 			  size_t nbytes, loff_t * ppos);
92 static unsigned int dtlk_poll(struct file *, poll_table *);
93 static int dtlk_open(struct inode *, struct file *);
94 static int dtlk_release(struct inode *, struct file *);
95 static int dtlk_ioctl(struct inode *inode, struct file *file,
96 		      unsigned int cmd, unsigned long arg);
97 
98 static const struct file_operations dtlk_fops =
99 {
100 	.owner		= THIS_MODULE,
101 	.read		= dtlk_read,
102 	.write		= dtlk_write,
103 	.poll		= dtlk_poll,
104 	.ioctl		= dtlk_ioctl,
105 	.open		= dtlk_open,
106 	.release	= dtlk_release,
107 };
108 
109 /* local prototypes */
110 static int dtlk_dev_probe(void);
111 static struct dtlk_settings *dtlk_interrogate(void);
112 static int dtlk_readable(void);
113 static char dtlk_read_lpc(void);
114 static char dtlk_read_tts(void);
115 static int dtlk_writeable(void);
116 static char dtlk_write_bytes(const char *buf, int n);
117 static char dtlk_write_tts(char);
118 /*
119    static void dtlk_handle_error(char, char, unsigned int);
120  */
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_path.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_path.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 /* Note that nobody ever sets dtlk_busy... */
293 static int dtlk_open(struct inode *inode, struct file *file)
294 {
295 	TRACE_TEXT("(dtlk_open");
296 
297 	cycle_kernel_lock();
298 	nonseekable_open(inode, file);
299 	switch (iminor(inode)) {
300 	case DTLK_MINOR:
301 		if (dtlk_busy)
302 			return -EBUSY;
303 		return nonseekable_open(inode, file);
304 
305 	default:
306 		return -ENXIO;
307 	}
308 }
309 
310 static int dtlk_release(struct inode *inode, struct file *file)
311 {
312 	TRACE_TEXT("(dtlk_release");
313 
314 	switch (iminor(inode)) {
315 	case DTLK_MINOR:
316 		break;
317 
318 	default:
319 		break;
320 	}
321 	TRACE_RET;
322 
323 	del_timer_sync(&dtlk_timer);
324 
325 	return 0;
326 }
327 
328 static int __init dtlk_init(void)
329 {
330 	int err;
331 
332 	dtlk_port_lpc = 0;
333 	dtlk_port_tts = 0;
334 	dtlk_busy = 0;
335 	dtlk_major = register_chrdev(0, "dtlk", &dtlk_fops);
336 	if (dtlk_major < 0) {
337 		printk(KERN_ERR "DoubleTalk PC - cannot register device\n");
338 		return dtlk_major;
339 	}
340 	err = dtlk_dev_probe();
341 	if (err) {
342 		unregister_chrdev(dtlk_major, "dtlk");
343 		return err;
344 	}
345 	printk(", MAJOR %d\n", dtlk_major);
346 
347 	init_waitqueue_head(&dtlk_process_list);
348 
349 	return 0;
350 }
351 
352 static void __exit dtlk_cleanup (void)
353 {
354 	dtlk_write_bytes("goodbye", 8);
355 	msleep_interruptible(500);		/* nap 0.50 sec but
356 						   could be awakened
357 						   earlier by
358 						   signals... */
359 
360 	dtlk_write_tts(DTLK_CLEAR);
361 	unregister_chrdev(dtlk_major, "dtlk");
362 	release_region(dtlk_port_lpc, DTLK_IO_EXTENT);
363 }
364 
365 module_init(dtlk_init);
366 module_exit(dtlk_cleanup);
367 
368 /* ------------------------------------------------------------------------ */
369 
370 static int dtlk_readable(void)
371 {
372 #ifdef TRACING
373 	printk(" dtlk_readable=%u@%u", inb_p(dtlk_port_lpc) != 0x7f, jiffies);
374 #endif
375 	return inb_p(dtlk_port_lpc) != 0x7f;
376 }
377 
378 static int dtlk_writeable(void)
379 {
380 	/* TRACE_TEXT(" dtlk_writeable"); */
381 #ifdef TRACINGMORE
382 	printk(" dtlk_writeable=%u", (inb_p(dtlk_port_tts) & TTS_WRITABLE)!=0);
383 #endif
384 	return inb_p(dtlk_port_tts) & TTS_WRITABLE;
385 }
386 
387 static int __init dtlk_dev_probe(void)
388 {
389 	unsigned int testval = 0;
390 	int i = 0;
391 	struct dtlk_settings *sp;
392 
393 	if (dtlk_port_lpc | dtlk_port_tts)
394 		return -EBUSY;
395 
396 	for (i = 0; dtlk_portlist[i]; i++) {
397 #if 0
398 		printk("DoubleTalk PC - Port %03x = %04x\n",
399 		       dtlk_portlist[i], (testval = inw_p(dtlk_portlist[i])));
400 #endif
401 
402 		if (!request_region(dtlk_portlist[i], DTLK_IO_EXTENT,
403 			       "dtlk"))
404 			continue;
405 		testval = inw_p(dtlk_portlist[i]);
406 		if ((testval &= 0xfbff) == 0x107f) {
407 			dtlk_port_lpc = dtlk_portlist[i];
408 			dtlk_port_tts = dtlk_port_lpc + 1;
409 
410 			sp = dtlk_interrogate();
411 			printk("DoubleTalk PC at %03x-%03x, "
412 			       "ROM version %s, serial number %u",
413 			       dtlk_portlist[i], dtlk_portlist[i] +
414 			       DTLK_IO_EXTENT - 1,
415 			       sp->rom_version, sp->serial_number);
416 
417                         /* put LPC port into known state, so
418 			   dtlk_readable() gives valid result */
419 			outb_p(0xff, dtlk_port_lpc);
420 
421                         /* INIT string and index marker */
422 			dtlk_write_bytes("\036\1@\0\0012I\r", 8);
423 			/* posting an index takes 18 msec.  Here, we
424 			   wait up to 100 msec to see whether it
425 			   appears. */
426 			msleep_interruptible(100);
427 			dtlk_has_indexing = dtlk_readable();
428 #ifdef TRACING
429 			printk(", indexing %d\n", dtlk_has_indexing);
430 #endif
431 #ifdef INSCOPE
432 			{
433 /* This macro records ten samples read from the LPC port, for later display */
434 #define LOOK					\
435 for (i = 0; i < 10; i++)			\
436   {						\
437     buffer[b++] = inb_p(dtlk_port_lpc);		\
438     __delay(loops_per_jiffy/(1000000/HZ));             \
439   }
440 				char buffer[1000];
441 				int b = 0, i, j;
442 
443 				LOOK
444 				outb_p(0xff, dtlk_port_lpc);
445 				buffer[b++] = 0;
446 				LOOK
447 				dtlk_write_bytes("\0012I\r", 4);
448 				buffer[b++] = 0;
449 				__delay(50 * loops_per_jiffy / (1000/HZ));
450 				outb_p(0xff, dtlk_port_lpc);
451 				buffer[b++] = 0;
452 				LOOK
453 
454 				printk("\n");
455 				for (j = 0; j < b; j++)
456 					printk(" %02x", buffer[j]);
457 				printk("\n");
458 			}
459 #endif				/* INSCOPE */
460 
461 #ifdef OUTSCOPE
462 			{
463 /* This macro records ten samples read from the TTS port, for later display */
464 #define LOOK					\
465 for (i = 0; i < 10; i++)			\
466   {						\
467     buffer[b++] = inb_p(dtlk_port_tts);		\
468     __delay(loops_per_jiffy/(1000000/HZ));  /* 1 us */ \
469   }
470 				char buffer[1000];
471 				int b = 0, i, j;
472 
473 				mdelay(10);	/* 10 ms */
474 				LOOK
475 				outb_p(0x03, dtlk_port_tts);
476 				buffer[b++] = 0;
477 				LOOK
478 				LOOK
479 
480 				printk("\n");
481 				for (j = 0; j < b; j++)
482 					printk(" %02x", buffer[j]);
483 				printk("\n");
484 			}
485 #endif				/* OUTSCOPE */
486 
487 			dtlk_write_bytes("Double Talk found", 18);
488 
489 			return 0;
490 		}
491 		release_region(dtlk_portlist[i], DTLK_IO_EXTENT);
492 	}
493 
494 	printk(KERN_INFO "DoubleTalk PC - not found\n");
495 	return -ENODEV;
496 }
497 
498 /*
499    static void dtlk_handle_error(char op, char rc, unsigned int minor)
500    {
501    printk(KERN_INFO"\nDoubleTalk PC - MINOR: %d, OPCODE: %d, ERROR: %d\n",
502    minor, op, rc);
503    return;
504    }
505  */
506 
507 /* interrogate the DoubleTalk PC and return its settings */
508 static struct dtlk_settings *dtlk_interrogate(void)
509 {
510 	unsigned char *t;
511 	static char buf[sizeof(struct dtlk_settings) + 1];
512 	int total, i;
513 	static struct dtlk_settings status;
514 	TRACE_TEXT("(dtlk_interrogate");
515 	dtlk_write_bytes("\030\001?", 3);
516 	for (total = 0, i = 0; i < 50; i++) {
517 		buf[total] = dtlk_read_tts();
518 		if (total > 2 && buf[total] == 0x7f)
519 			break;
520 		if (total < sizeof(struct dtlk_settings))
521 			total++;
522 	}
523 	/*
524 	   if (i==50) printk("interrogate() read overrun\n");
525 	   for (i=0; i<sizeof(buf); i++)
526 	   printk(" %02x", buf[i]);
527 	   printk("\n");
528 	 */
529 	t = buf;
530 	status.serial_number = t[0] + t[1] * 256; /* serial number is
531 						     little endian */
532 	t += 2;
533 
534 	i = 0;
535 	while (*t != '\r') {
536 		status.rom_version[i] = *t;
537 		if (i < sizeof(status.rom_version) - 1)
538 			i++;
539 		t++;
540 	}
541 	status.rom_version[i] = 0;
542 	t++;
543 
544 	status.mode = *t++;
545 	status.punc_level = *t++;
546 	status.formant_freq = *t++;
547 	status.pitch = *t++;
548 	status.speed = *t++;
549 	status.volume = *t++;
550 	status.tone = *t++;
551 	status.expression = *t++;
552 	status.ext_dict_loaded = *t++;
553 	status.ext_dict_status = *t++;
554 	status.free_ram = *t++;
555 	status.articulation = *t++;
556 	status.reverb = *t++;
557 	status.eob = *t++;
558 	status.has_indexing = dtlk_has_indexing;
559 	TRACE_RET;
560 	return &status;
561 }
562 
563 static char dtlk_read_tts(void)
564 {
565 	int portval, retries = 0;
566 	char ch;
567 	TRACE_TEXT("(dtlk_read_tts");
568 
569 	/* verify DT is ready, read char, wait for ACK */
570 	do {
571 		portval = inb_p(dtlk_port_tts);
572 	} while ((portval & TTS_READABLE) == 0 &&
573 		 retries++ < DTLK_MAX_RETRIES);
574 	if (retries == DTLK_MAX_RETRIES)
575 		printk(KERN_ERR "dtlk_read_tts() timeout\n");
576 
577 	ch = inb_p(dtlk_port_tts);	/* input from TTS port */
578 	ch &= 0x7f;
579 	outb_p(ch, dtlk_port_tts);
580 
581 	retries = 0;
582 	do {
583 		portval = inb_p(dtlk_port_tts);
584 	} while ((portval & TTS_READABLE) != 0 &&
585 		 retries++ < DTLK_MAX_RETRIES);
586 	if (retries == DTLK_MAX_RETRIES)
587 		printk(KERN_ERR "dtlk_read_tts() timeout\n");
588 
589 	TRACE_RET;
590 	return ch;
591 }
592 
593 static char dtlk_read_lpc(void)
594 {
595 	int retries = 0;
596 	char ch;
597 	TRACE_TEXT("(dtlk_read_lpc");
598 
599 	/* no need to test -- this is only called when the port is readable */
600 
601 	ch = inb_p(dtlk_port_lpc);	/* input from LPC port */
602 
603 	outb_p(0xff, dtlk_port_lpc);
604 
605 	/* acknowledging a read takes 3-4
606 	   usec.  Here, we wait up to 20 usec
607 	   for the acknowledgement */
608 	retries = (loops_per_jiffy * 20) / (1000000/HZ);
609 	while (inb_p(dtlk_port_lpc) != 0x7f && --retries > 0);
610 	if (retries == 0)
611 		printk(KERN_ERR "dtlk_read_lpc() timeout\n");
612 
613 	TRACE_RET;
614 	return ch;
615 }
616 
617 /* write n bytes to tts port */
618 static char dtlk_write_bytes(const char *buf, int n)
619 {
620 	char val = 0;
621 	/*  printk("dtlk_write_bytes(\"%-*s\", %d)\n", n, buf, n); */
622 	TRACE_TEXT("(dtlk_write_bytes");
623 	while (n-- > 0)
624 		val = dtlk_write_tts(*buf++);
625 	TRACE_RET;
626 	return val;
627 }
628 
629 static char dtlk_write_tts(char ch)
630 {
631 	int retries = 0;
632 #ifdef TRACINGMORE
633 	printk("  dtlk_write_tts(");
634 	if (' ' <= ch && ch <= '~')
635 		printk("'%c'", ch);
636 	else
637 		printk("0x%02x", ch);
638 #endif
639 	if (ch != DTLK_CLEAR)	/* no flow control for CLEAR command */
640 		while ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0 &&
641 		       retries++ < DTLK_MAX_RETRIES)	/* DT ready? */
642 			;
643 	if (retries == DTLK_MAX_RETRIES)
644 		printk(KERN_ERR "dtlk_write_tts() timeout\n");
645 
646 	outb_p(ch, dtlk_port_tts);	/* output to TTS port */
647 	/* the RDY bit goes zero 2-3 usec after writing, and goes
648 	   1 again 180-190 usec later.  Here, we wait up to 10
649 	   usec for the RDY bit to go zero. */
650 	for (retries = 0; retries < loops_per_jiffy / (100000/HZ); retries++)
651 		if ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0)
652 			break;
653 
654 #ifdef TRACINGMORE
655 	printk(")\n");
656 #endif
657 	return 0;
658 }
659 
660 MODULE_LICENSE("GPL");
661