xref: /linux/drivers/accessibility/speakup/serialio.c (revision 23de9e6a25d19e57875a7ca9daed2aeaf31d3bdc)
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 #include <linux/serial.h>
12 
13 static int timeouts;
14 
15 static int spk_serial_out(struct spk_synth *in_synth, const char ch);
16 static void spk_serial_send_xchar(struct spk_synth *in_synth, char ch);
17 static void spk_serial_tiocmset(struct spk_synth *in_synth, unsigned int set, unsigned int clear);
18 static unsigned char spk_serial_in(struct spk_synth *in_synth);
19 static unsigned char spk_serial_in_nowait(struct spk_synth *in_synth);
20 static void spk_serial_flush_buffer(struct spk_synth *in_synth);
21 static int spk_serial_wait_for_xmitr(struct spk_synth *in_synth);
22 
23 struct spk_io_ops spk_serial_io_ops = {
24 	.synth_out = spk_serial_out,
25 	.send_xchar = spk_serial_send_xchar,
26 	.tiocmset = spk_serial_tiocmset,
27 	.synth_in = spk_serial_in,
28 	.synth_in_nowait = spk_serial_in_nowait,
29 	.flush_buffer = spk_serial_flush_buffer,
30 	.wait_for_xmitr = spk_serial_wait_for_xmitr,
31 };
32 EXPORT_SYMBOL_GPL(spk_serial_io_ops);
33 
34 static void spk_serial_send_xchar(struct spk_synth *synth, char ch)
35 {
36 	int timeout = SPK_XMITR_TIMEOUT;
37 
38 	while (spk_serial_tx_busy()) {
39 		if (!--timeout)
40 			break;
41 		udelay(1);
42 	}
43 	outb(ch, speakup_info.port_tts);
44 }
45 
46 static void spk_serial_tiocmset(struct spk_synth *in_synth, unsigned int set, unsigned int clear)
47 {
48 	int old = inb(speakup_info.port_tts + UART_MCR);
49 
50 	outb((old & ~clear) | set, speakup_info.port_tts + UART_MCR);
51 }
52 
53 static int spk_serial_wait_for_xmitr(struct spk_synth *in_synth)
54 {
55 	int tmout = SPK_XMITR_TIMEOUT;
56 
57 	if ((in_synth->alive) && (timeouts >= NUM_DISABLE_TIMEOUTS)) {
58 		pr_warn("%s: too many timeouts, deactivating speakup\n",
59 			in_synth->long_name);
60 		in_synth->alive = 0;
61 		/* No synth any more, so nobody will restart TTYs, and we thus
62 		 * need to do it ourselves.  Now that there is no synth we can
63 		 * let application flood anyway
64 		 */
65 		speakup_start_ttys();
66 		timeouts = 0;
67 		return 0;
68 	}
69 	while (spk_serial_tx_busy()) {
70 		if (--tmout == 0) {
71 			pr_warn("%s: timed out (tx busy)\n",
72 				in_synth->long_name);
73 			timeouts++;
74 			return 0;
75 		}
76 		udelay(1);
77 	}
78 	tmout = SPK_CTS_TIMEOUT;
79 	while (!((inb_p(speakup_info.port_tts + UART_MSR)) & UART_MSR_CTS)) {
80 		/* CTS */
81 		if (--tmout == 0) {
82 			timeouts++;
83 			return 0;
84 		}
85 		udelay(1);
86 	}
87 	timeouts = 0;
88 	return 1;
89 }
90 
91 static unsigned char spk_serial_in(struct spk_synth *in_synth)
92 {
93 	int tmout = SPK_SERIAL_TIMEOUT;
94 
95 	while (!(inb_p(speakup_info.port_tts + UART_LSR) & UART_LSR_DR)) {
96 		if (--tmout == 0) {
97 			pr_warn("time out while waiting for input.\n");
98 			return 0xff;
99 		}
100 		udelay(1);
101 	}
102 	return inb_p(speakup_info.port_tts + UART_RX);
103 }
104 
105 static unsigned char spk_serial_in_nowait(struct spk_synth *in_synth)
106 {
107 	unsigned char lsr;
108 
109 	lsr = inb_p(speakup_info.port_tts + UART_LSR);
110 	if (!(lsr & UART_LSR_DR))
111 		return 0;
112 	return inb_p(speakup_info.port_tts + UART_RX);
113 }
114 
115 static void spk_serial_flush_buffer(struct spk_synth *in_synth)
116 {
117 	/* TODO: flush the UART 16550 buffer */
118 }
119 
120 static int spk_serial_out(struct spk_synth *in_synth, const char ch)
121 {
122 	if (in_synth->alive && spk_serial_wait_for_xmitr(in_synth)) {
123 		outb_p(ch, speakup_info.port_tts);
124 		return 1;
125 	}
126 	return 0;
127 }
128