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