1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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>
57#include <linux/ioport.h>
58#include <linux/delay.h>
59#include <asm/io.h>
60#include <asm/uaccess.h>
61#include <linux/wait.h>
62#include <linux/init.h>
63#include <linux/poll.h>
64#include <linux/dtlk.h>
65
66#ifdef TRACING
67#define TRACE_TEXT(str) printk(str);
68#define TRACE_RET printk(")")
69#else
70#define TRACE_TEXT(str) ((void) 0)
71#define TRACE_RET ((void) 0)
72#endif
73
74static void dtlk_timer_tick(unsigned long data);
75
76static int dtlk_major;
77static int dtlk_port_lpc;
78static int dtlk_port_tts;
79static int dtlk_busy;
80static int dtlk_has_indexing;
81static unsigned int dtlk_portlist[] =
82{0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0};
83static wait_queue_head_t dtlk_process_list;
84static DEFINE_TIMER(dtlk_timer, dtlk_timer_tick, 0, 0);
85
86
87static ssize_t dtlk_read(struct file *, char __user *,
88 size_t nbytes, loff_t * ppos);
89static ssize_t dtlk_write(struct file *, const char __user *,
90 size_t nbytes, loff_t * ppos);
91static unsigned int dtlk_poll(struct file *, poll_table *);
92static int dtlk_open(struct inode *, struct file *);
93static int dtlk_release(struct inode *, struct file *);
94static int dtlk_ioctl(struct inode *inode, struct file *file,
95 unsigned int cmd, unsigned long arg);
96
97static 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
109static int dtlk_dev_probe(void);
110static struct dtlk_settings *dtlk_interrogate(void);
111static int dtlk_readable(void);
112static char dtlk_read_lpc(void);
113static char dtlk_read_tts(void);
114static int dtlk_writeable(void);
115static char dtlk_write_bytes(const char *buf, int n);
116static char dtlk_write_tts(char);
117
118
119
120
121static 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
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
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
154static 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
187
188
189
190
191 msleep_interruptible(1);
192 else {
193
194
195
196
197
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) {
215
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
227static 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
235
236
237
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
250
251
252 expires = jiffies + 3*HZ / 100;
253 mod_timer(&dtlk_timer, expires);
254
255 return mask;
256}
257
258static void dtlk_timer_tick(unsigned long data)
259{
260 TRACE_TEXT(" dtlk_timer_tick");
261 wake_up_interruptible(&dtlk_process_list);
262}
263
264static 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
291static 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
307static 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
325static 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
349static void __exit dtlk_cleanup (void)
350{
351 dtlk_write_bytes("goodbye", 8);
352 msleep_interruptible(500);
353
354
355
356
357 dtlk_write_tts(DTLK_CLEAR);
358 unregister_chrdev(dtlk_major, "dtlk");
359 release_region(dtlk_port_lpc, DTLK_IO_EXTENT);
360}
361
362module_init(dtlk_init);
363module_exit(dtlk_cleanup);
364
365
366
367static 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
375static int dtlk_writeable(void)
376{
377
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
384static 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
415
416 outb_p(0xff, dtlk_port_lpc);
417
418
419 dtlk_write_bytes("\036\1@\0\0012I\r", 8);
420
421
422
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
431#define LOOK \
432for (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
457
458#ifdef OUTSCOPE
459 {
460
461#define LOOK \
462for (i = 0; i < 10; i++) \
463 { \
464 buffer[b++] = inb_p(dtlk_port_tts); \
465 __delay(loops_per_jiffy/(1000000/HZ)); \
466 }
467 char buffer[1000];
468 int b = 0, i, j;
469
470 mdelay(10);
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
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
497
498
499
500
501
502
503
504
505static 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
522
523
524
525
526 t = buf;
527 status.serial_number = t[0] + t[1] * 256;
528
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
560static char dtlk_read_tts(void)
561{
562 int portval, retries = 0;
563 char ch;
564 TRACE_TEXT("(dtlk_read_tts");
565
566
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);
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
590static char dtlk_read_lpc(void)
591{
592 int retries = 0;
593 char ch;
594 TRACE_TEXT("(dtlk_read_lpc");
595
596
597
598 ch = inb_p(dtlk_port_lpc);
599
600 outb_p(0xff, dtlk_port_lpc);
601
602
603
604
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
615static char dtlk_write_bytes(const char *buf, int n)
616{
617 char val = 0;
618
619 TRACE_TEXT("(dtlk_write_bytes");
620 while (n-- > 0)
621 val = dtlk_write_tts(*buf++);
622 TRACE_RET;
623 return val;
624}
625
626static 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)
637 while ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0 &&
638 retries++ < DTLK_MAX_RETRIES)
639 ;
640 if (retries == DTLK_MAX_RETRIES)
641 printk(KERN_ERR "dtlk_write_tts() timeout\n");
642
643 outb_p(ch, dtlk_port_tts);
644
645
646
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
657MODULE_LICENSE("GPL");
658