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