cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

speakup_keypc.c (8328B)


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