root/drivers/staging/speakup/speakup_decpc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. dt_getstatus
  2. dt_sendcmd
  3. dt_waitbit
  4. dt_wait_dma
  5. dt_ctrl
  6. synth_flush
  7. dt_sendchar
  8. testkernel
  9. do_catch_up
  10. synth_immediate
  11. synth_probe
  12. dtpc_release

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * This is the DECtalk PC speakup driver
   4  *
   5  * Some constants from DEC's DOS driver:
   6  *      Copyright (c) by Digital Equipment Corp.
   7  *
   8  * 386BSD DECtalk PC driver:
   9  *      Copyright (c) 1996 Brian Buhrow <buhrow@lothlorien.nfbcal.org>
  10  *
  11  * Linux DECtalk PC driver:
  12  *      Copyright (c) 1997 Nicolas Pitre <nico@cam.org>
  13  *
  14  * speakup DECtalk PC Internal driver:
  15  *      Copyright (c) 2003 David Borowski <david575@golden.net>
  16  *
  17  * All rights reserved.
  18  */
  19 #include <linux/jiffies.h>
  20 #include <linux/sched.h>
  21 #include <linux/timer.h>
  22 #include <linux/kthread.h>
  23 
  24 #include "spk_priv.h"
  25 #include "speakup.h"
  26 
  27 #define MODULE_init             0x0dec  /* module in boot code */
  28 #define MODULE_self_test        0x8800  /* module in self-test */
  29 #define MODULE_reset            0xffff  /* reinit the whole module */
  30 
  31 #define MODE_mask               0xf000  /* mode bits in high nibble */
  32 #define MODE_null               0x0000
  33 #define MODE_test               0x2000  /* in testing mode */
  34 #define MODE_status             0x8000
  35 #define STAT_int                0x0001  /* running in interrupt mode */
  36 #define STAT_tr_char            0x0002  /* character data to transmit */
  37 #define STAT_rr_char            0x0004  /* ready to receive char data */
  38 #define STAT_cmd_ready          0x0008  /* ready to accept commands */
  39 #define STAT_dma_ready          0x0010  /* dma command ready */
  40 #define STAT_digitized          0x0020  /* spc in digitized mode */
  41 #define STAT_new_index          0x0040  /* new last index ready */
  42 #define STAT_new_status         0x0080  /* new status posted */
  43 #define STAT_dma_state          0x0100  /* dma state toggle */
  44 #define STAT_index_valid        0x0200  /* indexs are valid */
  45 #define STAT_flushing           0x0400  /* flush in progress */
  46 #define STAT_self_test          0x0800  /* module in self test */
  47 #define MODE_ready              0xc000  /* module ready for next phase */
  48 #define READY_boot              0x0000
  49 #define READY_kernel            0x0001
  50 #define MODE_error              0xf000
  51 
  52 #define CMD_mask                0xf000  /* mask for command nibble */
  53 #define CMD_null                0x0000  /* post status */
  54 #define CMD_control             0x1000  /* hard control command */
  55 #define CTRL_mask               0x0F00  /* mask off control nibble */
  56 #define CTRL_data               0x00FF  /* mask to get data byte */
  57 #define CTRL_null               0x0000  /* null control */
  58 #define CTRL_vol_up             0x0100  /* increase volume */
  59 #define CTRL_vol_down           0x0200  /* decrease volume */
  60 #define CTRL_vol_set            0x0300  /* set volume */
  61 #define CTRL_pause              0x0400  /* pause spc */
  62 #define CTRL_resume             0x0500  /* resume spc clock */
  63 #define CTRL_resume_spc         0x0001  /* resume spc soft pause */
  64 #define CTRL_flush              0x0600  /* flush all buffers */
  65 #define CTRL_int_enable         0x0700  /* enable status change ints */
  66 #define CTRL_buff_free          0x0800  /* buffer remain count */
  67 #define CTRL_buff_used          0x0900  /* buffer in use */
  68 #define CTRL_speech             0x0a00  /* immediate speech change */
  69 #define CTRL_SP_voice           0x0001  /* voice change */
  70 #define CTRL_SP_rate            0x0002  /* rate change */
  71 #define CTRL_SP_comma           0x0003  /* comma pause change */
  72 #define CTRL_SP_period          0x0004  /* period pause change */
  73 #define CTRL_SP_rate_delta      0x0005  /* delta rate change */
  74 #define CTRL_SP_get_param       0x0006  /* return the desired parameter */
  75 #define CTRL_last_index         0x0b00  /* get last index spoken */
  76 #define CTRL_io_priority        0x0c00  /* change i/o priority */
  77 #define CTRL_free_mem           0x0d00  /* get free paragraphs on module */
  78 #define CTRL_get_lang           0x0e00  /* return bitmask of loaded languages */
  79 #define CMD_test                0x2000  /* self-test request */
  80 #define TEST_mask               0x0F00  /* isolate test field */
  81 #define TEST_null               0x0000  /* no test requested */
  82 #define TEST_isa_int            0x0100  /* assert isa irq */
  83 #define TEST_echo               0x0200  /* make data in == data out */
  84 #define TEST_seg                0x0300  /* set peek/poke segment */
  85 #define TEST_off                0x0400  /* set peek/poke offset */
  86 #define TEST_peek               0x0500  /* data out == *peek */
  87 #define TEST_poke               0x0600  /* *peek == data in */
  88 #define TEST_sub_code           0x00FF  /* user defined test sub codes */
  89 #define CMD_id                  0x3000  /* return software id */
  90 #define ID_null                 0x0000  /* null id */
  91 #define ID_kernel               0x0100  /* kernel code executing */
  92 #define ID_boot                 0x0200  /* boot code executing */
  93 #define CMD_dma                 0x4000  /* force a dma start */
  94 #define CMD_reset               0x5000  /* reset module status */
  95 #define CMD_sync                0x6000  /* kernel sync command */
  96 #define CMD_char_in             0x7000  /* single character send */
  97 #define CMD_char_out            0x8000  /* single character get */
  98 #define CHAR_count_1            0x0100  /* one char in cmd_low */
  99 #define CHAR_count_2            0x0200  /* the second in data_low */
 100 #define CHAR_count_3            0x0300  /* the third in data_high */
 101 #define CMD_spc_mode            0x9000  /* change spc mode */
 102 #define CMD_spc_to_text         0x0100  /* set to text mode */
 103 #define CMD_spc_to_digit        0x0200  /* set to digital mode */
 104 #define CMD_spc_rate            0x0400  /* change spc data rate */
 105 #define CMD_error               0xf000  /* severe error */
 106 
 107 enum {  PRIMARY_DIC     = 0, USER_DIC, COMMAND_DIC, ABBREV_DIC };
 108 
 109 #define DMA_single_in           0x01
 110 #define DMA_single_out          0x02
 111 #define DMA_buff_in             0x03
 112 #define DMA_buff_out            0x04
 113 #define DMA_control             0x05
 114 #define DT_MEM_ALLOC            0x03
 115 #define DT_SET_DIC              0x04
 116 #define DT_START_TASK           0x05
 117 #define DT_LOAD_MEM             0x06
 118 #define DT_READ_MEM             0x07
 119 #define DT_DIGITAL_IN           0x08
 120 #define DMA_sync                0x06
 121 #define DMA_sync_char           0x07
 122 
 123 #define DRV_VERSION "2.12"
 124 #define PROCSPEECH 0x0b
 125 #define SYNTH_IO_EXTENT 8
 126 
 127 static int synth_probe(struct spk_synth *synth);
 128 static void dtpc_release(void);
 129 static const char *synth_immediate(struct spk_synth *synth, const char *buf);
 130 static void do_catch_up(struct spk_synth *synth);
 131 static void synth_flush(struct spk_synth *synth);
 132 
 133 static int synth_portlist[] = { 0x340, 0x350, 0x240, 0x250, 0 };
 134 static int in_escape, is_flushing;
 135 static int dt_stat, dma_state;
 136 
 137 static struct var_t vars[] = {
 138         { CAPS_START, .u.s = {"[:dv ap 200]" } },
 139         { CAPS_STOP, .u.s = {"[:dv ap 100]" } },
 140         { RATE, .u.n = {"[:ra %d]", 9, 0, 18, 150, 25, NULL } },
 141         { PITCH, .u.n = {"[:dv ap %d]", 80, 0, 100, 20, 0, NULL } },
 142         { VOL, .u.n = {"[:vo se %d]", 5, 0, 9, 5, 10, NULL } },
 143         { PUNCT, .u.n = {"[:pu %c]", 0, 0, 2, 0, 0, "nsa" } },
 144         { VOICE, .u.n = {"[:n%c]", 0, 0, 9, 0, 0, "phfdburwkv" } },
 145         { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
 146         V_LAST_VAR
 147 };
 148 
 149 /*
 150  * These attributes will appear in /sys/accessibility/speakup/decpc.
 151  */
 152 static struct kobj_attribute caps_start_attribute =
 153         __ATTR(caps_start, 0644, spk_var_show, spk_var_store);
 154 static struct kobj_attribute caps_stop_attribute =
 155         __ATTR(caps_stop, 0644, spk_var_show, spk_var_store);
 156 static struct kobj_attribute pitch_attribute =
 157         __ATTR(pitch, 0644, spk_var_show, spk_var_store);
 158 static struct kobj_attribute punct_attribute =
 159         __ATTR(punct, 0644, spk_var_show, spk_var_store);
 160 static struct kobj_attribute rate_attribute =
 161         __ATTR(rate, 0644, spk_var_show, spk_var_store);
 162 static struct kobj_attribute voice_attribute =
 163         __ATTR(voice, 0644, spk_var_show, spk_var_store);
 164 static struct kobj_attribute vol_attribute =
 165         __ATTR(vol, 0644, spk_var_show, spk_var_store);
 166 
 167 static struct kobj_attribute delay_time_attribute =
 168         __ATTR(delay_time, 0644, spk_var_show, spk_var_store);
 169 static struct kobj_attribute direct_attribute =
 170         __ATTR(direct, 0644, spk_var_show, spk_var_store);
 171 static struct kobj_attribute full_time_attribute =
 172         __ATTR(full_time, 0644, spk_var_show, spk_var_store);
 173 static struct kobj_attribute jiffy_delta_attribute =
 174         __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store);
 175 static struct kobj_attribute trigger_time_attribute =
 176         __ATTR(trigger_time, 0644, spk_var_show, spk_var_store);
 177 
 178 /*
 179  * Create a group of attributes so that we can create and destroy them all
 180  * at once.
 181  */
 182 static struct attribute *synth_attrs[] = {
 183         &caps_start_attribute.attr,
 184         &caps_stop_attribute.attr,
 185         &pitch_attribute.attr,
 186         &punct_attribute.attr,
 187         &rate_attribute.attr,
 188         &voice_attribute.attr,
 189         &vol_attribute.attr,
 190         &delay_time_attribute.attr,
 191         &direct_attribute.attr,
 192         &full_time_attribute.attr,
 193         &jiffy_delta_attribute.attr,
 194         &trigger_time_attribute.attr,
 195         NULL,   /* need to NULL terminate the list of attributes */
 196 };
 197 
 198 static struct spk_synth synth_dec_pc = {
 199         .name = "decpc",
 200         .version = DRV_VERSION,
 201         .long_name = "Dectalk PC",
 202         .init = "[:pe -380]",
 203         .procspeech = PROCSPEECH,
 204         .delay = 500,
 205         .trigger = 50,
 206         .jiffies = 50,
 207         .full = 1000,
 208         .flags = SF_DEC,
 209         .startup = SYNTH_START,
 210         .checkval = SYNTH_CHECK,
 211         .vars = vars,
 212         .io_ops = &spk_serial_io_ops,
 213         .probe = synth_probe,
 214         .release = dtpc_release,
 215         .synth_immediate = synth_immediate,
 216         .catch_up = do_catch_up,
 217         .flush = synth_flush,
 218         .is_alive = spk_synth_is_alive_nop,
 219         .synth_adjust = NULL,
 220         .read_buff_add = NULL,
 221         .get_index = NULL,
 222         .indexing = {
 223                 .command = NULL,
 224                 .lowindex = 0,
 225                 .highindex = 0,
 226                 .currindex = 0,
 227         },
 228         .attributes = {
 229                 .attrs = synth_attrs,
 230                 .name = "decpc",
 231         },
 232 };
 233 
 234 static int dt_getstatus(void)
 235 {
 236         dt_stat = inb_p(speakup_info.port_tts) |
 237                  (inb_p(speakup_info.port_tts + 1) << 8);
 238         return dt_stat;
 239 }
 240 
 241 static void dt_sendcmd(u_int cmd)
 242 {
 243         outb_p(cmd & 0xFF, speakup_info.port_tts);
 244         outb_p((cmd >> 8) & 0xFF, speakup_info.port_tts + 1);
 245 }
 246 
 247 static int dt_waitbit(int bit)
 248 {
 249         int timeout = 100;
 250 
 251         while (--timeout > 0) {
 252                 if ((dt_getstatus() & bit) == bit)
 253                         return 1;
 254                 udelay(50);
 255         }
 256         return 0;
 257 }
 258 
 259 static int dt_wait_dma(void)
 260 {
 261         int timeout = 100, state = dma_state;
 262 
 263         if (!dt_waitbit(STAT_dma_ready))
 264                 return 0;
 265         while (--timeout > 0) {
 266                 if ((dt_getstatus() & STAT_dma_state) == state)
 267                         return 1;
 268                 udelay(50);
 269         }
 270         dma_state = dt_getstatus() & STAT_dma_state;
 271         return 1;
 272 }
 273 
 274 static int dt_ctrl(u_int cmd)
 275 {
 276         int timeout = 10;
 277 
 278         if (!dt_waitbit(STAT_cmd_ready))
 279                 return -1;
 280         outb_p(0, speakup_info.port_tts + 2);
 281         outb_p(0, speakup_info.port_tts + 3);
 282         dt_getstatus();
 283         dt_sendcmd(CMD_control | cmd);
 284         outb_p(0, speakup_info.port_tts + 6);
 285         while (dt_getstatus() & STAT_cmd_ready) {
 286                 udelay(20);
 287                 if (--timeout == 0)
 288                         break;
 289         }
 290         dt_sendcmd(CMD_null);
 291         return 0;
 292 }
 293 
 294 static void synth_flush(struct spk_synth *synth)
 295 {
 296         int timeout = 10;
 297 
 298         if (is_flushing)
 299                 return;
 300         is_flushing = 4;
 301         in_escape = 0;
 302         while (dt_ctrl(CTRL_flush)) {
 303                 if (--timeout == 0)
 304                         break;
 305                 udelay(50);
 306         }
 307         for (timeout = 0; timeout < 10; timeout++) {
 308                 if (dt_waitbit(STAT_dma_ready))
 309                         break;
 310                 udelay(50);
 311         }
 312         outb_p(DMA_sync, speakup_info.port_tts + 4);
 313         outb_p(0, speakup_info.port_tts + 4);
 314         udelay(100);
 315         for (timeout = 0; timeout < 10; timeout++) {
 316                 if (!(dt_getstatus() & STAT_flushing))
 317                         break;
 318                 udelay(50);
 319         }
 320         dma_state = dt_getstatus() & STAT_dma_state;
 321         dma_state ^= STAT_dma_state;
 322         is_flushing = 0;
 323 }
 324 
 325 static int dt_sendchar(char ch)
 326 {
 327         if (!dt_wait_dma())
 328                 return -1;
 329         if (!(dt_stat & STAT_rr_char))
 330                 return -2;
 331         outb_p(DMA_single_in, speakup_info.port_tts + 4);
 332         outb_p(ch, speakup_info.port_tts + 4);
 333         dma_state ^= STAT_dma_state;
 334         return 0;
 335 }
 336 
 337 static int testkernel(void)
 338 {
 339         int status = 0;
 340 
 341         if (dt_getstatus() == 0xffff) {
 342                 status = -1;
 343                 goto oops;
 344         }
 345         dt_sendcmd(CMD_sync);
 346         if (!dt_waitbit(STAT_cmd_ready))
 347                 status = -2;
 348         else if (dt_stat & 0x8000)
 349                 return 0;
 350         else if (dt_stat == 0x0dec)
 351                 pr_warn("dec_pc at 0x%x, software not loaded\n",
 352                         speakup_info.port_tts);
 353         status = -3;
 354 oops:   synth_release_region(speakup_info.port_tts, SYNTH_IO_EXTENT);
 355         speakup_info.port_tts = 0;
 356         return status;
 357 }
 358 
 359 static void do_catch_up(struct spk_synth *synth)
 360 {
 361         u_char ch;
 362         static u_char last;
 363         unsigned long flags;
 364         unsigned long jiff_max;
 365         struct var_t *jiffy_delta;
 366         struct var_t *delay_time;
 367         int jiffy_delta_val;
 368         int delay_time_val;
 369 
 370         jiffy_delta = spk_get_var(JIFFY);
 371         delay_time = spk_get_var(DELAY);
 372         spin_lock_irqsave(&speakup_info.spinlock, flags);
 373         jiffy_delta_val = jiffy_delta->u.n.value;
 374         spin_unlock_irqrestore(&speakup_info.spinlock, flags);
 375         jiff_max = jiffies + jiffy_delta_val;
 376 
 377         while (!kthread_should_stop()) {
 378                 spin_lock_irqsave(&speakup_info.spinlock, flags);
 379                 if (speakup_info.flushing) {
 380                         speakup_info.flushing = 0;
 381                         spin_unlock_irqrestore(&speakup_info.spinlock, flags);
 382                         synth->flush(synth);
 383                         continue;
 384                 }
 385                 synth_buffer_skip_nonlatin1();
 386                 if (synth_buffer_empty()) {
 387                         spin_unlock_irqrestore(&speakup_info.spinlock, flags);
 388                         break;
 389                 }
 390                 ch = synth_buffer_peek();
 391                 set_current_state(TASK_INTERRUPTIBLE);
 392                 delay_time_val = delay_time->u.n.value;
 393                 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
 394                 if (ch == '\n')
 395                         ch = 0x0D;
 396                 if (dt_sendchar(ch)) {
 397                         schedule_timeout(msecs_to_jiffies(delay_time_val));
 398                         continue;
 399                 }
 400                 set_current_state(TASK_RUNNING);
 401                 spin_lock_irqsave(&speakup_info.spinlock, flags);
 402                 synth_buffer_getc();
 403                 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
 404                 if (ch == '[') {
 405                         in_escape = 1;
 406                 } else if (ch == ']') {
 407                         in_escape = 0;
 408                 } else if (ch <= SPACE) {
 409                         if (!in_escape && strchr(",.!?;:", last))
 410                                 dt_sendchar(PROCSPEECH);
 411                         if (time_after_eq(jiffies, jiff_max)) {
 412                                 if (!in_escape)
 413                                         dt_sendchar(PROCSPEECH);
 414                                 spin_lock_irqsave(&speakup_info.spinlock,
 415                                                   flags);
 416                                 jiffy_delta_val = jiffy_delta->u.n.value;
 417                                 delay_time_val = delay_time->u.n.value;
 418                                 spin_unlock_irqrestore(&speakup_info.spinlock,
 419                                                        flags);
 420                                 schedule_timeout(msecs_to_jiffies
 421                                                  (delay_time_val));
 422                                 jiff_max = jiffies + jiffy_delta_val;
 423                         }
 424                 }
 425                 last = ch;
 426                 ch = 0;
 427         }
 428         if (!in_escape)
 429                 dt_sendchar(PROCSPEECH);
 430 }
 431 
 432 static const char *synth_immediate(struct spk_synth *synth, const char *buf)
 433 {
 434         u_char ch;
 435 
 436         while ((ch = *buf)) {
 437                 if (ch == '\n')
 438                         ch = PROCSPEECH;
 439                 if (dt_sendchar(ch))
 440                         return buf;
 441                 buf++;
 442         }
 443         return NULL;
 444 }
 445 
 446 static int synth_probe(struct spk_synth *synth)
 447 {
 448         int i = 0, failed = 0;
 449 
 450         pr_info("Probing for %s.\n", synth->long_name);
 451         for (i = 0; synth_portlist[i]; i++) {
 452                 if (synth_request_region(synth_portlist[i], SYNTH_IO_EXTENT)) {
 453                         pr_warn("request_region: failed with 0x%x, %d\n",
 454                                 synth_portlist[i], SYNTH_IO_EXTENT);
 455                         continue;
 456                 }
 457                 speakup_info.port_tts = synth_portlist[i];
 458                 failed = testkernel();
 459                 if (failed == 0)
 460                         break;
 461         }
 462         if (failed) {
 463                 pr_info("%s: not found\n", synth->long_name);
 464                 return -ENODEV;
 465         }
 466         pr_info("%s: %03x-%03x, Driver Version %s,\n", synth->long_name,
 467                 speakup_info.port_tts, speakup_info.port_tts + 7,
 468                 synth->version);
 469         synth->alive = 1;
 470         return 0;
 471 }
 472 
 473 static void dtpc_release(void)
 474 {
 475         spk_stop_serial_interrupt();
 476         if (speakup_info.port_tts)
 477                 synth_release_region(speakup_info.port_tts, SYNTH_IO_EXTENT);
 478         speakup_info.port_tts = 0;
 479 }
 480 
 481 module_param_named(start, synth_dec_pc.startup, short, 0444);
 482 
 483 MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
 484 
 485 module_spk_synth(synth_dec_pc);
 486 
 487 MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
 488 MODULE_AUTHOR("David Borowski");
 489 MODULE_DESCRIPTION("Speakup support for DECtalk PC synthesizers");
 490 MODULE_LICENSE("GPL");
 491 MODULE_VERSION(DRV_VERSION);

/* [<][>][^][v][top][bottom][index][help] */