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