Linux Audio

Check our new training course

Linux kernel drivers training

May 6-19, 2025
Register
Loading...
v3.1
 
  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 struct serial_state rs_table[] = {
 12	SERIAL_PORT_DFNS
 13};
 14static struct serial_state *serstate;
 
 15static int timeouts;
 16
 17struct serial_state *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	struct serial_state *ser = NULL;
 23	int err;
 24
 
 
 
 
 25	ser = rs_table + index;
 
 26	/*	Divisor, bytesize and parity */
 27	quot = ser->baud_base / baud;
 28	cval = cflag & (CSIZE | CSTOPB);
 29#if defined(__powerpc__) || defined(__alpha__)
 30	cval >>= 8;
 31#else /* !__powerpc__ && !__alpha__ */
 32	cval >>= 4;
 33#endif /* !__powerpc__ && !__alpha__ */
 34	if (cflag & PARENB)
 35		cval |= UART_LCR_PARITY;
 36	if (!(cflag & PARODD))
 37		cval |= UART_LCR_EPAR;
 38	if (synth_request_region(ser->port, 8)) {
 39		/* try to take it back. */
 40		printk(KERN_INFO "Ports not available, trying to steal them\n");
 41		__release_region(&ioport_resource, ser->port, 8);
 42		err = synth_request_region(ser->port, 8);
 43		if (err) {
 44			pr_warn("Unable to allocate port at %lx, errno %i",
 45				ser->port, err);
 46			return NULL;
 47		}
 48	}
 49
 50	/*	Disable UART interrupts, set DTR and RTS high
 51	 *	and set speed. */
 
 52	outb(cval | UART_LCR_DLAB, ser->port + UART_LCR);	/* set DLAB */
 53	outb(quot & 0xff, ser->port + UART_DLL);	/* LS of divisor */
 54	outb(quot >> 8, ser->port + UART_DLM);		/* MS of divisor */
 55	outb(cval, ser->port + UART_LCR);		/* reset DLAB */
 56
 57	/* Turn off Interrupts */
 58	outb(0, ser->port + UART_IER);
 59	outb(UART_MCR_DTR | UART_MCR_RTS, ser->port + UART_MCR);
 60
 61	/* If we read 0xff from the LSR, there is no UART here. */
 62	if (inb(ser->port + UART_LSR) == 0xff) {
 63		synth_release_region(ser->port, 8);
 64		serstate = NULL;
 65		return NULL;
 66	}
 67
 68	mdelay(1);
 69	speakup_info.port_tts = ser->port;
 70	serstate = ser;
 71
 72	start_serial_interrupt(ser->irq);
 73
 74	return ser;
 75}
 76
 77static irqreturn_t synth_readbuf_handler(int irq, void *dev_id)
 78{
 79	unsigned long flags;
 80/*printk(KERN_ERR "in irq\n"); */
 81/*pr_warn("in IRQ\n"); */
 82	int c;
 83	spk_lock(flags);
 84	while (inb_p(speakup_info.port_tts + UART_LSR) & UART_LSR_DR) {
 85
 86		c = inb_p(speakup_info.port_tts+UART_RX);
 87		synth->read_buff_add((u_char) c);
 88/*printk(KERN_ERR "c = %d\n", c); */
 89/*pr_warn("C = %d\n", c); */
 90	}
 91	spk_unlock(flags);
 92	return IRQ_HANDLED;
 93}
 94
 95static void start_serial_interrupt(int irq)
 96{
 97	int rv;
 98
 99	if (synth->read_buff_add == NULL)
100		return;
101
102	rv = request_irq(irq, synth_readbuf_handler, IRQF_SHARED,
103			 "serial", (void *) synth_readbuf_handler);
104
105	if (rv)
106		printk(KERN_ERR "Unable to request Speakup serial I R Q\n");
107	/* Set MCR */
108	outb(UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2,
109			speakup_info.port_tts + UART_MCR);
110	/* Turn on Interrupts */
111	outb(UART_IER_MSI|UART_IER_RLSI|UART_IER_RDI,
112			speakup_info.port_tts + UART_IER);
113	inb(speakup_info.port_tts+UART_LSR);
114	inb(speakup_info.port_tts+UART_RX);
115	inb(speakup_info.port_tts+UART_IIR);
116	inb(speakup_info.port_tts+UART_MSR);
117	outb(1, speakup_info.port_tts + UART_FCR);	/* Turn FIFO On */
118}
119
120void stop_serial_interrupt(void)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121{
122	if (speakup_info.port_tts == 0)
123		return;
124
125	if (synth->read_buff_add == NULL)
126		return;
127
128	/* Turn off interrupts */
129	outb(0, speakup_info.port_tts+UART_IER);
130	/* Free IRQ */
131	free_irq(serstate->irq, (void *) synth_readbuf_handler);
132}
 
