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