1c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs/*
2c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * written by David Borowski
3c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs *
4c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * Copyright (C) 2003 David Borowski.
5c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs *
6c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * This program is free software; you can redistribute it and/or modify
7c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * it under the terms of the GNU General Public License as published by
8c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * the Free Software Foundation; either version 2 of the License, or
9c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * (at your option) any later version.
10c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs *
11c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * This program is distributed in the hope that it will be useful,
12c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * but WITHOUT ANY WARRANTY; without even the implied warranty of
13c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * GNU General Public License for more details.
15c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs *
16c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * You should have received a copy of the GNU General Public License
17c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * along with this program; if not, write to the Free Software
18c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs *
20c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * specificly written as a driver for the speakup screenreview
21c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * package it's not a general device driver.
22c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * This driver is for the Keynote Gold internal synthesizer.
23c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs */
24c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#include <linux/jiffies.h>
25c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#include <linux/sched.h>
26c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#include <linux/timer.h>
27c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#include <linux/kthread.h>
28c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#include <linux/serial_reg.h>
29c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
30c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#include "spk_priv.h"
31c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#include "speakup.h"
32c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
33c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define DRV_VERSION "2.10"
34c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define SYNTH_IO_EXTENT	0x04
35c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define SWAIT udelay(70)
36c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define PROCSPEECH 0x1f
37c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define SYNTH_CLEAR 0x03
38c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
39c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic int synth_probe(struct spk_synth *synth);
40c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic void keynote_release(void);
41c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic const char *synth_immediate(struct spk_synth *synth, const char *buf);
42c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic void do_catch_up(struct spk_synth *synth);
43c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic void synth_flush(struct spk_synth *synth);
44c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
45c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic int synth_port;
46c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic int port_forced;
47c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic unsigned int synth_portlist[] = { 0x2a8, 0 };
48c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
49c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct var_t vars[] = {
5075d6282d8b2d58b4722e63225f3951e1c9b0ebf8Christopher Brannon	{ CAPS_START, .u.s = {"[f130]" } },
5175d6282d8b2d58b4722e63225f3951e1c9b0ebf8Christopher Brannon	{ CAPS_STOP, .u.s = {"[f90]" } },
5275d6282d8b2d58b4722e63225f3951e1c9b0ebf8Christopher Brannon	{ RATE, .u.n = {"\04%c ", 8, 0, 10, 81, -8, NULL } },
5375d6282d8b2d58b4722e63225f3951e1c9b0ebf8Christopher Brannon	{ PITCH, .u.n = {"[f%d]", 5, 0, 9, 40, 10, NULL } },
5475d6282d8b2d58b4722e63225f3951e1c9b0ebf8Christopher Brannon	{ DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
55c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	V_LAST_VAR
56c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs};
57c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
58c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs/*
59c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * These attributes will appear in /sys/accessibility/speakup/keypc.
60c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs */
61c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct kobj_attribute caps_start_attribute =
62c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	__ATTR(caps_start, USER_RW, spk_var_show, spk_var_store);
63c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct kobj_attribute caps_stop_attribute =
64c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	__ATTR(caps_stop, USER_RW, spk_var_show, spk_var_store);
65c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct kobj_attribute pitch_attribute =
66c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	__ATTR(pitch, USER_RW, spk_var_show, spk_var_store);
67c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct kobj_attribute rate_attribute =
68c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	__ATTR(rate, USER_RW, spk_var_show, spk_var_store);
69c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
70c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct kobj_attribute delay_time_attribute =
71c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	__ATTR(delay_time, ROOT_W, spk_var_show, spk_var_store);
72c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct kobj_attribute direct_attribute =
73c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	__ATTR(direct, USER_RW, spk_var_show, spk_var_store);
74c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct kobj_attribute full_time_attribute =
75c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	__ATTR(full_time, ROOT_W, spk_var_show, spk_var_store);
76c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct kobj_attribute jiffy_delta_attribute =
77c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	__ATTR(jiffy_delta, ROOT_W, spk_var_show, spk_var_store);
78c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct kobj_attribute trigger_time_attribute =
79c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	__ATTR(trigger_time, ROOT_W, spk_var_show, spk_var_store);
80c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
81c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs/*
82c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * Create a group of attributes so that we can create and destroy them all
83c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * at once.
84c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs */
85c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct attribute *synth_attrs[] = {
86c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	&caps_start_attribute.attr,
87c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	&caps_stop_attribute.attr,
88c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	&pitch_attribute.attr,
89c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	&rate_attribute.attr,
90c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	&delay_time_attribute.attr,
91c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	&direct_attribute.attr,
92c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	&full_time_attribute.attr,
93c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	&jiffy_delta_attribute.attr,
94c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	&trigger_time_attribute.attr,
95c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	NULL,	/* need to NULL terminate the list of attributes */
96c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs};
97c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
98c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct spk_synth synth_keypc = {
99c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.name = "keypc",
100c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.version = DRV_VERSION,
101c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.long_name = "Keynote PC",
102c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.init = "[t][n7,1][n8,0]",
103c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.procspeech = PROCSPEECH,
104c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.clear = SYNTH_CLEAR,
105c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.delay = 500,
106c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.trigger = 50,
107c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.jiffies = 50,
108c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.full = 1000,
109c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.startup = SYNTH_START,
110c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.checkval = SYNTH_CHECK,
111c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.vars = vars,
112c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.probe = synth_probe,
113c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.release = keynote_release,
114c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.synth_immediate = synth_immediate,
115c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.catch_up = do_catch_up,
116c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.flush = synth_flush,
117c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.is_alive = spk_synth_is_alive_nop,
118c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.synth_adjust = NULL,
119c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.read_buff_add = NULL,
120c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.get_index = NULL,
121c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.indexing = {
122c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		.command = NULL,
123c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		.lowindex = 0,
124c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		.highindex = 0,
125c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		.currindex = 0,
126c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	},
127c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.attributes = {
128c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		.attrs = synth_attrs,
129c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		.name = "keypc",
130c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	},
131c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs};
132c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
13375d6282d8b2d58b4722e63225f3951e1c9b0ebf8Christopher Brannonstatic inline bool synth_writable(void)
13475d6282d8b2d58b4722e63225f3951e1c9b0ebf8Christopher Brannon{
13575d6282d8b2d58b4722e63225f3951e1c9b0ebf8Christopher Brannon	return (inb_p(synth_port + UART_RX) & 0x10) != 0;
13675d6282d8b2d58b4722e63225f3951e1c9b0ebf8Christopher Brannon}
13775d6282d8b2d58b4722e63225f3951e1c9b0ebf8Christopher Brannon
13875d6282d8b2d58b4722e63225f3951e1c9b0ebf8Christopher Brannonstatic inline bool synth_full(void)
13975d6282d8b2d58b4722e63225f3951e1c9b0ebf8Christopher Brannon{
14075d6282d8b2d58b4722e63225f3951e1c9b0ebf8Christopher Brannon	return (inb_p(synth_port + UART_RX) & 0x80) == 0;
14175d6282d8b2d58b4722e63225f3951e1c9b0ebf8Christopher Brannon}
14275d6282d8b2d58b4722e63225f3951e1c9b0ebf8Christopher Brannon
143c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic char *oops(void)
144c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs{
145c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	int s1, s2, s3, s4;
146c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	s1 = inb_p(synth_port);
147c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	s2 = inb_p(synth_port+1);
148c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	s3 = inb_p(synth_port+2);
149c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	s4 = inb_p(synth_port+3);
150c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	pr_warn("synth timeout %d %d %d %d\n", s1, s2, s3, s4);
151c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	return NULL;
152c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs}
153c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
154c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic const char *synth_immediate(struct spk_synth *synth, const char *buf)
155c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs{
156c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	u_char ch;
157c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	int timeout;
158c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	while ((ch = *buf)) {
159c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if (ch == '\n')
160c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			ch = PROCSPEECH;
161c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if (synth_full())
162c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			return buf;
163c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		timeout = 1000;
164c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		while (synth_writable())
165c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			if (--timeout <= 0)
166c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs				return oops();
167c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		outb_p(ch, synth_port);
168c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		udelay(70);
169c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		buf++;
170c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	}
171c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	return 0;
172c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs}
173c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
174c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic void do_catch_up(struct spk_synth *synth)
175c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs{
176c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	u_char ch;
177c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	int timeout;
178c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	unsigned long flags;
179c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	unsigned long jiff_max;
180c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	struct var_t *jiffy_delta;
181c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	struct var_t *delay_time;
182c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	struct var_t *full_time;
183c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	int delay_time_val;
184c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	int full_time_val;
185c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	int jiffy_delta_val;
186c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
187c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	jiffy_delta = get_var(JIFFY);
188c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	delay_time = get_var(DELAY);
189c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	full_time = get_var(FULL);
190c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsspk_lock(flags);
191c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	jiffy_delta_val = jiffy_delta->u.n.value;
192c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	spk_unlock(flags);
193c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
194c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	jiff_max = jiffies + jiffy_delta_val;
195c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	while (!kthread_should_stop()) {
196c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		spk_lock(flags);
197c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if (speakup_info.flushing) {
198c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			speakup_info.flushing = 0;
199c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			spk_unlock(flags);
200c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			synth->flush(synth);
201c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			continue;
202c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		}
203c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if (synth_buffer_empty()) {
204c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			spk_unlock(flags);
205c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			break;
206c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		}
207c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		set_current_state(TASK_INTERRUPTIBLE);
208c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		full_time_val = full_time->u.n.value;
209c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		spk_unlock(flags);
210c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if (synth_full()) {
211c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			schedule_timeout(msecs_to_jiffies(full_time_val));
212c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			continue;
213c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		}
214c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		set_current_state(TASK_RUNNING);
215c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		timeout = 1000;
216c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		while (synth_writable())
217c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			if (--timeout <= 0)
218c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs				break;
219c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if (timeout <= 0) {
220c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			oops();
221c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			break;
222c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		}
223c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		spk_lock(flags);
224c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		ch = synth_buffer_getc();
225c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		spk_unlock(flags);
226c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if (ch == '\n')
227c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			ch = PROCSPEECH;
228c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		outb_p(ch, synth_port);
229c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		SWAIT;
230c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if ((jiffies >= jiff_max) && (ch == SPACE)) {
231c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			timeout = 1000;
232c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			while (synth_writable())
233c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs				if (--timeout <= 0)
234c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs					break;
235c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			if (timeout <= 0) {
236c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs				oops();
237c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs				break;
238c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			}
239c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			outb_p(PROCSPEECH, synth_port);
240c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			spk_lock(flags);
241c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			jiffy_delta_val = jiffy_delta->u.n.value;
242c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			delay_time_val = delay_time->u.n.value;
243c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			spk_unlock(flags);
244c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			schedule_timeout(msecs_to_jiffies(delay_time_val));
245c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			jiff_max = jiffies+jiffy_delta_val;
246c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		}
247c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	}
248c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	timeout = 1000;
249c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	while (synth_writable())
250c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if (--timeout <= 0)
251c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			break;
252c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	if (timeout <= 0)
253c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		oops();
254c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	else
255c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		outb_p(PROCSPEECH, synth_port);
256c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs}
257c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
258c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic void synth_flush(struct spk_synth *synth)
259c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs{
260c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	outb_p(SYNTH_CLEAR, synth_port);
261c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs}
262c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
263c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic int synth_probe(struct spk_synth *synth)
264c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs{
265c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	unsigned int port_val = 0;
266c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	int i = 0;
267c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	pr_info("Probing for %s.\n", synth->long_name);
268c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	if (port_forced) {
269c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		synth_port = port_forced;
270c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		pr_info("probe forced to %x by kernel command line\n",
271c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs				synth_port);
272c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if (synth_request_region(synth_port-1, SYNTH_IO_EXTENT)) {
273c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			pr_warn("sorry, port already reserved\n");
274c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			return -EBUSY;
275c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		}
276c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		port_val = inb(synth_port);
277c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	} else {
278c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		for (i = 0; synth_portlist[i]; i++) {
279c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			if (synth_request_region(synth_portlist[i],
280c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs						SYNTH_IO_EXTENT)) {
28175d6282d8b2d58b4722e63225f3951e1c9b0ebf8Christopher Brannon				pr_warn
28275d6282d8b2d58b4722e63225f3951e1c9b0ebf8Christopher Brannon				    ("request_region: failed with 0x%x, %d\n",
28375d6282d8b2d58b4722e63225f3951e1c9b0ebf8Christopher Brannon				     synth_portlist[i], SYNTH_IO_EXTENT);
284c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs				continue;
285c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			}
286c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			port_val = inb(synth_portlist[i]);
287c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			if (port_val == 0x80) {
288c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs				synth_port = synth_portlist[i];
289c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs				break;
290c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			}
291c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		}
292c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	}
293c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	if (port_val != 0x80) {
294c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		pr_info("%s: not found\n", synth->long_name);
295c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		synth_release_region(synth_port, SYNTH_IO_EXTENT);
296c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		synth_port = 0;
297c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		return -ENODEV;
298c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	}
299c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	pr_info("%s: %03x-%03x, driver version %s,\n", synth->long_name,
300c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		synth_port, synth_port+SYNTH_IO_EXTENT-1,
301c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		synth->version);
302c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	synth->alive = 1;
303c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	return 0;
304c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs}
305c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
306c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic void keynote_release(void)
307c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs{
308c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	if (synth_port)
309c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		synth_release_region(synth_port, SYNTH_IO_EXTENT);
310c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	synth_port = 0;
311c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs}
312c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
313c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsmodule_param_named(port, port_forced, int, S_IRUGO);
314c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsmodule_param_named(start, synth_keypc.startup, short, S_IRUGO);
315c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
316c6e3fd22cd538365bfeb82997d5b89562e077d42William HubbsMODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing).");
317c6e3fd22cd538365bfeb82997d5b89562e077d42William HubbsMODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
318c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
319c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic int __init keypc_init(void)
320c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs{
321c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	return synth_add(&synth_keypc);
322c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs}
323c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
324c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic void __exit keypc_exit(void)
325c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs{
326c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	synth_remove(&synth_keypc);
327c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs}
328c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
329c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsmodule_init(keypc_init);
330c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsmodule_exit(keypc_exit);
331c6e3fd22cd538365bfeb82997d5b89562e077d42William HubbsMODULE_AUTHOR("David Borowski");
332c6e3fd22cd538365bfeb82997d5b89562e077d42William HubbsMODULE_DESCRIPTION("Speakup support for Keynote Gold PC synthesizers");
333c6e3fd22cd538365bfeb82997d5b89562e077d42William HubbsMODULE_LICENSE("GPL");
334c6e3fd22cd538365bfeb82997d5b89562e077d42William HubbsMODULE_VERSION(DRV_VERSION);
335c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
336