133
134int wait_for_xmitr(void)
135{
136	int tmout = SPK_XMITR_TIMEOUT;
137	if ((synth->alive) && (timeouts >= NUM_DISABLE_TIMEOUTS)) {
 
138		pr_warn("%s: too many timeouts, deactivating speakup\n",
139			synth->long_name);
140		synth->alive = 0;
141		/* No synth any more, so nobody will restart TTYs, and we thus
142		 * need to do it ourselves.  Now that there is no synth we can
143		 * let application flood anyway */
 
144		speakup_start_ttys();
145		timeouts = 0;
146		return 0;
147	}
148	while (spk_serial_tx_busy()) {
149		if (--tmout == 0) {
150			pr_warn("%s: timed out (tx busy)\n", synth->long_name);
 
151			timeouts++;
152			return 0;
153		}
154		udelay(1);
155	}
156	tmout = SPK_CTS_TIMEOUT;
157	while (!((inb_p(speakup_info.port_tts + UART_MSR)) & UART_MSR_CTS)) {
158		/* CTS */
159		if (--tmout == 0) {
160			/* pr_warn("%s: timed out (cts)\n",
161			 * synth->long_name); */
162			timeouts++;
163			return 0;
164		}
165		udelay(1);
166	}
167	timeouts = 0;
168	return 1;
169}
170
171unsigned char spk_serial_in(void)
172{
173	int tmout = SPK_SERIAL_TIMEOUT;
174
175	while (!(inb_p(speakup_info.port_tts + UART_LSR) & UART_LSR_DR)) {
176		if (--tmout == 0) {
177			pr_warn("time out while waiting for input.\n");
178			return 0xff;
179		}
180		udelay(1);
181	}
182	return inb_p(speakup_info.port_tts + UART_RX);
183}
184EXPORT_SYMBOL_GPL(spk_serial_in);
185
186unsigned char spk_serial_in_nowait(void)
187{
188	unsigned char lsr;
189
190	lsr = inb_p(speakup_info.port_tts + UART_LSR);
191	if (!(lsr & UART_LSR_DR))
192		return 0;
193	return inb_p(speakup_info.port_tts + UART_RX);
194}
195EXPORT_SYMBOL_GPL(spk_serial_in_nowait);
196
197int spk_serial_out(const char ch)
198{
199	if (synth->alive && wait_for_xmitr()) {
 
 
 
 
 
200		outb_p(ch, speakup_info.port_tts);
201		return 1;
202	}
203	return 0;
204}
205EXPORT_SYMBOL_GPL(spk_serial_out);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
207void spk_serial_release(void)
208{
 
209	if (speakup_info.port_tts == 0)
210		return;
211	synth_release_region(speakup_info.port_tts, 8);
212	speakup_info.port_tts = 0;
213}
214EXPORT_SYMBOL_GPL(spk_serial_release);
215
v5.4
  1// SPDX-License-Identifier: GPL-2.0
  2#include <linux/interrupt.h>
  3#include <linux/ioport.h>
  4
  5#include "spk_types.h"
  6#include "speakup.h"
  7#include "spk_priv.h"
  8#include "serialio.h"
  9
 10#include <linux/serial_core.h>
 11/* WARNING:  Do not change this to <linux/serial.h> without testing that
 12 * SERIAL_PORT_DFNS does get defined to the appropriate value.
 13 */
 14#include <asm/serial.h>
 15
 16#ifndef SERIAL_PORT_DFNS
 17#define SERIAL_PORT_DFNS
 18#endif
 19
 20static void start_serial_interrupt(int irq);
 21
 22static const struct old_serial_port rs_table[] = {
 23	SERIAL_PORT_DFNS
 24};
 25
 26static const struct old_serial_port *serstate;
 27static int timeouts;
 28
 29static int spk_serial_out(struct spk_synth *in_synth, const char ch);
 30static void spk_serial_send_xchar(char ch);
 31static void spk_serial_tiocmset(unsigned int set, unsigned int clear);
 32static unsigned char spk_serial_in(void);
 33static unsigned char spk_serial_in_nowait(void);
 34static void spk_serial_flush_buffer(void);
 35
 36struct spk_io_ops spk_serial_io_ops = {
 37	.synth_out = spk_serial_out,
 38	.send_xchar = spk_serial_send_xchar,
 39	.tiocmset = spk_serial_tiocmset,
 40	.synth_in = spk_serial_in,
 41	.synth_in_nowait = spk_serial_in_nowait,
 42	.flush_buffer = spk_serial_flush_buffer,
 43};
 44EXPORT_SYMBOL_GPL(spk_serial_io_ops);
 45
 46const struct old_serial_port *spk_serial_init(int index)
 47{
 48	int baud = 9600, quot = 0;
 49	unsigned int cval = 0;
 50	int cflag = CREAD | HUPCL | CLOCAL | B9600 | CS8;
 51	const struct old_serial_port *ser;
 52	int err;
 53
 54	if (index >= ARRAY_SIZE(rs_table)) {
 55		pr_info("no port info for ttyS%d\n", index);
 56		return NULL;
 57	}
 58	ser = rs_table + index;
 59
 60	/*	Divisor, bytesize and parity */
 61	quot = ser->baud_base / baud;
 62	cval = cflag & (CSIZE | CSTOPB);
 63#if defined(__powerpc__) || defined(__alpha__)
 64	cval >>= 8;
 65#else /* !__powerpc__ && !__alpha__ */
 66	cval >>= 4;
 67#endif /* !__powerpc__ && !__alpha__ */
 68	if (cflag & PARENB)
 69		cval |= UART_LCR_PARITY;
 70	if (!(cflag & PARODD))
 71		cval |= UART_LCR_EPAR;
 72	if (synth_request_region(ser->port, 8)) {
 73		/* try to take it back. */
 74		pr_info("Ports not available, trying to steal them\n");
 75		__release_region(&ioport_resource, ser->port, 8);
 76		err = synth_request_region(ser->port, 8);
 77		if (err) {
 78			pr_warn("Unable to allocate port at %x, errno %i",
 79				ser->port, err);
 80			return NULL;
 81		}
 82	}
 83
 84	/*	Disable UART interrupts, set DTR and RTS high
 85	 *	and set speed.
 86	 */
 87	outb(cval | UART_LCR_DLAB, ser->port + UART_LCR);	/* set DLAB */
 88	outb(quot & 0xff, ser->port + UART_DLL);	/* LS of divisor */
 89	outb(quot >> 8, ser->port + UART_DLM);		/* MS of divisor */
 90	outb(cval, ser->port + UART_LCR);		/* reset DLAB */
 91
 92	/* Turn off Interrupts */
 93	outb(0, ser->port + UART_IER);
 94	outb(UART_MCR_DTR | UART_MCR_RTS, ser->port + UART_MCR);
 95
 96	/* If we read 0xff from the LSR, there is no UART here. */
 97	if (inb(ser->port + UART_LSR) == 0xff) {
 98		synth_release_region(ser->port, 8);
 99		serstate = NULL;
100		return NULL;
101	}
102
103	mdelay(1);
104	speakup_info.port_tts = ser->port;
105	serstate = ser;
106
107	start_serial_interrupt(ser->irq);
108
109	return ser;
110}
111
112static irqreturn_t synth_readbuf_handler(int irq, void *dev_id)
113{
114	unsigned long flags;
 
 
115	int c;
 
 
116
117	spin_lock_irqsave(&speakup_info.spinlock, flags);
118	while (inb_p(speakup_info.port_tts + UART_LSR) & UART_LSR_DR) {
119		c = inb_p(speakup_info.port_tts + UART_RX);
120		synth->read_buff_add((u_char)c);
121	}
122	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
123	return IRQ_HANDLED;
124}
125
126static void start_serial_interrupt(int irq)
127{
128	int rv;
129
130	if (!synth->read_buff_add)
131		return;
132
133	rv = request_irq(irq, synth_readbuf_handler, IRQF_SHARED,
134			 "serial", (void *)synth_readbuf_handler);
135
136	if (rv)
137		pr_err("Unable to request Speakup serial I R Q\n");
138	/* Set MCR */
139	outb(UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2,
140	     speakup_info.port_tts + UART_MCR);
141	/* Turn on Interrupts */
142	outb(UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI,
143	     speakup_info.port_tts + UART_IER);
144	inb(speakup_info.port_tts + UART_LSR);
145	inb(speakup_info.port_tts + UART_RX);
146	inb(speakup_info.port_tts + UART_IIR);
147	inb(speakup_info.port_tts + UART_MSR);
148	outb(1, speakup_info.port_tts + UART_FCR);	/* Turn FIFO On */
149}
150
151static void spk_serial_send_xchar(char ch)
152{
153	int timeout = SPK_XMITR_TIMEOUT;
154
155	while (spk_serial_tx_busy()) {
156		if (!--timeout)
157			break;
158		udelay(1);
159	}
160	outb(ch, speakup_info.port_tts);
161}
162
163static void spk_serial_tiocmset(unsigned int set, unsigned int clear)
164{
165	int old = inb(speakup_info.port_tts + UART_MCR);
166
167	outb((old & ~clear) | set, speakup_info.port_tts + UART_MCR);
168}
169
170int spk_serial_synth_probe(struct spk_synth *synth)
171{
172	const struct old_serial_port *ser;
173	int failed = 0;
174
175	if ((synth->ser >= SPK_LO_TTY) && (synth->ser <= SPK_HI_TTY)) {
176		ser = spk_serial_init(synth->ser);
177		if (!ser) {
178			failed = -1;
179		} else {
180			outb_p(0, ser->port);
181			mdelay(1);
182			outb_p('\r', ser->port);
183		}
184	} else {
185		failed = -1;
186		pr_warn("ttyS%i is an invalid port\n", synth->ser);
187	}
188	if (failed) {
189		pr_info("%s: not found\n", synth->long_name);
190		return -ENODEV;
191	}
192	pr_info("%s: ttyS%i, Driver Version %s\n",
193		synth->long_name, synth->ser, synth->version);
194	synth->alive = 1;
195	return 0;
196}
197EXPORT_SYMBOL_GPL(spk_serial_synth_probe);
198
199void spk_stop_serial_interrupt(void)
200{
201	if (speakup_info.port_tts == 0)
202		return;
203
204	if (!synth->read_buff_add)
205		return;
206
207	/* Turn off interrupts */
208	outb(0, speakup_info.port_tts + UART_IER);
209	/* Free IRQ */
210	free_irq(serstate->irq, (void *)synth_readbuf_handler);
211}
212EXPORT_SYMBOL_GPL(spk_stop_serial_interrupt);
213
214int spk_wait_for_xmitr(struct spk_synth *in_synth)
215{
216	int tmout = SPK_XMITR_TIMEOUT;
217
218	if ((in_synth->alive) && (timeouts >= NUM_DISABLE_TIMEOUTS)) {
219		pr_warn("%s: too many timeouts, deactivating speakup\n",
220			in_synth->long_name);
221		in_synth->alive = 0;
222		/* No synth any more, so nobody will restart TTYs, and we thus
223		 * need to do it ourselves.  Now that there is no synth we can
224		 * let application flood anyway
225		 */
226		speakup_start_ttys();
227		timeouts = 0;
228		return 0;
229	}
230	while (spk_serial_tx_busy()) {
231		if (--tmout == 0) {
232			pr_warn("%s: timed out (tx busy)\n",
233				in_synth->long_name);
234			timeouts++;
235			return 0;
236		}
237		udelay(1);
238	}
239	tmout = SPK_CTS_TIMEOUT;
240	while (!((inb_p(speakup_info.port_tts + UART_MSR)) & UART_MSR_CTS)) {
241		/* CTS */
242		if (--tmout == 0) {
 
 
243			timeouts++;
244			return 0;
245		}
246		udelay(1);
247	}
248	timeouts = 0;
249	return 1;
250}
251
252static unsigned char spk_serial_in(void)
253{
254	int tmout = SPK_SERIAL_TIMEOUT;
255
256	while (!(inb_p(speakup_info.port_tts + UART_LSR) & UART_LSR_DR)) {
257		if (--tmout == 0) {
258			pr_warn("time out while waiting for input.\n");
259			return 0xff;
260		}
261		udelay(1);
262	}
263	return inb_p(speakup_info.port_tts + UART_RX);
264}
 
