Path: blob/master/drivers/accessibility/speakup/speakup_keypc.c
26282 views
// SPDX-License-Identifier: GPL-2.0+1/*2* written by David Borowski3*4* Copyright (C) 2003 David Borowski.5*6* specifically written as a driver for the speakup screenreview7* package it's not a general device driver.8* This driver is for the Keynote Gold internal synthesizer.9*/10#include <linux/jiffies.h>11#include <linux/sched.h>12#include <linux/timer.h>13#include <linux/kthread.h>14#include <linux/serial_reg.h>1516#include "spk_priv.h"17#include "speakup.h"1819#define DRV_VERSION "2.10"20#define SYNTH_IO_EXTENT 0x0421#define SWAIT udelay(70)22#define PROCSPEECH 0x1f23#define SYNTH_CLEAR 0x032425static int synth_probe(struct spk_synth *synth);26static void keynote_release(struct spk_synth *synth);27static const char *synth_immediate(struct spk_synth *synth, const char *buf);28static void do_catch_up(struct spk_synth *synth);29static void synth_flush(struct spk_synth *synth);3031static int synth_port;32static int port_forced;33static unsigned int synth_portlist[] = { 0x2a8, 0 };343536enum default_vars_id {37CAPS_START_ID = 0, CAPS_STOP_ID,38RATE_ID, PITCH_ID,39DIRECT_ID, V_LAST_VAR_ID,40NB_ID41};424344static struct var_t vars[NB_ID] = {45[CAPS_START_ID] = { CAPS_START, .u.s = {"[f130]" } },46[CAPS_STOP_ID] = { CAPS_STOP, .u.s = {"[f90]" } },47[RATE_ID] = { RATE, .u.n = {"\04%c ", 8, 0, 10, 81, -8, NULL } },48[PITCH_ID] = { PITCH, .u.n = {"[f%d]", 5, 0, 9, 40, 10, NULL } },49[DIRECT_ID] = { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },50V_LAST_VAR51};5253/*54* These attributes will appear in /sys/accessibility/speakup/keypc.55*/56static struct kobj_attribute caps_start_attribute =57__ATTR(caps_start, 0644, spk_var_show, spk_var_store);58static struct kobj_attribute caps_stop_attribute =59__ATTR(caps_stop, 0644, spk_var_show, spk_var_store);60static struct kobj_attribute pitch_attribute =61__ATTR(pitch, 0644, spk_var_show, spk_var_store);62static struct kobj_attribute rate_attribute =63__ATTR(rate, 0644, spk_var_show, spk_var_store);6465static struct kobj_attribute delay_time_attribute =66__ATTR(delay_time, 0644, spk_var_show, spk_var_store);67static struct kobj_attribute direct_attribute =68__ATTR(direct, 0644, spk_var_show, spk_var_store);69static struct kobj_attribute full_time_attribute =70__ATTR(full_time, 0644, spk_var_show, spk_var_store);71static struct kobj_attribute jiffy_delta_attribute =72__ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store);73static struct kobj_attribute trigger_time_attribute =74__ATTR(trigger_time, 0644, spk_var_show, spk_var_store);7576/*77* Create a group of attributes so that we can create and destroy them all78* at once.79*/80static struct attribute *synth_attrs[] = {81&caps_start_attribute.attr,82&caps_stop_attribute.attr,83&pitch_attribute.attr,84&rate_attribute.attr,85&delay_time_attribute.attr,86&direct_attribute.attr,87&full_time_attribute.attr,88&jiffy_delta_attribute.attr,89&trigger_time_attribute.attr,90NULL, /* need to NULL terminate the list of attributes */91};9293static struct spk_synth synth_keypc = {94.name = "keypc",95.version = DRV_VERSION,96.long_name = "Keynote PC",97.init = "[t][n7,1][n8,0]",98.procspeech = PROCSPEECH,99.clear = SYNTH_CLEAR,100.delay = 500,101.trigger = 50,102.jiffies = 50,103.full = 1000,104.startup = SYNTH_START,105.checkval = SYNTH_CHECK,106.vars = vars,107.io_ops = &spk_serial_io_ops,108.probe = synth_probe,109.release = keynote_release,110.synth_immediate = synth_immediate,111.catch_up = do_catch_up,112.flush = synth_flush,113.is_alive = spk_synth_is_alive_nop,114.synth_adjust = NULL,115.read_buff_add = NULL,116.get_index = NULL,117.indexing = {118.command = NULL,119.lowindex = 0,120.highindex = 0,121.currindex = 0,122},123.attributes = {124.attrs = synth_attrs,125.name = "keypc",126},127};128129static inline bool synth_writable(void)130{131return (inb_p(synth_port + UART_RX) & 0x10) != 0;132}133134static inline bool synth_full(void)135{136return (inb_p(synth_port + UART_RX) & 0x80) == 0;137}138139static char *oops(void)140{141int s1, s2, s3, s4;142143s1 = inb_p(synth_port);144s2 = inb_p(synth_port + 1);145s3 = inb_p(synth_port + 2);146s4 = inb_p(synth_port + 3);147pr_warn("synth timeout %d %d %d %d\n", s1, s2, s3, s4);148return NULL;149}150151static const char *synth_immediate(struct spk_synth *synth, const char *buf)152{153u_char ch;154int timeout;155156while ((ch = *buf)) {157if (ch == '\n')158ch = PROCSPEECH;159if (synth_full())160return buf;161timeout = 1000;162while (synth_writable())163if (--timeout <= 0)164return oops();165outb_p(ch, synth_port);166udelay(70);167buf++;168}169return NULL;170}171172static void do_catch_up(struct spk_synth *synth)173{174u_char ch;175int timeout;176unsigned long flags;177unsigned long jiff_max;178struct var_t *jiffy_delta;179struct var_t *delay_time;180struct var_t *full_time;181int delay_time_val;182int full_time_val;183int jiffy_delta_val;184185jiffy_delta = spk_get_var(JIFFY);186delay_time = spk_get_var(DELAY);187full_time = spk_get_var(FULL);188spin_lock_irqsave(&speakup_info.spinlock, flags);189jiffy_delta_val = jiffy_delta->u.n.value;190spin_unlock_irqrestore(&speakup_info.spinlock, flags);191192jiff_max = jiffies + jiffy_delta_val;193while (!kthread_should_stop()) {194spin_lock_irqsave(&speakup_info.spinlock, flags);195if (speakup_info.flushing) {196speakup_info.flushing = 0;197spin_unlock_irqrestore(&speakup_info.spinlock, flags);198synth->flush(synth);199continue;200}201synth_buffer_skip_nonlatin1();202if (synth_buffer_empty()) {203spin_unlock_irqrestore(&speakup_info.spinlock, flags);204break;205}206set_current_state(TASK_INTERRUPTIBLE);207full_time_val = full_time->u.n.value;208spin_unlock_irqrestore(&speakup_info.spinlock, flags);209if (synth_full()) {210schedule_timeout(msecs_to_jiffies(full_time_val));211continue;212}213set_current_state(TASK_RUNNING);214timeout = 1000;215while (synth_writable())216if (--timeout <= 0)217break;218if (timeout <= 0) {219oops();220break;221}222spin_lock_irqsave(&speakup_info.spinlock, flags);223ch = synth_buffer_getc();224spin_unlock_irqrestore(&speakup_info.spinlock, flags);225if (ch == '\n')226ch = PROCSPEECH;227outb_p(ch, synth_port);228SWAIT;229if (time_after_eq(jiffies, jiff_max) && (ch == SPACE)) {230timeout = 1000;231while (synth_writable())232if (--timeout <= 0)233break;234if (timeout <= 0) {235oops();236break;237}238outb_p(PROCSPEECH, synth_port);239spin_lock_irqsave(&speakup_info.spinlock, flags);240jiffy_delta_val = jiffy_delta->u.n.value;241delay_time_val = delay_time->u.n.value;242spin_unlock_irqrestore(&speakup_info.spinlock, flags);243schedule_timeout(msecs_to_jiffies(delay_time_val));244jiff_max = jiffies + jiffy_delta_val;245}246}247timeout = 1000;248while (synth_writable())249if (--timeout <= 0)250break;251if (timeout <= 0)252oops();253else254outb_p(PROCSPEECH, synth_port);255}256257static void synth_flush(struct spk_synth *synth)258{259outb_p(SYNTH_CLEAR, synth_port);260}261262static int synth_probe(struct spk_synth *synth)263{264unsigned int port_val = 0;265int i;266267pr_info("Probing for %s.\n", synth->long_name);268if (port_forced) {269synth_port = port_forced;270pr_info("probe forced to %x by kernel command line\n",271synth_port);272if (synth_request_region(synth_port - 1, SYNTH_IO_EXTENT)) {273pr_warn("sorry, port already reserved\n");274return -EBUSY;275}276port_val = inb(synth_port);277} else {278for (i = 0; synth_portlist[i]; i++) {279if (synth_request_region(synth_portlist[i],280SYNTH_IO_EXTENT)) {281pr_warn282("request_region: failed with 0x%x, %d\n",283synth_portlist[i], SYNTH_IO_EXTENT);284continue;285}286port_val = inb(synth_portlist[i]);287if (port_val == 0x80) {288synth_port = synth_portlist[i];289break;290}291}292}293if (port_val != 0x80) {294pr_info("%s: not found\n", synth->long_name);295synth_release_region(synth_port, SYNTH_IO_EXTENT);296synth_port = 0;297return -ENODEV;298}299pr_info("%s: %03x-%03x, driver version %s,\n", synth->long_name,300synth_port, synth_port + SYNTH_IO_EXTENT - 1,301synth->version);302synth->alive = 1;303return 0;304}305306static void keynote_release(struct spk_synth *synth)307{308spk_stop_serial_interrupt();309if (synth_port)310synth_release_region(synth_port, SYNTH_IO_EXTENT);311synth_port = 0;312}313314module_param_hw_named(port, port_forced, int, ioport, 0444);315module_param_named(start, synth_keypc.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(direct, vars[DIRECT_ID].u.n.default_val, int, 0444);319320MODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing).");321MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");322MODULE_PARM_DESC(rate, "Set the rate variable on load.");323MODULE_PARM_DESC(pitch, "Set the pitch variable on load.");324MODULE_PARM_DESC(direct, "Set the direct variable on load.");325326327328module_spk_synth(synth_keypc);329330MODULE_AUTHOR("David Borowski");331MODULE_DESCRIPTION("Speakup support for Keynote Gold PC synthesizers");332MODULE_LICENSE("GPL");333MODULE_VERSION(DRV_VERSION);334335336337