0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051 #include <linux/module.h>
0052
0053 #define KERNEL
0054 #include <linux/types.h>
0055 #include <linux/fs.h>
0056 #include <linux/mm.h>
0057 #include <linux/errno.h> /* for -EBUSY */
0058 #include <linux/ioport.h> /* for request_region */
0059 #include <linux/delay.h> /* for loops_per_jiffy */
0060 #include <linux/sched.h>
0061 #include <linux/mutex.h>
0062 #include <asm/io.h> /* for inb_p, outb_p, inb, outb, etc. */
0063 #include <linux/uaccess.h> /* for get_user, etc. */
0064 #include <linux/wait.h> /* for wait_queue */
0065 #include <linux/init.h> /* for __init, module_{init,exit} */
0066 #include <linux/poll.h> /* for EPOLLIN, etc. */
0067 #include <linux/dtlk.h> /* local header file for DoubleTalk values */
0068
0069 #ifdef TRACING
0070 #define TRACE_TEXT(str) printk(str);
0071 #define TRACE_RET printk(")")
0072 #else
0073 #define TRACE_TEXT(str) ((void) 0)
0074 #define TRACE_RET ((void) 0)
0075 #endif
0076
0077 static DEFINE_MUTEX(dtlk_mutex);
0078 static void dtlk_timer_tick(struct timer_list *unused);
0079
0080 static int dtlk_major;
0081 static int dtlk_port_lpc;
0082 static int dtlk_port_tts;
0083 static int dtlk_busy;
0084 static int dtlk_has_indexing;
0085 static unsigned int dtlk_portlist[] =
0086 {0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0};
0087 static wait_queue_head_t dtlk_process_list;
0088 static DEFINE_TIMER(dtlk_timer, dtlk_timer_tick);
0089
0090
0091 static ssize_t dtlk_read(struct file *, char __user *,
0092 size_t nbytes, loff_t * ppos);
0093 static ssize_t dtlk_write(struct file *, const char __user *,
0094 size_t nbytes, loff_t * ppos);
0095 static __poll_t dtlk_poll(struct file *, poll_table *);
0096 static int dtlk_open(struct inode *, struct file *);
0097 static int dtlk_release(struct inode *, struct file *);
0098 static long dtlk_ioctl(struct file *file,
0099 unsigned int cmd, unsigned long arg);
0100
0101 static const struct file_operations dtlk_fops =
0102 {
0103 .owner = THIS_MODULE,
0104 .read = dtlk_read,
0105 .write = dtlk_write,
0106 .poll = dtlk_poll,
0107 .unlocked_ioctl = dtlk_ioctl,
0108 .open = dtlk_open,
0109 .release = dtlk_release,
0110 .llseek = no_llseek,
0111 };
0112
0113
0114 static int dtlk_dev_probe(void);
0115 static struct dtlk_settings *dtlk_interrogate(void);
0116 static int dtlk_readable(void);
0117 static char dtlk_read_lpc(void);
0118 static char dtlk_read_tts(void);
0119 static int dtlk_writeable(void);
0120 static char dtlk_write_bytes(const char *buf, int n);
0121 static char dtlk_write_tts(char);
0122
0123
0124
0125
0126 static ssize_t dtlk_read(struct file *file, char __user *buf,
0127 size_t count, loff_t * ppos)
0128 {
0129 unsigned int minor = iminor(file_inode(file));
0130 char ch;
0131 int i = 0, retries;
0132
0133 TRACE_TEXT("(dtlk_read");
0134
0135
0136 if (minor != DTLK_MINOR || !dtlk_has_indexing)
0137 return -EINVAL;
0138
0139 for (retries = 0; retries < loops_per_jiffy; retries++) {
0140 while (i < count && dtlk_readable()) {
0141 ch = dtlk_read_lpc();
0142
0143 if (put_user(ch, buf++))
0144 return -EFAULT;
0145 i++;
0146 }
0147 if (i)
0148 return i;
0149 if (file->f_flags & O_NONBLOCK)
0150 break;
0151 msleep_interruptible(100);
0152 }
0153 if (retries == loops_per_jiffy)
0154 printk(KERN_ERR "dtlk_read times out\n");
0155 TRACE_RET;
0156 return -EAGAIN;
0157 }
0158
0159 static ssize_t dtlk_write(struct file *file, const char __user *buf,
0160 size_t count, loff_t * ppos)
0161 {
0162 int i = 0, retries = 0, ch;
0163
0164 TRACE_TEXT("(dtlk_write");
0165 #ifdef TRACING
0166 printk(" \"");
0167 {
0168 int i, ch;
0169 for (i = 0; i < count; i++) {
0170 if (get_user(ch, buf + i))
0171 return -EFAULT;
0172 if (' ' <= ch && ch <= '~')
0173 printk("%c", ch);
0174 else
0175 printk("\\%03o", ch);
0176 }
0177 printk("\"");
0178 }
0179 #endif
0180
0181 if (iminor(file_inode(file)) != DTLK_MINOR)
0182 return -EINVAL;
0183
0184 while (1) {
0185 while (i < count && !get_user(ch, buf) &&
0186 (ch == DTLK_CLEAR || dtlk_writeable())) {
0187 dtlk_write_tts(ch);
0188 buf++;
0189 i++;
0190 if (i % 5 == 0)
0191
0192
0193
0194
0195
0196 msleep_interruptible(1);
0197 else {
0198
0199
0200
0201
0202
0203 for (retries = 0;
0204 retries < loops_per_jiffy / (4000/HZ);
0205 retries++)
0206 if (inb_p(dtlk_port_tts) &
0207 TTS_WRITABLE)
0208 break;
0209 }
0210 retries = 0;
0211 }
0212 if (i == count)
0213 return i;
0214 if (file->f_flags & O_NONBLOCK)
0215 break;
0216
0217 msleep_interruptible(1);
0218
0219 if (++retries > 10 * HZ) {
0220
0221 printk("dtlk: write timeout. "
0222 "inb_p(dtlk_port_tts) = 0x%02x\n",
0223 inb_p(dtlk_port_tts));
0224 TRACE_RET;
0225 return -EBUSY;
0226 }
0227 }
0228 TRACE_RET;
0229 return -EAGAIN;
0230 }
0231
0232 static __poll_t dtlk_poll(struct file *file, poll_table * wait)
0233 {
0234 __poll_t mask = 0;
0235 unsigned long expires;
0236
0237 TRACE_TEXT(" dtlk_poll");
0238
0239
0240
0241
0242
0243
0244 poll_wait(file, &dtlk_process_list, wait);
0245
0246 if (dtlk_has_indexing && dtlk_readable()) {
0247 del_timer(&dtlk_timer);
0248 mask = EPOLLIN | EPOLLRDNORM;
0249 }
0250 if (dtlk_writeable()) {
0251 del_timer(&dtlk_timer);
0252 mask |= EPOLLOUT | EPOLLWRNORM;
0253 }
0254
0255
0256
0257 expires = jiffies + 3*HZ / 100;
0258 mod_timer(&dtlk_timer, expires);
0259
0260 return mask;
0261 }
0262
0263 static void dtlk_timer_tick(struct timer_list *unused)
0264 {
0265 TRACE_TEXT(" dtlk_timer_tick");
0266 wake_up_interruptible(&dtlk_process_list);
0267 }
0268
0269 static long dtlk_ioctl(struct file *file,
0270 unsigned int cmd,
0271 unsigned long arg)
0272 {
0273 char __user *argp = (char __user *)arg;
0274 struct dtlk_settings *sp;
0275 char portval;
0276 TRACE_TEXT(" dtlk_ioctl");
0277
0278 switch (cmd) {
0279
0280 case DTLK_INTERROGATE:
0281 mutex_lock(&dtlk_mutex);
0282 sp = dtlk_interrogate();
0283 mutex_unlock(&dtlk_mutex);
0284 if (copy_to_user(argp, sp, sizeof(struct dtlk_settings)))
0285 return -EINVAL;
0286 return 0;
0287
0288 case DTLK_STATUS:
0289 portval = inb_p(dtlk_port_tts);
0290 return put_user(portval, argp);
0291
0292 default:
0293 return -EINVAL;
0294 }
0295 }
0296
0297
0298 static int dtlk_open(struct inode *inode, struct file *file)
0299 {
0300 TRACE_TEXT("(dtlk_open");
0301
0302 switch (iminor(inode)) {
0303 case DTLK_MINOR:
0304 if (dtlk_busy)
0305 return -EBUSY;
0306 return stream_open(inode, file);
0307
0308 default:
0309 return -ENXIO;
0310 }
0311 }
0312
0313 static int dtlk_release(struct inode *inode, struct file *file)
0314 {
0315 TRACE_TEXT("(dtlk_release");
0316
0317 switch (iminor(inode)) {
0318 case DTLK_MINOR:
0319 break;
0320
0321 default:
0322 break;
0323 }
0324 TRACE_RET;
0325
0326 del_timer_sync(&dtlk_timer);
0327
0328 return 0;
0329 }
0330
0331 static int __init dtlk_init(void)
0332 {
0333 int err;
0334
0335 dtlk_port_lpc = 0;
0336 dtlk_port_tts = 0;
0337 dtlk_busy = 0;
0338 dtlk_major = register_chrdev(0, "dtlk", &dtlk_fops);
0339 if (dtlk_major < 0) {
0340 printk(KERN_ERR "DoubleTalk PC - cannot register device\n");
0341 return dtlk_major;
0342 }
0343 err = dtlk_dev_probe();
0344 if (err) {
0345 unregister_chrdev(dtlk_major, "dtlk");
0346 return err;
0347 }
0348 printk(", MAJOR %d\n", dtlk_major);
0349
0350 init_waitqueue_head(&dtlk_process_list);
0351
0352 return 0;
0353 }
0354
0355 static void __exit dtlk_cleanup (void)
0356 {
0357 dtlk_write_bytes("goodbye", 8);
0358 msleep_interruptible(500);
0359
0360
0361
0362
0363 dtlk_write_tts(DTLK_CLEAR);
0364 unregister_chrdev(dtlk_major, "dtlk");
0365 release_region(dtlk_port_lpc, DTLK_IO_EXTENT);
0366 }
0367
0368 module_init(dtlk_init);
0369 module_exit(dtlk_cleanup);
0370
0371
0372
0373 static int dtlk_readable(void)
0374 {
0375 #ifdef TRACING
0376 printk(" dtlk_readable=%u@%u", inb_p(dtlk_port_lpc) != 0x7f, jiffies);
0377 #endif
0378 return inb_p(dtlk_port_lpc) != 0x7f;
0379 }
0380
0381 static int dtlk_writeable(void)
0382 {
0383
0384 #ifdef TRACINGMORE
0385 printk(" dtlk_writeable=%u", (inb_p(dtlk_port_tts) & TTS_WRITABLE)!=0);
0386 #endif
0387 return inb_p(dtlk_port_tts) & TTS_WRITABLE;
0388 }
0389
0390 static int __init dtlk_dev_probe(void)
0391 {
0392 unsigned int testval = 0;
0393 int i = 0;
0394 struct dtlk_settings *sp;
0395
0396 if (dtlk_port_lpc | dtlk_port_tts)
0397 return -EBUSY;
0398
0399 for (i = 0; dtlk_portlist[i]; i++) {
0400 #if 0
0401 printk("DoubleTalk PC - Port %03x = %04x\n",
0402 dtlk_portlist[i], (testval = inw_p(dtlk_portlist[i])));
0403 #endif
0404
0405 if (!request_region(dtlk_portlist[i], DTLK_IO_EXTENT,
0406 "dtlk"))
0407 continue;
0408 testval = inw_p(dtlk_portlist[i]);
0409 if ((testval &= 0xfbff) == 0x107f) {
0410 dtlk_port_lpc = dtlk_portlist[i];
0411 dtlk_port_tts = dtlk_port_lpc + 1;
0412
0413 sp = dtlk_interrogate();
0414 printk("DoubleTalk PC at %03x-%03x, "
0415 "ROM version %s, serial number %u",
0416 dtlk_portlist[i], dtlk_portlist[i] +
0417 DTLK_IO_EXTENT - 1,
0418 sp->rom_version, sp->serial_number);
0419
0420
0421
0422 outb_p(0xff, dtlk_port_lpc);
0423
0424
0425 dtlk_write_bytes("\036\1@\0\0012I\r", 8);
0426
0427
0428
0429 msleep_interruptible(100);
0430 dtlk_has_indexing = dtlk_readable();
0431 #ifdef TRACING
0432 printk(", indexing %d\n", dtlk_has_indexing);
0433 #endif
0434 #ifdef INSCOPE
0435 {
0436
0437 #define LOOK \
0438 for (i = 0; i < 10; i++) \
0439 { \
0440 buffer[b++] = inb_p(dtlk_port_lpc); \
0441 __delay(loops_per_jiffy/(1000000/HZ)); \
0442 }
0443 char buffer[1000];
0444 int b = 0, i, j;
0445
0446 LOOK
0447 outb_p(0xff, dtlk_port_lpc);
0448 buffer[b++] = 0;
0449 LOOK
0450 dtlk_write_bytes("\0012I\r", 4);
0451 buffer[b++] = 0;
0452 __delay(50 * loops_per_jiffy / (1000/HZ));
0453 outb_p(0xff, dtlk_port_lpc);
0454 buffer[b++] = 0;
0455 LOOK
0456
0457 printk("\n");
0458 for (j = 0; j < b; j++)
0459 printk(" %02x", buffer[j]);
0460 printk("\n");
0461 }
0462 #endif
0463
0464 #ifdef OUTSCOPE
0465 {
0466
0467 #define LOOK \
0468 for (i = 0; i < 10; i++) \
0469 { \
0470 buffer[b++] = inb_p(dtlk_port_tts); \
0471 __delay(loops_per_jiffy/(1000000/HZ)); \
0472 }
0473 char buffer[1000];
0474 int b = 0, i, j;
0475
0476 mdelay(10);
0477 LOOK
0478 outb_p(0x03, dtlk_port_tts);
0479 buffer[b++] = 0;
0480 LOOK
0481 LOOK
0482
0483 printk("\n");
0484 for (j = 0; j < b; j++)
0485 printk(" %02x", buffer[j]);
0486 printk("\n");
0487 }
0488 #endif
0489
0490 dtlk_write_bytes("Double Talk found", 18);
0491
0492 return 0;
0493 }
0494 release_region(dtlk_portlist[i], DTLK_IO_EXTENT);
0495 }
0496
0497 printk(KERN_INFO "DoubleTalk PC - not found\n");
0498 return -ENODEV;
0499 }
0500
0501
0502
0503
0504
0505
0506
0507
0508
0509
0510
0511 static struct dtlk_settings *dtlk_interrogate(void)
0512 {
0513 unsigned char *t;
0514 static char buf[sizeof(struct dtlk_settings) + 1];
0515 int total, i;
0516 static struct dtlk_settings status;
0517 TRACE_TEXT("(dtlk_interrogate");
0518 dtlk_write_bytes("\030\001?", 3);
0519 for (total = 0, i = 0; i < 50; i++) {
0520 buf[total] = dtlk_read_tts();
0521 if (total > 2 && buf[total] == 0x7f)
0522 break;
0523 if (total < sizeof(struct dtlk_settings))
0524 total++;
0525 }
0526
0527
0528
0529
0530
0531
0532 t = buf;
0533 status.serial_number = t[0] + t[1] * 256;
0534
0535 t += 2;
0536
0537 i = 0;
0538 while (*t != '\r') {
0539 status.rom_version[i] = *t;
0540 if (i < sizeof(status.rom_version) - 1)
0541 i++;
0542 t++;
0543 }
0544 status.rom_version[i] = 0;
0545 t++;
0546
0547 status.mode = *t++;
0548 status.punc_level = *t++;
0549 status.formant_freq = *t++;
0550 status.pitch = *t++;
0551 status.speed = *t++;
0552 status.volume = *t++;
0553 status.tone = *t++;
0554 status.expression = *t++;
0555 status.ext_dict_loaded = *t++;
0556 status.ext_dict_status = *t++;
0557 status.free_ram = *t++;
0558 status.articulation = *t++;
0559 status.reverb = *t++;
0560 status.eob = *t++;
0561 status.has_indexing = dtlk_has_indexing;
0562 TRACE_RET;
0563 return &status;
0564 }
0565
0566 static char dtlk_read_tts(void)
0567 {
0568 int portval, retries = 0;
0569 char ch;
0570 TRACE_TEXT("(dtlk_read_tts");
0571
0572
0573 do {
0574 portval = inb_p(dtlk_port_tts);
0575 } while ((portval & TTS_READABLE) == 0 &&
0576 retries++ < DTLK_MAX_RETRIES);
0577 if (retries > DTLK_MAX_RETRIES)
0578 printk(KERN_ERR "dtlk_read_tts() timeout\n");
0579
0580 ch = inb_p(dtlk_port_tts);
0581 ch &= 0x7f;
0582 outb_p(ch, dtlk_port_tts);
0583
0584 retries = 0;
0585 do {
0586 portval = inb_p(dtlk_port_tts);
0587 } while ((portval & TTS_READABLE) != 0 &&
0588 retries++ < DTLK_MAX_RETRIES);
0589 if (retries > DTLK_MAX_RETRIES)
0590 printk(KERN_ERR "dtlk_read_tts() timeout\n");
0591
0592 TRACE_RET;
0593 return ch;
0594 }
0595
0596 static char dtlk_read_lpc(void)
0597 {
0598 int retries = 0;
0599 char ch;
0600 TRACE_TEXT("(dtlk_read_lpc");
0601
0602
0603
0604 ch = inb_p(dtlk_port_lpc);
0605
0606 outb_p(0xff, dtlk_port_lpc);
0607
0608
0609
0610
0611 retries = (loops_per_jiffy * 20) / (1000000/HZ);
0612 while (inb_p(dtlk_port_lpc) != 0x7f && --retries > 0);
0613 if (retries == 0)
0614 printk(KERN_ERR "dtlk_read_lpc() timeout\n");
0615
0616 TRACE_RET;
0617 return ch;
0618 }
0619
0620
0621 static char dtlk_write_bytes(const char *buf, int n)
0622 {
0623 char val = 0;
0624
0625 TRACE_TEXT("(dtlk_write_bytes");
0626 while (n-- > 0)
0627 val = dtlk_write_tts(*buf++);
0628 TRACE_RET;
0629 return val;
0630 }
0631
0632 static char dtlk_write_tts(char ch)
0633 {
0634 int retries = 0;
0635 #ifdef TRACINGMORE
0636 printk(" dtlk_write_tts(");
0637 if (' ' <= ch && ch <= '~')
0638 printk("'%c'", ch);
0639 else
0640 printk("0x%02x", ch);
0641 #endif
0642 if (ch != DTLK_CLEAR)
0643 while ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0 &&
0644 retries++ < DTLK_MAX_RETRIES)
0645 ;
0646 if (retries > DTLK_MAX_RETRIES)
0647 printk(KERN_ERR "dtlk_write_tts() timeout\n");
0648
0649 outb_p(ch, dtlk_port_tts);
0650
0651
0652
0653 for (retries = 0; retries < loops_per_jiffy / (100000/HZ); retries++)
0654 if ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0)
0655 break;
0656
0657 #ifdef TRACINGMORE
0658 printk(")\n");
0659 #endif
0660 return 0;
0661 }
0662
0663 MODULE_LICENSE("GPL");