Path: blob/master/drivers/accessibility/speakup/speakup_acntpc.c
26282 views
// SPDX-License-Identifier: GPL-2.0+1/*2* written by: Kirk Reiser <[email protected]>3* this version considerably modified by David Borowski, [email protected]4*5* Copyright (C) 1998-99 Kirk Reiser.6* Copyright (C) 2003 David Borowski.7*8* this code is specifically written as a driver for the speakup screenreview9* package and is not a general device driver.10* This driver is for the Aicom Acent PC internal synthesizer.11*/1213#include <linux/jiffies.h>14#include <linux/sched.h>15#include <linux/timer.h>16#include <linux/kthread.h>1718#include "spk_priv.h"19#include "serialio.h"20#include "speakup.h"21#include "speakup_acnt.h" /* local header file for Accent values */2223#define DRV_VERSION "2.10"24#define PROCSPEECH '\r'2526static int synth_probe(struct spk_synth *synth);27static void accent_release(struct spk_synth *synth);28static const char *synth_immediate(struct spk_synth *synth, const char *buf);29static void do_catch_up(struct spk_synth *synth);30static void synth_flush(struct spk_synth *synth);3132static int synth_port_control;33static int port_forced;34static unsigned int synth_portlist[] = { 0x2a8, 0 };3536enum default_vars_id {37CAPS_START_ID = 0, CAPS_STOP_ID,38RATE_ID, PITCH_ID,39VOL_ID, TONE_ID,40DIRECT_ID, V_LAST_VAR_ID,41NB_ID42};434445static struct var_t vars[NB_ID] = {46[CAPS_START_ID] = { CAPS_START, .u.s = {"\033P8" } },47[CAPS_STOP_ID] = { CAPS_STOP, .u.s = {"\033P5" } },48[RATE_ID] = { RATE, .u.n = {"\033R%c", 9, 0, 17, 0, 0, "0123456789abcdefgh" } },49[PITCH_ID] = { PITCH, .u.n = {"\033P%d", 5, 0, 9, 0, 0, NULL } },50[VOL_ID] = { VOL, .u.n = {"\033A%d", 5, 0, 9, 0, 0, NULL } },51[TONE_ID] = { TONE, .u.n = {"\033V%d", 5, 0, 9, 0, 0, NULL } },52[DIRECT_ID] = { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },53V_LAST_VAR54};5556/*57* These attributes will appear in /sys/accessibility/speakup/acntpc.58*/59static struct kobj_attribute caps_start_attribute =60__ATTR(caps_start, 0644, spk_var_show, spk_var_store);61static struct kobj_attribute caps_stop_attribute =62__ATTR(caps_stop, 0644, spk_var_show, spk_var_store);63static struct kobj_attribute pitch_attribute =64__ATTR(pitch, 0644, spk_var_show, spk_var_store);65static struct kobj_attribute rate_attribute =66__ATTR(rate, 0644, spk_var_show, spk_var_store);67static struct kobj_attribute tone_attribute =68__ATTR(tone, 0644, spk_var_show, spk_var_store);69static struct kobj_attribute vol_attribute =70__ATTR(vol, 0644, spk_var_show, spk_var_store);7172static struct kobj_attribute delay_time_attribute =73__ATTR(delay_time, 0644, spk_var_show, spk_var_store);74static struct kobj_attribute direct_attribute =75__ATTR(direct, 0644, spk_var_show, spk_var_store);76static struct kobj_attribute full_time_attribute =77__ATTR(full_time, 0644, spk_var_show, spk_var_store);78static struct kobj_attribute jiffy_delta_attribute =79__ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store);80static struct kobj_attribute trigger_time_attribute =81__ATTR(trigger_time, 0644, spk_var_show, spk_var_store);8283/*84* Create a group of attributes so that we can create and destroy them all85* at once.86*/87static struct attribute *synth_attrs[] = {88&caps_start_attribute.attr,89&caps_stop_attribute.attr,90&pitch_attribute.attr,91&rate_attribute.attr,92&tone_attribute.attr,93&vol_attribute.attr,94&delay_time_attribute.attr,95&direct_attribute.attr,96&full_time_attribute.attr,97&jiffy_delta_attribute.attr,98&trigger_time_attribute.attr,99NULL, /* need to NULL terminate the list of attributes */100};101102static struct spk_synth synth_acntpc = {103.name = "acntpc",104.version = DRV_VERSION,105.long_name = "Accent PC",106.init = "\033=X \033Oi\033T2\033=M\033N1\n",107.procspeech = PROCSPEECH,108.clear = SYNTH_CLEAR,109.delay = 500,110.trigger = 50,111.jiffies = 50,112.full = 1000,113.startup = SYNTH_START,114.checkval = SYNTH_CHECK,115.vars = vars,116.io_ops = &spk_serial_io_ops,117.probe = synth_probe,118.release = accent_release,119.synth_immediate = synth_immediate,120.catch_up = do_catch_up,121.flush = synth_flush,122.is_alive = spk_synth_is_alive_nop,123.synth_adjust = NULL,124.read_buff_add = NULL,125.get_index = NULL,126.indexing = {127.command = NULL,128.lowindex = 0,129.highindex = 0,130.currindex = 0,131},132.attributes = {133.attrs = synth_attrs,134.name = "acntpc",135},136};137138static inline bool synth_writable(void)139{140return inb_p(synth_port_control) & SYNTH_WRITABLE;141}142143static inline bool synth_full(void)144{145return inb_p(speakup_info.port_tts + UART_RX) == 'F';146}147148static const char *synth_immediate(struct spk_synth *synth, const char *buf)149{150u_char ch;151152while ((ch = *buf)) {153int timeout = SPK_XMITR_TIMEOUT;154155if (ch == '\n')156ch = PROCSPEECH;157if (synth_full())158return buf;159while (synth_writable()) {160if (!--timeout)161return buf;162udelay(1);163}164outb_p(ch, speakup_info.port_tts);165buf++;166}167return NULL;168}169170static void do_catch_up(struct spk_synth *synth)171{172u_char ch;173unsigned long flags;174unsigned long jiff_max;175int timeout;176int delay_time_val;177int jiffy_delta_val;178int full_time_val;179struct var_t *delay_time;180struct var_t *full_time;181struct var_t *jiffy_delta;182183jiffy_delta = spk_get_var(JIFFY);184delay_time = spk_get_var(DELAY);185full_time = spk_get_var(FULL);186187spin_lock_irqsave(&speakup_info.spinlock, flags);188jiffy_delta_val = jiffy_delta->u.n.value;189spin_unlock_irqrestore(&speakup_info.spinlock, flags);190191jiff_max = jiffies + jiffy_delta_val;192while (!kthread_should_stop()) {193spin_lock_irqsave(&speakup_info.spinlock, flags);194if (speakup_info.flushing) {195speakup_info.flushing = 0;196spin_unlock_irqrestore(&speakup_info.spinlock, flags);197synth->flush(synth);198continue;199}200synth_buffer_skip_nonlatin1();201if (synth_buffer_empty()) {202spin_unlock_irqrestore(&speakup_info.spinlock, flags);203break;204}205set_current_state(TASK_INTERRUPTIBLE);206full_time_val = full_time->u.n.value;207spin_unlock_irqrestore(&speakup_info.spinlock, flags);208if (synth_full()) {209schedule_timeout(msecs_to_jiffies(full_time_val));210continue;211}212set_current_state(TASK_RUNNING);213timeout = SPK_XMITR_TIMEOUT;214while (synth_writable()) {215if (!--timeout)216break;217udelay(1);218}219spin_lock_irqsave(&speakup_info.spinlock, flags);220ch = synth_buffer_getc();221spin_unlock_irqrestore(&speakup_info.spinlock, flags);222if (ch == '\n')223ch = PROCSPEECH;224outb_p(ch, speakup_info.port_tts);225if (time_after_eq(jiffies, jiff_max) && ch == SPACE) {226timeout = SPK_XMITR_TIMEOUT;227while (synth_writable()) {228if (!--timeout)229break;230udelay(1);231}232outb_p(PROCSPEECH, speakup_info.port_tts);233spin_lock_irqsave(&speakup_info.spinlock, flags);234jiffy_delta_val = jiffy_delta->u.n.value;235delay_time_val = delay_time->u.n.value;236spin_unlock_irqrestore(&speakup_info.spinlock, flags);237schedule_timeout(msecs_to_jiffies(delay_time_val));238jiff_max = jiffies + jiffy_delta_val;239}240}241timeout = SPK_XMITR_TIMEOUT;242while (synth_writable()) {243if (!--timeout)244break;245udelay(1);246}247outb_p(PROCSPEECH, speakup_info.port_tts);248}249250static void synth_flush(struct spk_synth *synth)251{252outb_p(SYNTH_CLEAR, speakup_info.port_tts);253}254255static int synth_probe(struct spk_synth *synth)256{257unsigned int port_val = 0;258int i;259260pr_info("Probing for %s.\n", synth->long_name);261if (port_forced) {262speakup_info.port_tts = port_forced;263pr_info("probe forced to %x by kernel command line\n",264speakup_info.port_tts);265if (synth_request_region(speakup_info.port_tts - 1,266SYNTH_IO_EXTENT)) {267pr_warn("sorry, port already reserved\n");268return -EBUSY;269}270port_val = inw(speakup_info.port_tts - 1);271synth_port_control = speakup_info.port_tts - 1;272} else {273for (i = 0; synth_portlist[i]; i++) {274if (synth_request_region(synth_portlist[i],275SYNTH_IO_EXTENT)) {276pr_warn277("request_region: failed with 0x%x, %d\n",278synth_portlist[i], SYNTH_IO_EXTENT);279continue;280}281port_val = inw(synth_portlist[i]) & 0xfffc;282if (port_val == 0x53fc) {283/* 'S' and out&input bits */284synth_port_control = synth_portlist[i];285speakup_info.port_tts = synth_port_control + 1;286break;287}288}289}290port_val &= 0xfffc;291if (port_val != 0x53fc) {292/* 'S' and out&input bits */293pr_info("%s: not found\n", synth->long_name);294synth_release_region(synth_port_control, SYNTH_IO_EXTENT);295synth_port_control = 0;296return -ENODEV;297}298pr_info("%s: %03x-%03x, driver version %s,\n", synth->long_name,299synth_port_control, synth_port_control + SYNTH_IO_EXTENT - 1,300synth->version);301synth->alive = 1;302return 0;303}304305static void accent_release(struct spk_synth *synth)306{307spk_stop_serial_interrupt();308if (speakup_info.port_tts)309synth_release_region(speakup_info.port_tts - 1,310SYNTH_IO_EXTENT);311speakup_info.port_tts = 0;312}313314module_param_hw_named(port, port_forced, int, ioport, 0444);315module_param_named(start, synth_acntpc.startup, short, 0444);316module_param_named(rate, vars[RATE_ID].u.n.default_val, int, 0444);317module_param_named(pitch, vars[PITCH_ID].u.n.default_val, int, 0444);318module_param_named(vol, vars[VOL_ID].u.n.default_val, int, 0444);319module_param_named(tone, vars[TONE_ID].u.n.default_val, int, 0444);320module_param_named(direct, vars[DIRECT_ID].u.n.default_val, int, 0444);321322323324MODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing).");325MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");326MODULE_PARM_DESC(rate, "Set the rate variable on load.");327MODULE_PARM_DESC(pitch, "Set the pitch variable on load.");328MODULE_PARM_DESC(vol, "Set the vol variable on load.");329MODULE_PARM_DESC(tone, "Set the tone variable on load.");330MODULE_PARM_DESC(direct, "Set the direct variable on load.");331332333module_spk_synth(synth_acntpc);334335MODULE_AUTHOR("Kirk Reiser <[email protected]>");336MODULE_AUTHOR("David Borowski");337MODULE_DESCRIPTION("Speakup support for Accent PC synthesizer");338MODULE_LICENSE("GPL");339MODULE_VERSION(DRV_VERSION);340341342343