1#include <linux/interrupt.h>
2#include <linux/ioport.h>
3
4#include "spk_types.h"
5#include "speakup.h"
6#include "spk_priv.h"
7#include "serialio.h"
8
9static void start_serial_interrupt(int irq);
10
11static const struct old_serial_port rs_table[] = {
12	SERIAL_PORT_DFNS
13};
14static const struct old_serial_port *serstate;
15static int timeouts;
16
17const struct old_serial_port *spk_serial_init(int index)
18{
19	int baud = 9600, quot = 0;
20	unsigned int cval = 0;
21	int cflag = CREAD | HUPCL | CLOCAL | B9600 | CS8;
22	const struct old_serial_port *ser = rs_table + index;
23	int err;
24
25	/*	Divisor, bytesize and parity */
26	quot = ser->baud_base / baud;
27	cval = cflag & (CSIZE | CSTOPB);
28#if defined(__powerpc__) || defined(__alpha__)
29	cval >>= 8;
30#else /* !__powerpc__ && !__alpha__ */
31	cval >>= 4;
32#endif /* !__powerpc__ && !__alpha__ */
33	if (cflag & PARENB)
34		cval |= UART_LCR_PARITY;
35	if (!(cflag & PARODD))
36		cval |= UART_LCR_EPAR;
37	if (synth_request_region(ser->port, 8)) {
38		/* try to take it back. */
39		printk(KERN_INFO "Ports not available, trying to steal them\n");
40		__release_region(&ioport_resource, ser->port, 8);
41		err = synth_request_region(ser->port, 8);
42		if (err) {
43			pr_warn("Unable to allocate port at %x, errno %i",
44				ser->port, err);
45			return NULL;
46		}
47	}
48
49	/*	Disable UART interrupts, set DTR and RTS high
50	 *	and set speed. */
51	outb(cval | UART_LCR_DLAB, ser->port + UART_LCR);	/* set DLAB */
52	outb(quot & 0xff, ser->port + UART_DLL);	/* LS of divisor */
53	outb(quot >> 8, ser->port + UART_DLM);		/* MS of divisor */
54	outb(cval, ser->port + UART_LCR);		/* reset DLAB */
55
56	/* Turn off Interrupts */
57	outb(0, ser->port + UART_IER);
58	outb(UART_MCR_DTR | UART_MCR_RTS, ser->port + UART_MCR);
59
60	/* If we read 0xff from the LSR, there is no UART here. */
61	if (inb(ser->port + UART_LSR) == 0xff) {
62		synth_release_region(ser->port, 8);
63		serstate = NULL;
64		return NULL;
65	}
66
67	mdelay(1);
68	speakup_info.port_tts = ser->port;
69	serstate = ser;
70
71	start_serial_interrupt(ser->irq);
72
73	return ser;
74}
75
76static irqreturn_t synth_readbuf_handler(int irq, void *dev_id)
77{
78	unsigned long flags;
79/*printk(KERN_ERR "in irq\n"); */
80/*pr_warn("in IRQ\n"); */
81	int c;
82	spk_lock(flags);
83	while (inb_p(speakup_info.port_tts + UART_LSR) & UART_LSR_DR) {
84
85		c = inb_p(speakup_info.port_tts+UART_RX);
86		synth->read_buff_add((u_char) c);
87/*printk(KERN_ERR "c = %d\n", c); */
88/*pr_warn("C = %d\n", c); */
89	}
90	spk_unlock(flags);
91	return IRQ_HANDLED;
92}
93
94static void start_serial_interrupt(int irq)
95{
96	int rv;
97
98	if (synth->read_buff_add == NULL)
99		return;
100
101	rv = request_irq(irq, synth_readbuf_handler, IRQF_SHARED,
102			 "serial", (void *) synth_readbuf_handler);
103
104	if (rv)
105		printk(KERN_ERR "Unable to request Speakup serial I R Q\n");
106	/* Set MCR */
107	outb(UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2,
108			speakup_info.port_tts + UART_MCR);
109	/* Turn on Interrupts */
110	outb(UART_IER_MSI|UART_IER_RLSI|UART_IER_RDI,
111			speakup_info.port_tts + UART_IER);
112	inb(speakup_info.port_tts+UART_LSR);
113	inb(speakup_info.port_tts+UART_RX);
114	inb(speakup_info.port_tts+UART_IIR);
115	inb(speakup_info.port_tts+UART_MSR);
116	outb(1, speakup_info.port_tts + UART_FCR);	/* Turn FIFO On */
117}
118
119void stop_serial_interrupt(void)
120{
121	if (speakup_info.port_tts == 0)
122		return;
123
124	if (synth->read_buff_add == NULL)
125		return;
126
127	/* Turn off interrupts */
128	outb(0, speakup_info.port_tts+UART_IER);
129	/* Free IRQ */
130	free_irq(serstate->irq, (void *) synth_readbuf_handler);
131}
132
133int wait_for_xmitr(void)
134{
135	int tmout = SPK_XMITR_TIMEOUT;
136	if ((synth->alive) && (timeouts >= NUM_DISABLE_TIMEOUTS)) {
137		pr_warn("%s: too many timeouts, deactivating speakup\n",
138			synth->long_name);
139		synth->alive = 0;
140		/* No synth any more, so nobody will restart TTYs, and we thus
141		 * need to do it ourselves.  Now that there is no synth we can
142		 * let application flood anyway */
143		speakup_start_ttys();
144		timeouts = 0;
145		return 0;
146	}
147	while (spk_serial_tx_busy()) {
148		if (--tmout == 0) {
149			pr_warn("%s: timed out (tx busy)\n", synth->long_name);
150			timeouts++;
151			return 0;
152		}
153		udelay(1);
154	}
155	tmout = SPK_CTS_TIMEOUT;
156	while (!((inb_p(speakup_info.port_tts + UART_MSR)) & UART_MSR_CTS)) {
157		/* CTS */
158		if (--tmout == 0) {
159			/* pr_warn("%s: timed out (cts)\n",
160			 * synth->long_name); */
161			timeouts++;
162			return 0;
163		}
164		udelay(1);
165	}
166	timeouts = 0;
167	return 1;
168}
169
170unsigned char spk_serial_in(void)
171{
172	int tmout = SPK_SERIAL_TIMEOUT;
173
174	while (!(inb_p(speakup_info.port_tts + UART_LSR) & UART_LSR_DR)) {
175		if (--tmout == 0) {
176			pr_warn("time out while waiting for input.\n");
177			return 0xff;
178		}
179		udelay(1);
180	}
181	return inb_p(speakup_info.port_tts + UART_RX);
182}
183EXPORT_SYMBOL_GPL(spk_serial_in);
184
185unsigned char spk_serial_in_nowait(void)
186{
187	unsigned char lsr;
188
189	lsr = inb_p(speakup_info.port_tts + UART_LSR);
190	if (!(lsr & UART_LSR_DR))
191		return 0;
192	return inb_p(speakup_info.port_tts + UART_RX);
193}
194EXPORT_SYMBOL_GPL(spk_serial_in_nowait);
195
196int spk_serial_out(const char ch)
197{
198	if (synth->alive && wait_for_xmitr()) {
199		outb_p(ch, speakup_info.port_tts);
200		return 1;
201	}
202	return 0;
203}
204EXPORT_SYMBOL_GPL(spk_serial_out);
205
206void spk_serial_release(void)
207{
208	if (speakup_info.port_tts == 0)
209		return;
210	synth_release_region(speakup_info.port_tts, 8);
211	speakup_info.port_tts = 0;
212}
213EXPORT_SYMBOL_GPL(spk_serial_release);
214
215