Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * written by: Kirk Reiser <kirk@braille.uwo.ca>
0004  * this version considerably modified by David Borowski, david575@rogers.com
0005  *
0006  * Copyright (C) 1998-99  Kirk Reiser.
0007  * Copyright (C) 2003 David Borowski.
0008  *
0009  * this code is specifically written as a driver for the speakup screenreview
0010  * package and is not a general device driver.
0011  * This driver is for the Aicom Acent PC internal synthesizer.
0012  */
0013 
0014 #include <linux/jiffies.h>
0015 #include <linux/sched.h>
0016 #include <linux/timer.h>
0017 #include <linux/kthread.h>
0018 
0019 #include "spk_priv.h"
0020 #include "serialio.h"
0021 #include "speakup.h"
0022 #include "speakup_acnt.h" /* local header file for Accent values */
0023 
0024 #define DRV_VERSION "2.10"
0025 #define PROCSPEECH '\r'
0026 
0027 static int synth_probe(struct spk_synth *synth);
0028 static void accent_release(struct spk_synth *synth);
0029 static const char *synth_immediate(struct spk_synth *synth, const char *buf);
0030 static void do_catch_up(struct spk_synth *synth);
0031 static void synth_flush(struct spk_synth *synth);
0032 
0033 static int synth_port_control;
0034 static int port_forced;
0035 static unsigned int synth_portlist[] = { 0x2a8, 0 };
0036 
0037 static struct var_t vars[] = {
0038     { CAPS_START, .u.s = {"\033P8" } },
0039     { CAPS_STOP, .u.s = {"\033P5" } },
0040     { RATE, .u.n = {"\033R%c", 9, 0, 17, 0, 0, "0123456789abcdefgh" } },
0041     { PITCH, .u.n = {"\033P%d", 5, 0, 9, 0, 0, NULL } },
0042     { VOL, .u.n = {"\033A%d", 5, 0, 9, 0, 0, NULL } },
0043     { TONE, .u.n = {"\033V%d", 5, 0, 9, 0, 0, NULL } },
0044     { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
0045     V_LAST_VAR
0046 };
0047 
0048 /*
0049  * These attributes will appear in /sys/accessibility/speakup/acntpc.
0050  */
0051 static struct kobj_attribute caps_start_attribute =
0052     __ATTR(caps_start, 0644, spk_var_show, spk_var_store);
0053 static struct kobj_attribute caps_stop_attribute =
0054     __ATTR(caps_stop, 0644, spk_var_show, spk_var_store);
0055 static struct kobj_attribute pitch_attribute =
0056     __ATTR(pitch, 0644, spk_var_show, spk_var_store);
0057 static struct kobj_attribute rate_attribute =
0058     __ATTR(rate, 0644, spk_var_show, spk_var_store);
0059 static struct kobj_attribute tone_attribute =
0060     __ATTR(tone, 0644, spk_var_show, spk_var_store);
0061 static struct kobj_attribute vol_attribute =
0062     __ATTR(vol, 0644, spk_var_show, spk_var_store);
0063 
0064 static struct kobj_attribute delay_time_attribute =
0065     __ATTR(delay_time, 0644, spk_var_show, spk_var_store);
0066 static struct kobj_attribute direct_attribute =
0067     __ATTR(direct, 0644, spk_var_show, spk_var_store);
0068 static struct kobj_attribute full_time_attribute =
0069     __ATTR(full_time, 0644, spk_var_show, spk_var_store);
0070 static struct kobj_attribute jiffy_delta_attribute =
0071     __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store);
0072 static struct kobj_attribute trigger_time_attribute =
0073     __ATTR(trigger_time, 0644, spk_var_show, spk_var_store);
0074 
0075 /*
0076  * Create a group of attributes so that we can create and destroy them all
0077  * at once.
0078  */
0079 static struct attribute *synth_attrs[] = {
0080     &caps_start_attribute.attr,
0081     &caps_stop_attribute.attr,
0082     &pitch_attribute.attr,
0083     &rate_attribute.attr,
0084     &tone_attribute.attr,
0085     &vol_attribute.attr,
0086     &delay_time_attribute.attr,
0087     &direct_attribute.attr,
0088     &full_time_attribute.attr,
0089     &jiffy_delta_attribute.attr,
0090     &trigger_time_attribute.attr,
0091     NULL,   /* need to NULL terminate the list of attributes */
0092 };
0093 
0094 static struct spk_synth synth_acntpc = {
0095     .name = "acntpc",
0096     .version = DRV_VERSION,
0097     .long_name = "Accent PC",
0098     .init = "\033=X \033Oi\033T2\033=M\033N1\n",
0099     .procspeech = PROCSPEECH,
0100     .clear = SYNTH_CLEAR,
0101     .delay = 500,
0102     .trigger = 50,
0103     .jiffies = 50,
0104     .full = 1000,
0105     .startup = SYNTH_START,
0106     .checkval = SYNTH_CHECK,
0107     .vars = vars,
0108     .io_ops = &spk_serial_io_ops,
0109     .probe = synth_probe,
0110     .release = accent_release,
0111     .synth_immediate = synth_immediate,
0112     .catch_up = do_catch_up,
0113     .flush = synth_flush,
0114     .is_alive = spk_synth_is_alive_nop,
0115     .synth_adjust = NULL,
0116     .read_buff_add = NULL,
0117     .get_index = NULL,
0118     .indexing = {
0119         .command = NULL,
0120         .lowindex = 0,
0121         .highindex = 0,
0122         .currindex = 0,
0123     },
0124     .attributes = {
0125         .attrs = synth_attrs,
0126         .name = "acntpc",
0127     },
0128 };
0129 
0130 static inline bool synth_writable(void)
0131 {
0132     return inb_p(synth_port_control) & SYNTH_WRITABLE;
0133 }
0134 
0135 static inline bool synth_full(void)
0136 {
0137     return inb_p(speakup_info.port_tts + UART_RX) == 'F';
0138 }
0139 
0140 static const char *synth_immediate(struct spk_synth *synth, const char *buf)
0141 {
0142     u_char ch;
0143 
0144     while ((ch = *buf)) {
0145         int timeout = SPK_XMITR_TIMEOUT;
0146 
0147         if (ch == '\n')
0148             ch = PROCSPEECH;
0149         if (synth_full())
0150             return buf;
0151         while (synth_writable()) {
0152             if (!--timeout)
0153                 return buf;
0154             udelay(1);
0155         }
0156         outb_p(ch, speakup_info.port_tts);
0157         buf++;
0158     }
0159     return NULL;
0160 }
0161 
0162 static void do_catch_up(struct spk_synth *synth)
0163 {
0164     u_char ch;
0165     unsigned long flags;
0166     unsigned long jiff_max;
0167     int timeout;
0168     int delay_time_val;
0169     int jiffy_delta_val;
0170     int full_time_val;
0171     struct var_t *delay_time;
0172     struct var_t *full_time;
0173     struct var_t *jiffy_delta;
0174 
0175     jiffy_delta = spk_get_var(JIFFY);
0176     delay_time = spk_get_var(DELAY);
0177     full_time = spk_get_var(FULL);
0178 
0179     spin_lock_irqsave(&speakup_info.spinlock, flags);
0180     jiffy_delta_val = jiffy_delta->u.n.value;
0181     spin_unlock_irqrestore(&speakup_info.spinlock, flags);
0182 
0183     jiff_max = jiffies + jiffy_delta_val;
0184     while (!kthread_should_stop()) {
0185         spin_lock_irqsave(&speakup_info.spinlock, flags);
0186         if (speakup_info.flushing) {
0187             speakup_info.flushing = 0;
0188             spin_unlock_irqrestore(&speakup_info.spinlock, flags);
0189             synth->flush(synth);
0190             continue;
0191         }
0192         synth_buffer_skip_nonlatin1();
0193         if (synth_buffer_empty()) {
0194             spin_unlock_irqrestore(&speakup_info.spinlock, flags);
0195             break;
0196         }
0197         set_current_state(TASK_INTERRUPTIBLE);
0198         full_time_val = full_time->u.n.value;
0199         spin_unlock_irqrestore(&speakup_info.spinlock, flags);
0200         if (synth_full()) {
0201             schedule_timeout(msecs_to_jiffies(full_time_val));
0202             continue;
0203         }
0204         set_current_state(TASK_RUNNING);
0205         timeout = SPK_XMITR_TIMEOUT;
0206         while (synth_writable()) {
0207             if (!--timeout)
0208                 break;
0209             udelay(1);
0210         }
0211         spin_lock_irqsave(&speakup_info.spinlock, flags);
0212         ch = synth_buffer_getc();
0213         spin_unlock_irqrestore(&speakup_info.spinlock, flags);
0214         if (ch == '\n')
0215             ch = PROCSPEECH;
0216         outb_p(ch, speakup_info.port_tts);
0217         if (time_after_eq(jiffies, jiff_max) && ch == SPACE) {
0218             timeout = SPK_XMITR_TIMEOUT;
0219             while (synth_writable()) {
0220                 if (!--timeout)
0221                     break;
0222                 udelay(1);
0223             }
0224             outb_p(PROCSPEECH, speakup_info.port_tts);
0225             spin_lock_irqsave(&speakup_info.spinlock, flags);
0226             jiffy_delta_val = jiffy_delta->u.n.value;
0227             delay_time_val = delay_time->u.n.value;
0228             spin_unlock_irqrestore(&speakup_info.spinlock, flags);
0229             schedule_timeout(msecs_to_jiffies(delay_time_val));
0230             jiff_max = jiffies + jiffy_delta_val;
0231         }
0232     }
0233     timeout = SPK_XMITR_TIMEOUT;
0234     while (synth_writable()) {
0235         if (!--timeout)
0236             break;
0237         udelay(1);
0238     }
0239     outb_p(PROCSPEECH, speakup_info.port_tts);
0240 }
0241 
0242 static void synth_flush(struct spk_synth *synth)
0243 {
0244     outb_p(SYNTH_CLEAR, speakup_info.port_tts);
0245 }
0246 
0247 static int synth_probe(struct spk_synth *synth)
0248 {
0249     unsigned int port_val = 0;
0250     int i;
0251 
0252     pr_info("Probing for %s.\n", synth->long_name);
0253     if (port_forced) {
0254         speakup_info.port_tts = port_forced;
0255         pr_info("probe forced to %x by kernel command line\n",
0256             speakup_info.port_tts);
0257         if (synth_request_region(speakup_info.port_tts - 1,
0258                      SYNTH_IO_EXTENT)) {
0259             pr_warn("sorry, port already reserved\n");
0260             return -EBUSY;
0261         }
0262         port_val = inw(speakup_info.port_tts - 1);
0263         synth_port_control = speakup_info.port_tts - 1;
0264     } else {
0265         for (i = 0; synth_portlist[i]; i++) {
0266             if (synth_request_region(synth_portlist[i],
0267                          SYNTH_IO_EXTENT)) {
0268                 pr_warn
0269                     ("request_region: failed with 0x%x, %d\n",
0270                      synth_portlist[i], SYNTH_IO_EXTENT);
0271                 continue;
0272             }
0273             port_val = inw(synth_portlist[i]) & 0xfffc;
0274             if (port_val == 0x53fc) {
0275                 /* 'S' and out&input bits */
0276                 synth_port_control = synth_portlist[i];
0277                 speakup_info.port_tts = synth_port_control + 1;
0278                 break;
0279             }
0280         }
0281     }
0282     port_val &= 0xfffc;
0283     if (port_val != 0x53fc) {
0284         /* 'S' and out&input bits */
0285         pr_info("%s: not found\n", synth->long_name);
0286         synth_release_region(synth_port_control, SYNTH_IO_EXTENT);
0287         synth_port_control = 0;
0288         return -ENODEV;
0289     }
0290     pr_info("%s: %03x-%03x, driver version %s,\n", synth->long_name,
0291         synth_port_control, synth_port_control + SYNTH_IO_EXTENT - 1,
0292         synth->version);
0293     synth->alive = 1;
0294     return 0;
0295 }
0296 
0297 static void accent_release(struct spk_synth *synth)
0298 {
0299     spk_stop_serial_interrupt();
0300     if (speakup_info.port_tts)
0301         synth_release_region(speakup_info.port_tts - 1,
0302                      SYNTH_IO_EXTENT);
0303     speakup_info.port_tts = 0;
0304 }
0305 
0306 module_param_hw_named(port, port_forced, int, ioport, 0444);
0307 module_param_named(start, synth_acntpc.startup, short, 0444);
0308 
0309 MODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing).");
0310 MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
0311 
0312 module_spk_synth(synth_acntpc);
0313 
0314 MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
0315 MODULE_AUTHOR("David Borowski");
0316 MODULE_DESCRIPTION("Speakup support for Accent PC synthesizer");
0317 MODULE_LICENSE("GPL");
0318 MODULE_VERSION(DRV_VERSION);
0319