Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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