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