265
266static unsigned char spk_serial_in_nowait(void)
267{
268	unsigned char lsr;
269
270	lsr = inb_p(speakup_info.port_tts + UART_LSR);
271	if (!(lsr & UART_LSR_DR))
272		return 0;
273	return inb_p(speakup_info.port_tts + UART_RX);
274}
 
275
276static void spk_serial_flush_buffer(void)
277{
278	/* TODO: flush the UART 16550 buffer */
279}
280
281static int spk_serial_out(struct spk_synth *in_synth, const char ch)
282{
283	if (in_synth->alive && spk_wait_for_xmitr(in_synth)) {
284		outb_p(ch, speakup_info.port_tts);
285		return 1;
286	}
287	return 0;
288}
289
290const char *spk_serial_synth_immediate(struct spk_synth *synth,
291				       const char *buff)
292{
293	u_char ch;
294
295	while ((ch = *buff)) {
296		if (ch == '\n')
297			ch = synth->procspeech;
298		if (spk_wait_for_xmitr(synth))
299			outb(ch, speakup_info.port_tts);
300		else
301			return buff;
302		buff++;
303	}
304	return NULL;
305}
306EXPORT_SYMBOL_GPL(spk_serial_synth_immediate);
307
308void spk_serial_release(void)
309{
310	spk_stop_serial_interrupt();
311	if (speakup_info.port_tts == 0)
312		return;
313	synth_release_region(speakup_info.port_tts, 8);
314	speakup_info.port_tts = 0;
315}
316EXPORT_SYMBOL_GPL(spk_serial_release);