Commit | Line | Data |
---|---|---|
984263bc MD |
1 | /*- |
2 | * Copyright (c) 1991 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. All advertising materials mentioning features or use of this software | |
14 | * must display the following acknowledgement: | |
15 | * This product includes software developed by the University of | |
16 | * California, Berkeley and its contributors. | |
17 | * 4. Neither the name of the University nor the names of its contributors | |
18 | * may be used to endorse or promote products derived from this software | |
19 | * without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
31 | * SUCH DAMAGE. | |
32 | * | |
33 | * $FreeBSD: src/sys/isa/sio.c,v 1.291.2.35 2003/05/18 08:51:15 murray Exp $ | |
34 | * from: @(#)com.c 7.5 (Berkeley) 5/16/91 | |
35 | * from: i386/isa sio.c,v 1.234 | |
36 | */ | |
37 | ||
38 | #include "opt_comconsole.h" | |
39 | #include "opt_compat.h" | |
40 | #include "opt_ddb.h" | |
41 | #include "opt_sio.h" | |
42 | #include "card.h" | |
43 | #include "pci.h" | |
44 | #ifdef __i386__ | |
45 | #include "puc.h" | |
46 | #endif | |
47 | #include "sio.h" | |
48 | ||
49 | /* | |
50 | * Serial driver, based on 386BSD-0.1 com driver. | |
51 | * Mostly rewritten to use pseudo-DMA. | |
52 | * Works for National Semiconductor NS8250-NS16550AF UARTs. | |
53 | * COM driver, based on HP dca driver. | |
54 | * | |
55 | * Changes for PC-Card integration: | |
56 | * - Added PC-Card driver table and handlers | |
57 | */ | |
58 | #include <sys/param.h> | |
59 | #include <sys/systm.h> | |
60 | #include <sys/reboot.h> | |
61 | #include <sys/malloc.h> | |
62 | #include <sys/tty.h> | |
63 | #include <sys/proc.h> | |
64 | #include <sys/module.h> | |
65 | #include <sys/conf.h> | |
66 | #include <sys/dkstat.h> | |
67 | #include <sys/fcntl.h> | |
68 | #include <sys/interrupt.h> | |
69 | #include <sys/kernel.h> | |
70 | #include <sys/syslog.h> | |
71 | #include <sys/sysctl.h> | |
72 | #include <sys/bus.h> | |
73 | #include <machine/bus_pio.h> | |
74 | #include <machine/bus.h> | |
75 | #include <sys/rman.h> | |
76 | #include <sys/timepps.h> | |
77 | ||
78 | #include <machine/limits.h> | |
79 | ||
80 | #include <isa/isareg.h> | |
81 | #include <isa/isavar.h> | |
82 | #if NPCI > 0 | |
83 | #include <pci/pcireg.h> | |
84 | #include <pci/pcivar.h> | |
85 | #endif | |
86 | #if NPUC > 0 | |
87 | #include <dev/puc/pucvar.h> | |
88 | #endif | |
89 | #include <machine/lock.h> | |
90 | ||
91 | #include <machine/clock.h> | |
92 | #include <machine/ipl.h> | |
93 | #ifndef SMP | |
94 | #include <machine/lock.h> | |
95 | #endif | |
96 | #include <machine/resource.h> | |
97 | ||
98 | #include <isa/sioreg.h> | |
99 | ||
100 | #ifdef COM_ESP | |
101 | #include <dev/ic/esp.h> | |
102 | #endif | |
103 | #include <dev/ic/ns16550.h> | |
104 | ||
105 | #ifndef __i386__ | |
106 | #define disable_intr() | |
107 | #define enable_intr() | |
108 | #endif | |
109 | ||
110 | #ifdef SMP | |
111 | #define disable_intr() COM_DISABLE_INTR() | |
112 | #define enable_intr() COM_ENABLE_INTR() | |
113 | #endif /* SMP */ | |
114 | ||
115 | #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ | |
116 | ||
117 | #define CALLOUT_MASK 0x80 | |
118 | #define CONTROL_MASK 0x60 | |
119 | #define CONTROL_INIT_STATE 0x20 | |
120 | #define CONTROL_LOCK_STATE 0x40 | |
121 | #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) | |
122 | #define MINOR_TO_UNIT(mynor) ((((mynor) & ~0xffffU) >> (8 + 3)) \ | |
123 | | ((mynor) & 0x1f)) | |
124 | #define UNIT_TO_MINOR(unit) ((((unit) & ~0x1fU) << (8 + 3)) \ | |
125 | | ((unit) & 0x1f)) | |
126 | ||
127 | #ifdef COM_MULTIPORT | |
128 | /* checks in flags for multiport and which is multiport "master chip" | |
129 | * for a given card | |
130 | */ | |
131 | #define COM_ISMULTIPORT(flags) ((flags) & 0x01) | |
132 | #define COM_MPMASTER(flags) (((flags) >> 8) & 0x0ff) | |
133 | #define COM_NOTAST4(flags) ((flags) & 0x04) | |
134 | #endif /* COM_MULTIPORT */ | |
135 | ||
136 | #define COM_CONSOLE(flags) ((flags) & 0x10) | |
137 | #define COM_FORCECONSOLE(flags) ((flags) & 0x20) | |
138 | #define COM_LLCONSOLE(flags) ((flags) & 0x40) | |
139 | #define COM_DEBUGGER(flags) ((flags) & 0x80) | |
140 | #define COM_LOSESOUTINTS(flags) ((flags) & 0x08) | |
141 | #define COM_NOFIFO(flags) ((flags) & 0x02) | |
142 | #define COM_ST16650A(flags) ((flags) & 0x20000) | |
143 | #define COM_C_NOPROBE (0x40000) | |
144 | #define COM_NOPROBE(flags) ((flags) & COM_C_NOPROBE) | |
145 | #define COM_C_IIR_TXRDYBUG (0x80000) | |
146 | #define COM_IIR_TXRDYBUG(flags) ((flags) & COM_C_IIR_TXRDYBUG) | |
147 | #define COM_TI16754(flags) ((flags) & 0x200000) | |
148 | #define COM_FIFOSIZE(flags) (((flags) & 0xff000000) >> 24) | |
149 | ||
150 | #define com_scr 7 /* scratch register for 16450-16550 (R/W) */ | |
151 | ||
152 | #define sio_getreg(com, off) \ | |
153 | (bus_space_read_1((com)->bst, (com)->bsh, (off))) | |
154 | #define sio_setreg(com, off, value) \ | |
155 | (bus_space_write_1((com)->bst, (com)->bsh, (off), (value))) | |
156 | ||
157 | /* | |
158 | * com state bits. | |
159 | * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher | |
160 | * than the other bits so that they can be tested as a group without masking | |
161 | * off the low bits. | |
162 | * | |
163 | * The following com and tty flags correspond closely: | |
164 | * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and | |
165 | * comstop()) | |
166 | * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart()) | |
167 | * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) | |
168 | * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) | |
169 | * TS_FLUSH is not used. | |
170 | * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. | |
171 | * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). | |
172 | */ | |
173 | #define CS_BUSY 0x80 /* output in progress */ | |
174 | #define CS_TTGO 0x40 /* output not stopped by XOFF */ | |
175 | #define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ | |
176 | #define CS_CHECKMSR 1 /* check of MSR scheduled */ | |
177 | #define CS_CTS_OFLOW 2 /* use CTS output flow control */ | |
178 | #define CS_DTR_OFF 0x10 /* DTR held off */ | |
179 | #define CS_ODONE 4 /* output completed */ | |
180 | #define CS_RTS_IFLOW 8 /* use RTS input flow control */ | |
181 | #define CSE_BUSYCHECK 1 /* siobusycheck() scheduled */ | |
182 | ||
183 | static char const * const error_desc[] = { | |
184 | #define CE_OVERRUN 0 | |
185 | "silo overflow", | |
186 | #define CE_INTERRUPT_BUF_OVERFLOW 1 | |
187 | "interrupt-level buffer overflow", | |
188 | #define CE_TTY_BUF_OVERFLOW 2 | |
189 | "tty-level buffer overflow", | |
190 | }; | |
191 | ||
192 | #define CE_NTYPES 3 | |
193 | #define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) | |
194 | ||
195 | /* types. XXX - should be elsewhere */ | |
196 | typedef u_int Port_t; /* hardware port */ | |
197 | typedef u_char bool_t; /* boolean */ | |
198 | ||
199 | /* queue of linear buffers */ | |
200 | struct lbq { | |
201 | u_char *l_head; /* next char to process */ | |
202 | u_char *l_tail; /* one past the last char to process */ | |
203 | struct lbq *l_next; /* next in queue */ | |
204 | bool_t l_queued; /* nonzero if queued */ | |
205 | }; | |
206 | ||
207 | /* com device structure */ | |
208 | struct com_s { | |
209 | u_int flags; /* Copy isa device flags */ | |
210 | u_char state; /* miscellaneous flag bits */ | |
211 | bool_t active_out; /* nonzero if the callout device is open */ | |
212 | u_char cfcr_image; /* copy of value written to CFCR */ | |
213 | #ifdef COM_ESP | |
214 | bool_t esp; /* is this unit a hayes esp board? */ | |
215 | #endif | |
216 | u_char extra_state; /* more flag bits, separate for order trick */ | |
217 | u_char fifo_image; /* copy of value written to FIFO */ | |
218 | bool_t hasfifo; /* nonzero for 16550 UARTs */ | |
219 | bool_t st16650a; /* Is a Startech 16650A or RTS/CTS compat */ | |
220 | bool_t loses_outints; /* nonzero if device loses output interrupts */ | |
221 | u_char mcr_image; /* copy of value written to MCR */ | |
222 | #ifdef COM_MULTIPORT | |
223 | bool_t multiport; /* is this unit part of a multiport device? */ | |
224 | #endif /* COM_MULTIPORT */ | |
225 | bool_t no_irq; /* nonzero if irq is not attached */ | |
226 | bool_t gone; /* hardware disappeared */ | |
227 | bool_t poll; /* nonzero if polling is required */ | |
228 | bool_t poll_output; /* nonzero if polling for output is required */ | |
229 | int unit; /* unit number */ | |
230 | int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ | |
231 | u_int tx_fifo_size; | |
232 | u_int wopeners; /* # processes waiting for DCD in open() */ | |
233 | ||
234 | /* | |
235 | * The high level of the driver never reads status registers directly | |
236 | * because there would be too many side effects to handle conveniently. | |
237 | * Instead, it reads copies of the registers stored here by the | |
238 | * interrupt handler. | |
239 | */ | |
240 | u_char last_modem_status; /* last MSR read by intr handler */ | |
241 | u_char prev_modem_status; /* last MSR handled by high level */ | |
242 | ||
243 | u_char hotchar; /* ldisc-specific char to be handled ASAP */ | |
244 | u_char *ibuf; /* start of input buffer */ | |
245 | u_char *ibufend; /* end of input buffer */ | |
246 | u_char *ibufold; /* old input buffer, to be freed */ | |
247 | u_char *ihighwater; /* threshold in input buffer */ | |
248 | u_char *iptr; /* next free spot in input buffer */ | |
249 | int ibufsize; /* size of ibuf (not include error bytes) */ | |
250 | int ierroff; /* offset of error bytes in ibuf */ | |
251 | ||
252 | struct lbq obufq; /* head of queue of output buffers */ | |
253 | struct lbq obufs[2]; /* output buffers */ | |
254 | ||
255 | bus_space_tag_t bst; | |
256 | bus_space_handle_t bsh; | |
257 | ||
258 | Port_t data_port; /* i/o ports */ | |
259 | #ifdef COM_ESP | |
260 | Port_t esp_port; | |
261 | #endif | |
262 | Port_t int_id_port; | |
263 | Port_t modem_ctl_port; | |
264 | Port_t line_status_port; | |
265 | Port_t modem_status_port; | |
266 | Port_t intr_ctl_port; /* Ports of IIR register */ | |
267 | ||
268 | struct tty *tp; /* cross reference */ | |
269 | ||
270 | /* Initial state. */ | |
271 | struct termios it_in; /* should be in struct tty */ | |
272 | struct termios it_out; | |
273 | ||
274 | /* Lock state. */ | |
275 | struct termios lt_in; /* should be in struct tty */ | |
276 | struct termios lt_out; | |
277 | ||
278 | bool_t do_timestamp; | |
279 | bool_t do_dcd_timestamp; | |
280 | struct timeval timestamp; | |
281 | struct timeval dcd_timestamp; | |
282 | struct pps_state pps; | |
283 | ||
284 | u_long bytes_in; /* statistics */ | |
285 | u_long bytes_out; | |
286 | u_int delta_error_counts[CE_NTYPES]; | |
287 | u_long error_counts[CE_NTYPES]; | |
288 | ||
289 | u_long rclk; | |
290 | ||
291 | struct resource *irqres; | |
292 | struct resource *ioportres; | |
293 | void *cookie; | |
294 | ||
295 | /* | |
296 | * Data area for output buffers. Someday we should build the output | |
297 | * buffer queue without copying data. | |
298 | */ | |
299 | u_char obuf1[256]; | |
300 | u_char obuf2[256]; | |
301 | }; | |
302 | ||
303 | #ifdef COM_ESP | |
304 | static int espattach __P((struct com_s *com, Port_t esp_port)); | |
305 | #endif | |
306 | static int sioattach __P((device_t dev, int rid, u_long rclk)); | |
307 | static int sio_isa_attach __P((device_t dev)); | |
308 | ||
309 | static timeout_t siobusycheck; | |
310 | static u_int siodivisor __P((u_long rclk, speed_t speed)); | |
311 | static timeout_t siodtrwakeup; | |
312 | static void comhardclose __P((struct com_s *com)); | |
313 | static void sioinput __P((struct com_s *com)); | |
314 | static void siointr1 __P((struct com_s *com)); | |
315 | static void siointr __P((void *arg)); | |
316 | static int commctl __P((struct com_s *com, int bits, int how)); | |
317 | static int comparam __P((struct tty *tp, struct termios *t)); | |
318 | static swihand_t siopoll; | |
319 | static int sioprobe __P((device_t dev, int xrid, u_long rclk)); | |
320 | static int sio_isa_probe __P((device_t dev)); | |
321 | static void siosettimeout __P((void)); | |
322 | static int siosetwater __P((struct com_s *com, speed_t speed)); | |
323 | static void comstart __P((struct tty *tp)); | |
324 | static void comstop __P((struct tty *tp, int rw)); | |
325 | static timeout_t comwakeup; | |
326 | static void disc_optim __P((struct tty *tp, struct termios *t, | |
327 | struct com_s *com)); | |
328 | ||
329 | #if NCARD > 0 | |
330 | static int sio_pccard_attach __P((device_t dev)); | |
331 | static int sio_pccard_detach __P((device_t dev)); | |
332 | static int sio_pccard_probe __P((device_t dev)); | |
333 | #endif /* NCARD > 0 */ | |
334 | ||
335 | #if NPCI > 0 | |
336 | static int sio_pci_attach __P((device_t dev)); | |
337 | static void sio_pci_kludge_unit __P((device_t dev)); | |
338 | static int sio_pci_probe __P((device_t dev)); | |
339 | #endif /* NPCI > 0 */ | |
340 | ||
341 | #if NPUC > 0 | |
342 | static int sio_puc_attach __P((device_t dev)); | |
343 | static int sio_puc_probe __P((device_t dev)); | |
344 | #endif /* NPUC > 0 */ | |
345 | ||
346 | static char driver_name[] = "sio"; | |
347 | ||
348 | /* table and macro for fast conversion from a unit number to its com struct */ | |
349 | static devclass_t sio_devclass; | |
350 | #define com_addr(unit) ((struct com_s *) \ | |
351 | devclass_get_softc(sio_devclass, unit)) | |
352 | ||
353 | static device_method_t sio_isa_methods[] = { | |
354 | /* Device interface */ | |
355 | DEVMETHOD(device_probe, sio_isa_probe), | |
356 | DEVMETHOD(device_attach, sio_isa_attach), | |
357 | ||
358 | { 0, 0 } | |
359 | }; | |
360 | ||
361 | static driver_t sio_isa_driver = { | |
362 | driver_name, | |
363 | sio_isa_methods, | |
364 | sizeof(struct com_s), | |
365 | }; | |
366 | ||
367 | #if NCARD > 0 | |
368 | static device_method_t sio_pccard_methods[] = { | |
369 | /* Device interface */ | |
370 | DEVMETHOD(device_probe, sio_pccard_probe), | |
371 | DEVMETHOD(device_attach, sio_pccard_attach), | |
372 | DEVMETHOD(device_detach, sio_pccard_detach), | |
373 | ||
374 | { 0, 0 } | |
375 | }; | |
376 | ||
377 | static driver_t sio_pccard_driver = { | |
378 | driver_name, | |
379 | sio_pccard_methods, | |
380 | sizeof(struct com_s), | |
381 | }; | |
382 | #endif /* NCARD > 0 */ | |
383 | ||
384 | #if NPCI > 0 | |
385 | static device_method_t sio_pci_methods[] = { | |
386 | /* Device interface */ | |
387 | DEVMETHOD(device_probe, sio_pci_probe), | |
388 | DEVMETHOD(device_attach, sio_pci_attach), | |
389 | ||
390 | { 0, 0 } | |
391 | }; | |
392 | ||
393 | static driver_t sio_pci_driver = { | |
394 | driver_name, | |
395 | sio_pci_methods, | |
396 | sizeof(struct com_s), | |
397 | }; | |
398 | #endif /* NPCI > 0 */ | |
399 | ||
400 | #if NPUC > 0 | |
401 | static device_method_t sio_puc_methods[] = { | |
402 | /* Device interface */ | |
403 | DEVMETHOD(device_probe, sio_puc_probe), | |
404 | DEVMETHOD(device_attach, sio_puc_attach), | |
405 | ||
406 | { 0, 0 } | |
407 | }; | |
408 | ||
409 | static driver_t sio_puc_driver = { | |
410 | driver_name, | |
411 | sio_puc_methods, | |
412 | sizeof(struct com_s), | |
413 | }; | |
414 | #endif /* NPUC > 0 */ | |
415 | ||
416 | static d_open_t sioopen; | |
417 | static d_close_t sioclose; | |
418 | static d_read_t sioread; | |
419 | static d_write_t siowrite; | |
420 | static d_ioctl_t sioioctl; | |
421 | ||
422 | #define CDEV_MAJOR 28 | |
423 | static struct cdevsw sio_cdevsw = { | |
424 | /* open */ sioopen, | |
425 | /* close */ sioclose, | |
426 | /* read */ sioread, | |
427 | /* write */ siowrite, | |
428 | /* ioctl */ sioioctl, | |
429 | /* poll */ ttypoll, | |
430 | /* mmap */ nommap, | |
431 | /* strategy */ nostrategy, | |
432 | /* name */ driver_name, | |
433 | /* maj */ CDEV_MAJOR, | |
434 | /* dump */ nodump, | |
435 | /* psize */ nopsize, | |
436 | /* flags */ D_TTY | D_KQFILTER, | |
437 | /* bmaj */ -1, | |
438 | /* kqfilter */ ttykqfilter, | |
439 | }; | |
440 | ||
441 | int comconsole = -1; | |
442 | static volatile speed_t comdefaultrate = CONSPEED; | |
443 | static u_long comdefaultrclk = DEFAULT_RCLK; | |
444 | SYSCTL_ULONG(_machdep, OID_AUTO, conrclk, CTLFLAG_RW, &comdefaultrclk, 0, ""); | |
445 | #ifdef __alpha__ | |
446 | static volatile speed_t gdbdefaultrate = CONSPEED; | |
447 | #endif | |
448 | static u_int com_events; /* input chars + weighted output completions */ | |
449 | static Port_t siocniobase; | |
450 | static int siocnunit; | |
451 | static Port_t siogdbiobase; | |
452 | static int siogdbunit = -1; | |
453 | static bool_t sio_registered; | |
454 | static int sio_timeout; | |
455 | static int sio_timeouts_until_log; | |
456 | static struct callout_handle sio_timeout_handle | |
457 | = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle); | |
458 | static int sio_numunits; | |
459 | ||
460 | #ifdef COM_ESP | |
461 | /* XXX configure this properly. */ | |
462 | static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, }; | |
463 | static Port_t likely_esp_ports[] = { 0x140, 0x180, 0x280, 0 }; | |
464 | #endif | |
465 | ||
466 | /* | |
467 | * handle sysctl read/write requests for console speed | |
468 | * | |
469 | * In addition to setting comdefaultrate for I/O through /dev/console, | |
470 | * also set the initial and lock values for the /dev/ttyXX device | |
471 | * if there is one associated with the console. Finally, if the /dev/tty | |
472 | * device has already been open, change the speed on the open running port | |
473 | * itself. | |
474 | */ | |
475 | ||
476 | static int | |
477 | sysctl_machdep_comdefaultrate(SYSCTL_HANDLER_ARGS) | |
478 | { | |
479 | int error, s; | |
480 | speed_t newspeed; | |
481 | struct com_s *com; | |
482 | struct tty *tp; | |
483 | ||
484 | newspeed = comdefaultrate; | |
485 | ||
486 | error = sysctl_handle_opaque(oidp, &newspeed, sizeof newspeed, req); | |
487 | if (error || !req->newptr) | |
488 | return (error); | |
489 | ||
490 | comdefaultrate = newspeed; | |
491 | ||
492 | if (comconsole < 0) /* serial console not selected? */ | |
493 | return (0); | |
494 | ||
495 | com = com_addr(comconsole); | |
496 | if (com == NULL) | |
497 | return (ENXIO); | |
498 | ||
499 | /* | |
500 | * set the initial and lock rates for /dev/ttydXX and /dev/cuaXX | |
501 | * (note, the lock rates really are boolean -- if non-zero, disallow | |
502 | * speed changes) | |
503 | */ | |
504 | com->it_in.c_ispeed = com->it_in.c_ospeed = | |
505 | com->lt_in.c_ispeed = com->lt_in.c_ospeed = | |
506 | com->it_out.c_ispeed = com->it_out.c_ospeed = | |
507 | com->lt_out.c_ispeed = com->lt_out.c_ospeed = comdefaultrate; | |
508 | ||
509 | /* | |
510 | * if we're open, change the running rate too | |
511 | */ | |
512 | tp = com->tp; | |
513 | if (tp && (tp->t_state & TS_ISOPEN)) { | |
514 | tp->t_termios.c_ispeed = | |
515 | tp->t_termios.c_ospeed = comdefaultrate; | |
516 | s = spltty(); | |
517 | error = comparam(tp, &tp->t_termios); | |
518 | splx(s); | |
519 | } | |
520 | return error; | |
521 | } | |
522 | ||
523 | SYSCTL_PROC(_machdep, OID_AUTO, conspeed, CTLTYPE_INT | CTLFLAG_RW, | |
524 | 0, 0, sysctl_machdep_comdefaultrate, "I", ""); | |
525 | ||
526 | #define SET_FLAG(dev, bit) device_set_flags(dev, device_get_flags(dev) | (bit)) | |
527 | #define CLR_FLAG(dev, bit) device_set_flags(dev, device_get_flags(dev) & ~(bit)) | |
528 | ||
529 | #if NCARD > 0 | |
530 | static int | |
531 | sio_pccard_probe(dev) | |
532 | device_t dev; | |
533 | { | |
534 | /* Do not probe IRQ - pccard doesn't turn on the interrupt line */ | |
535 | /* until bus_setup_intr */ | |
536 | SET_FLAG(dev, COM_C_NOPROBE); | |
537 | ||
538 | return (sioprobe(dev, 0, 0UL)); | |
539 | } | |
540 | ||
541 | static int | |
542 | sio_pccard_attach(dev) | |
543 | device_t dev; | |
544 | { | |
545 | return (sioattach(dev, 0, 0UL)); | |
546 | } | |
547 | ||
548 | /* | |
549 | * sio_detach - unload the driver and clear the table. | |
550 | * XXX TODO: | |
551 | * This is usually called when the card is ejected, but | |
552 | * can be caused by a modunload of a controller driver. | |
553 | * The idea is to reset the driver's view of the device | |
554 | * and ensure that any driver entry points such as | |
555 | * read and write do not hang. | |
556 | */ | |
557 | static int | |
558 | sio_pccard_detach(dev) | |
559 | device_t dev; | |
560 | { | |
561 | struct com_s *com; | |
562 | ||
563 | com = (struct com_s *) device_get_softc(dev); | |
564 | if (com == NULL) { | |
565 | device_printf(dev, "NULL com in siounload\n"); | |
566 | return (0); | |
567 | } | |
568 | com->gone = 1; | |
569 | if (com->irqres) { | |
570 | bus_teardown_intr(dev, com->irqres, com->cookie); | |
571 | bus_release_resource(dev, SYS_RES_IRQ, 0, com->irqres); | |
572 | } | |
573 | if (com->ioportres) | |
574 | bus_release_resource(dev, SYS_RES_IOPORT, 0, com->ioportres); | |
575 | if (com->tp && (com->tp->t_state & TS_ISOPEN)) { | |
576 | device_printf(dev, "still open, forcing close\n"); | |
577 | com->tp->t_gen++; | |
578 | ttyclose(com->tp); | |
579 | ttwakeup(com->tp); | |
580 | ttwwakeup(com->tp); | |
581 | } else { | |
582 | if (com->ibuf != NULL) | |
583 | free(com->ibuf, M_DEVBUF); | |
584 | } | |
585 | device_printf(dev, "unloaded\n"); | |
586 | return (0); | |
587 | } | |
588 | #endif /* NCARD > 0 */ | |
589 | ||
590 | #if NPCI > 0 | |
591 | struct pci_ids { | |
592 | u_int32_t type; | |
593 | const char *desc; | |
594 | int rid; | |
595 | }; | |
596 | ||
597 | static struct pci_ids pci_ids[] = { | |
598 | { 0x100812b9, "3COM PCI FaxModem", 0x10 }, | |
599 | { 0x2000131f, "CyberSerial (1-port) 16550", 0x10 }, | |
600 | { 0x01101407, "Koutech IOFLEX-2S PCI Dual Port Serial", 0x10 }, | |
601 | { 0x01111407, "Koutech IOFLEX-2S PCI Dual Port Serial", 0x10 }, | |
602 | { 0x048011c1, "Lucent kermit based PCI Modem", 0x14 }, | |
603 | { 0x95211415, "Oxford Semiconductor PCI Dual Port Serial", 0x10 }, | |
604 | { 0x7101135e, "SeaLevel Ultra 530.PCI Single Port Serial", 0x18 }, | |
605 | { 0x0000151f, "SmartLink 5634PCV SurfRider", 0x10 }, | |
606 | { 0x98459710, "Netmos Nm9845 PCI Bridge with Dual UART", 0x10 }, | |
607 | { 0x00000000, NULL, 0 } | |
608 | }; | |
609 | ||
610 | static int | |
611 | sio_pci_attach(dev) | |
612 | device_t dev; | |
613 | { | |
614 | u_int32_t type; | |
615 | struct pci_ids *id; | |
616 | ||
617 | type = pci_get_devid(dev); | |
618 | id = pci_ids; | |
619 | while (id->type && id->type != type) | |
620 | id++; | |
621 | if (id->desc == NULL) | |
622 | return (ENXIO); | |
623 | sio_pci_kludge_unit(dev); | |
624 | return (sioattach(dev, id->rid, 0UL)); | |
625 | } | |
626 | ||
627 | /* | |
628 | * Don't cut and paste this to other drivers. It is a horrible kludge | |
629 | * which will fail to work and also be unnecessary in future versions. | |
630 | */ | |
631 | static void | |
632 | sio_pci_kludge_unit(dev) | |
633 | device_t dev; | |
634 | { | |
635 | devclass_t dc; | |
636 | int err; | |
637 | int start; | |
638 | int unit; | |
639 | ||
640 | unit = 0; | |
641 | start = 0; | |
642 | while (resource_int_value("sio", unit, "port", &start) == 0 && | |
643 | start > 0) | |
644 | unit++; | |
645 | if (device_get_unit(dev) < unit) { | |
646 | dc = device_get_devclass(dev); | |
647 | while (devclass_get_device(dc, unit)) | |
648 | unit++; | |
649 | device_printf(dev, "moving to sio%d\n", unit); | |
650 | err = device_set_unit(dev, unit); /* EVIL DO NOT COPY */ | |
651 | if (err) | |
652 | device_printf(dev, "error moving device %d\n", err); | |
653 | } | |
654 | } | |
655 | ||
656 | static int | |
657 | sio_pci_probe(dev) | |
658 | device_t dev; | |
659 | { | |
660 | u_int32_t type; | |
661 | struct pci_ids *id; | |
662 | ||
663 | type = pci_get_devid(dev); | |
664 | id = pci_ids; | |
665 | while (id->type && id->type != type) | |
666 | id++; | |
667 | if (id->desc == NULL) | |
668 | return (ENXIO); | |
669 | device_set_desc(dev, id->desc); | |
670 | return (sioprobe(dev, id->rid, 0UL)); | |
671 | } | |
672 | #endif /* NPCI > 0 */ | |
673 | ||
674 | #if NPUC > 0 | |
675 | static int | |
676 | sio_puc_attach(dev) | |
677 | device_t dev; | |
678 | { | |
679 | u_int rclk; | |
680 | ||
681 | if (BUS_READ_IVAR(device_get_parent(dev), dev, PUC_IVAR_FREQ, | |
682 | &rclk) != 0) | |
683 | rclk = DEFAULT_RCLK; | |
684 | return (sioattach(dev, 0, rclk)); | |
685 | } | |
686 | ||
687 | static int | |
688 | sio_puc_probe(dev) | |
689 | device_t dev; | |
690 | { | |
691 | u_int rclk; | |
692 | ||
693 | if (BUS_READ_IVAR(device_get_parent(dev), dev, PUC_IVAR_FREQ, | |
694 | &rclk) != 0) | |
695 | rclk = DEFAULT_RCLK; | |
696 | return (sioprobe(dev, 0, rclk)); | |
697 | } | |
698 | #endif /* NPUC */ | |
699 | ||
700 | static struct isa_pnp_id sio_ids[] = { | |
701 | {0x0005d041, "Standard PC COM port"}, /* PNP0500 */ | |
702 | {0x0105d041, "16550A-compatible COM port"}, /* PNP0501 */ | |
703 | {0x0205d041, "Multiport serial device (non-intelligent 16550)"}, /* PNP0502 */ | |
704 | {0x1005d041, "Generic IRDA-compatible device"}, /* PNP0510 */ | |
705 | {0x1105d041, "Generic IRDA-compatible device"}, /* PNP0511 */ | |
706 | /* Devices that do not have a compatid */ | |
707 | {0x12206804, NULL}, /* ACH2012 - 5634BTS 56K Video Ready Modem */ | |
708 | {0x7602a904, NULL}, /* AEI0276 - 56K v.90 Fax Modem (LKT) */ | |
709 | {0x00007905, NULL}, /* AKY0000 - 56K Plug&Play Modem */ | |
710 | {0x21107905, NULL}, /* AKY1021 - 56K Plug&Play Modem */ | |
711 | {0x01405407, NULL}, /* AZT4001 - AZT3000 PnP SOUND DEVICE, MODEM */ | |
712 | {0x56039008, NULL}, /* BDP0356 - Best Data 56x2 */ | |
713 | {0x56159008, NULL}, /* BDP1556 - B.D. Smart One 56SPS,Voice Modem*/ | |
714 | {0x36339008, NULL}, /* BDP3336 - Best Data Prods. 336F */ | |
715 | {0x0014490a, NULL}, /* BRI1400 - Boca 33.6 PnP */ | |
716 | {0x0015490a, NULL}, /* BRI1500 - Internal Fax Data */ | |
717 | {0x0034490a, NULL}, /* BRI3400 - Internal ACF Modem */ | |
718 | {0x0094490a, NULL}, /* BRI9400 - Boca K56Flex PnP */ | |
719 | {0x00b4490a, NULL}, /* BRIB400 - Boca 56k PnP */ | |
720 | {0x0030320d, NULL}, /* CIR3000 - Cirrus Logic V43 */ | |
721 | {0x0100440e, NULL}, /* CRD0001 - Cardinal MVP288IV ? */ | |
722 | {0x01308c0e, NULL}, /* CTL3001 - Creative Labs Phoneblaster */ | |
723 | {0x36033610, NULL}, /* DAV0336 - DAVICOM 336PNP MODEM */ | |
724 | {0x01009416, NULL}, /* ETT0001 - E-Tech Bullet 33k6 PnP */ | |
725 | {0x0000aa1a, NULL}, /* FUJ0000 - FUJITSU Modem 33600 PNP/I2 */ | |
726 | {0x1200c31e, NULL}, /* GVC0012 - VF1128HV-R9 (win modem?) */ | |
727 | {0x0303c31e, NULL}, /* GVC0303 - MaxTech 33.6 PnP D/F/V */ | |
728 | {0x0505c31e, NULL}, /* GVC0505 - GVC 56k Faxmodem */ | |
729 | {0x0116c31e, NULL}, /* GVC1601 - Rockwell V.34 Plug & Play Modem */ | |
730 | {0x0050c31e, NULL}, /* GVC5000 - some GVC modem */ | |
731 | {0x3800f91e, NULL}, /* GWY0038 - Telepath with v.90 */ | |
732 | {0x9062f91e, NULL}, /* GWY6290 - Telepath with x2 Technology */ | |
733 | {0x8100e425, NULL}, /* IOD0081 - I-O DATA DEVICE,INC. IFML-560 */ | |
734 | {0x21002534, NULL}, /* MAE0021 - Jetstream Int V.90 56k Voice Series 2*/ | |
735 | {0x0000f435, NULL}, /* MOT0000 - Motorola ModemSURFR 33.6 Intern */ | |
736 | {0x5015f435, NULL}, /* MOT1550 - Motorola ModemSURFR 56K Modem */ | |
737 | {0xf015f435, NULL}, /* MOT15F0 - Motorola VoiceSURFR 56K Modem */ | |
738 | {0x6045f435, NULL}, /* MOT4560 - Motorola ? */ | |
739 | {0x61e7a338, NULL}, /* NECE761 - 33.6Modem */ | |
740 | {0x08804f3f, NULL}, /* OZO8008 - Zoom (33.6k Modem) */ | |
741 | {0x0f804f3f, NULL}, /* OZO800f - Zoom 2812 (56k Modem) */ | |
742 | {0x39804f3f, NULL}, /* OZO8039 - Zoom 56k flex */ | |
743 | {0x00914f3f, NULL}, /* OZO9100 - Zoom 2919 (K56 Faxmodem) */ | |
744 | {0x3024a341, NULL}, /* PMC2430 - Pace 56 Voice Internal Modem */ | |
745 | {0x1000eb49, NULL}, /* ROK0010 - Rockwell ? */ | |
746 | {0x1200b23d, NULL}, /* RSS0012 - OMRON ME5614ISA */ | |
747 | {0x5002734a, NULL}, /* RSS0250 - 5614Jx3(G) Internal Modem */ | |
748 | {0x6202734a, NULL}, /* RSS0262 - 5614Jx3[G] V90+K56Flex Modem */ | |
749 | {0x1010104d, NULL}, /* SHP1010 - Rockwell 33600bps Modem */ | |
750 | {0xc100ad4d, NULL}, /* SMM00C1 - Leopard 56k PnP */ | |
751 | {0x9012b04e, NULL}, /* SUP1290 - Supra ? */ | |
752 | {0x1013b04e, NULL}, /* SUP1310 - SupraExpress 336i PnP */ | |
753 | {0x8013b04e, NULL}, /* SUP1380 - SupraExpress 288i PnP Voice */ | |
754 | {0x8113b04e, NULL}, /* SUP1381 - SupraExpress 336i PnP Voice */ | |
755 | {0x5016b04e, NULL}, /* SUP1650 - Supra 336i Sp Intl */ | |
756 | {0x7016b04e, NULL}, /* SUP1670 - Supra 336i V+ Intl */ | |
757 | {0x7420b04e, NULL}, /* SUP2070 - Supra ? */ | |
758 | {0x8020b04e, NULL}, /* SUP2080 - Supra ? */ | |
759 | {0x8420b04e, NULL}, /* SUP2084 - SupraExpress 56i PnP */ | |
760 | {0x7121b04e, NULL}, /* SUP2171 - SupraExpress 56i Sp? */ | |
761 | {0x8024b04e, NULL}, /* SUP2480 - Supra ? */ | |
762 | {0x01007256, NULL}, /* USR0001 - U.S. Robotics Inc., Sportster W */ | |
763 | {0x02007256, NULL}, /* USR0002 - U.S. Robotics Inc. Sportster 33. */ | |
764 | {0x04007256, NULL}, /* USR0004 - USR Sportster 14.4k */ | |
765 | {0x06007256, NULL}, /* USR0006 - USR Sportster 33.6k */ | |
766 | {0x11007256, NULL}, /* USR0011 - USR ? */ | |
767 | {0x01017256, NULL}, /* USR0101 - USR ? */ | |
768 | {0x30207256, NULL}, /* USR2030 - U.S.Robotics Inc. Sportster 560 */ | |
769 | {0x50207256, NULL}, /* USR2050 - U.S.Robotics Inc. Sportster 33. */ | |
770 | {0x70207256, NULL}, /* USR2070 - U.S.Robotics Inc. Sportster 560 */ | |
771 | {0x30307256, NULL}, /* USR3030 - U.S. Robotics 56K FAX INT */ | |
772 | {0x31307256, NULL}, /* USR3031 - U.S. Robotics 56K FAX INT */ | |
773 | {0x50307256, NULL}, /* USR3050 - U.S. Robotics 56K FAX INT */ | |
774 | {0x70307256, NULL}, /* USR3070 - U.S. Robotics 56K Voice INT */ | |
775 | {0x90307256, NULL}, /* USR3090 - USR ? */ | |
776 | {0x70917256, NULL}, /* USR9170 - U.S. Robotics 56K FAX INT */ | |
777 | {0x90917256, NULL}, /* USR9190 - USR 56k Voice INT */ | |
778 | {0x0300695c, NULL}, /* WCI0003 - Fax/Voice/Modem/Speakphone/Asvd */ | |
779 | {0x01a0896a, NULL}, /* ZTIA001 - Zoom Internal V90 Faxmodem */ | |
780 | {0x61f7896a, NULL}, /* ZTIF761 - Zoom ComStar 33.6 */ | |
781 | {0} | |
782 | }; | |
783 | ||
784 | ||
785 | ||
786 | static int | |
787 | sio_isa_probe(dev) | |
788 | device_t dev; | |
789 | { | |
790 | /* Check isapnp ids */ | |
791 | if (ISA_PNP_PROBE(device_get_parent(dev), dev, sio_ids) == ENXIO) | |
792 | return (ENXIO); | |
793 | return (sioprobe(dev, 0, 0UL)); | |
794 | } | |
795 | ||
796 | static int | |
797 | sioprobe(dev, xrid, rclk) | |
798 | device_t dev; | |
799 | int xrid; | |
800 | u_long rclk; | |
801 | { | |
802 | #if 0 | |
803 | static bool_t already_init; | |
804 | device_t xdev; | |
805 | #endif | |
806 | struct com_s *com; | |
807 | u_int divisor; | |
808 | bool_t failures[10]; | |
809 | int fn; | |
810 | device_t idev; | |
811 | Port_t iobase; | |
812 | intrmask_t irqmap[4]; | |
813 | intrmask_t irqs; | |
814 | u_char mcr_image; | |
815 | int result; | |
816 | u_long xirq; | |
817 | u_int flags = device_get_flags(dev); | |
818 | int rid; | |
819 | struct resource *port; | |
820 | ||
821 | rid = xrid; | |
822 | port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, | |
823 | 0, ~0, IO_COMSIZE, RF_ACTIVE); | |
824 | if (!port) | |
825 | return (ENXIO); | |
826 | ||
827 | com = device_get_softc(dev); | |
828 | com->bst = rman_get_bustag(port); | |
829 | com->bsh = rman_get_bushandle(port); | |
830 | if (rclk == 0) | |
831 | rclk = DEFAULT_RCLK; | |
832 | com->rclk = rclk; | |
833 | ||
834 | #if 0 | |
835 | /* | |
836 | * XXX this is broken - when we are first called, there are no | |
837 | * previously configured IO ports. We could hard code | |
838 | * 0x3f8, 0x2f8, 0x3e8, 0x2e8 etc but that's probably worse. | |
839 | * This code has been doing nothing since the conversion since | |
840 | * "count" is zero the first time around. | |
841 | */ | |
842 | if (!already_init) { | |
843 | /* | |
844 | * Turn off MCR_IENABLE for all likely serial ports. An unused | |
845 | * port with its MCR_IENABLE gate open will inhibit interrupts | |
846 | * from any used port that shares the interrupt vector. | |
847 | * XXX the gate enable is elsewhere for some multiports. | |
848 | */ | |
849 | device_t *devs; | |
850 | int count, i, xioport; | |
851 | ||
852 | devclass_get_devices(sio_devclass, &devs, &count); | |
853 | for (i = 0; i < count; i++) { | |
854 | xdev = devs[i]; | |
855 | if (device_is_enabled(xdev) && | |
856 | bus_get_resource(xdev, SYS_RES_IOPORT, 0, &xioport, | |
857 | NULL) == 0) | |
858 | outb(xioport + com_mcr, 0); | |
859 | } | |
860 | free(devs, M_TEMP); | |
861 | already_init = TRUE; | |
862 | } | |
863 | #endif | |
864 | ||
865 | if (COM_LLCONSOLE(flags)) { | |
866 | printf("sio%d: reserved for low-level i/o\n", | |
867 | device_get_unit(dev)); | |
868 | bus_release_resource(dev, SYS_RES_IOPORT, rid, port); | |
869 | return (ENXIO); | |
870 | } | |
871 | ||
872 | /* | |
873 | * If the device is on a multiport card and has an AST/4 | |
874 | * compatible interrupt control register, initialize this | |
875 | * register and prepare to leave MCR_IENABLE clear in the mcr. | |
876 | * Otherwise, prepare to set MCR_IENABLE in the mcr. | |
877 | * Point idev to the device struct giving the correct id_irq. | |
878 | * This is the struct for the master device if there is one. | |
879 | */ | |
880 | idev = dev; | |
881 | mcr_image = MCR_IENABLE; | |
882 | #ifdef COM_MULTIPORT | |
883 | if (COM_ISMULTIPORT(flags)) { | |
884 | Port_t xiobase; | |
885 | u_long io; | |
886 | ||
887 | idev = devclass_get_device(sio_devclass, COM_MPMASTER(flags)); | |
888 | if (idev == NULL) { | |
889 | printf("sio%d: master device %d not configured\n", | |
890 | device_get_unit(dev), COM_MPMASTER(flags)); | |
891 | idev = dev; | |
892 | } | |
893 | if (!COM_NOTAST4(flags)) { | |
894 | if (bus_get_resource(idev, SYS_RES_IOPORT, 0, &io, | |
895 | NULL) == 0) { | |
896 | xiobase = io; | |
897 | if (bus_get_resource(idev, SYS_RES_IRQ, 0, | |
898 | NULL, NULL) == 0) | |
899 | outb(xiobase + com_scr, 0x80); | |
900 | else | |
901 | outb(xiobase + com_scr, 0); | |
902 | } | |
903 | mcr_image = 0; | |
904 | } | |
905 | } | |
906 | #endif /* COM_MULTIPORT */ | |
907 | if (bus_get_resource(idev, SYS_RES_IRQ, 0, NULL, NULL) != 0) | |
908 | mcr_image = 0; | |
909 | ||
910 | bzero(failures, sizeof failures); | |
911 | iobase = rman_get_start(port); | |
912 | ||
913 | /* | |
914 | * We don't want to get actual interrupts, just masked ones. | |
915 | * Interrupts from this line should already be masked in the ICU, | |
916 | * but mask them in the processor as well in case there are some | |
917 | * (misconfigured) shared interrupts. | |
918 | */ | |
919 | disable_intr(); | |
920 | /* EXTRA DELAY? */ | |
921 | ||
922 | /* | |
923 | * For the TI16754 chips, set prescaler to 1 (4 is often the | |
924 | * default after-reset value) as otherwise it's impossible to | |
925 | * get highest baudrates. | |
926 | */ | |
927 | if (COM_TI16754(flags)) { | |
928 | u_char cfcr, efr; | |
929 | ||
930 | cfcr = sio_getreg(com, com_cfcr); | |
931 | sio_setreg(com, com_cfcr, CFCR_EFR_ENABLE); | |
932 | efr = sio_getreg(com, com_efr); | |
933 | /* Unlock extended features to turn off prescaler. */ | |
934 | sio_setreg(com, com_efr, efr | EFR_EFE); | |
935 | /* Disable EFR. */ | |
936 | sio_setreg(com, com_cfcr, (cfcr != CFCR_EFR_ENABLE) ? cfcr : 0); | |
937 | /* Turn off prescaler. */ | |
938 | sio_setreg(com, com_mcr, | |
939 | sio_getreg(com, com_mcr) & ~MCR_PRESCALE); | |
940 | sio_setreg(com, com_cfcr, CFCR_EFR_ENABLE); | |
941 | sio_setreg(com, com_efr, efr); | |
942 | sio_setreg(com, com_cfcr, cfcr); | |
943 | } | |
944 | ||
945 | /* | |
946 | * Initialize the speed and the word size and wait long enough to | |
947 | * drain the maximum of 16 bytes of junk in device output queues. | |
948 | * The speed is undefined after a master reset and must be set | |
949 | * before relying on anything related to output. There may be | |
950 | * junk after a (very fast) soft reboot and (apparently) after | |
951 | * master reset. | |
952 | * XXX what about the UART bug avoided by waiting in comparam()? | |
953 | * We don't want to to wait long enough to drain at 2 bps. | |
954 | */ | |
955 | if (iobase == siocniobase) | |
956 | DELAY((16 + 1) * 1000000 / (comdefaultrate / 10)); | |
957 | else { | |
958 | sio_setreg(com, com_cfcr, CFCR_DLAB | CFCR_8BITS); | |
959 | divisor = siodivisor(rclk, SIO_TEST_SPEED); | |
960 | sio_setreg(com, com_dlbl, divisor & 0xff); | |
961 | sio_setreg(com, com_dlbh, divisor >> 8); | |
962 | sio_setreg(com, com_cfcr, CFCR_8BITS); | |
963 | DELAY((16 + 1) * 1000000 / (SIO_TEST_SPEED / 10)); | |
964 | } | |
965 | ||
966 | /* | |
967 | * Enable the interrupt gate and disable device interupts. This | |
968 | * should leave the device driving the interrupt line low and | |
969 | * guarantee an edge trigger if an interrupt can be generated. | |
970 | */ | |
971 | /* EXTRA DELAY? */ | |
972 | sio_setreg(com, com_mcr, mcr_image); | |
973 | sio_setreg(com, com_ier, 0); | |
974 | DELAY(1000); /* XXX */ | |
975 | irqmap[0] = isa_irq_pending(); | |
976 | ||
977 | /* | |
978 | * Attempt to set loopback mode so that we can send a null byte | |
979 | * without annoying any external device. | |
980 | */ | |
981 | /* EXTRA DELAY? */ | |
982 | sio_setreg(com, com_mcr, mcr_image | MCR_LOOPBACK); | |
983 | ||
984 | /* | |
985 | * Attempt to generate an output interrupt. On 8250's, setting | |
986 | * IER_ETXRDY generates an interrupt independent of the current | |
987 | * setting and independent of whether the THR is empty. On 16450's, | |
988 | * setting IER_ETXRDY generates an interrupt independent of the | |
989 | * current setting. On 16550A's, setting IER_ETXRDY only | |
990 | * generates an interrupt when IER_ETXRDY is not already set. | |
991 | */ | |
992 | sio_setreg(com, com_ier, IER_ETXRDY); | |
993 | ||
994 | /* | |
995 | * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate | |
996 | * an interrupt. They'd better generate one for actually doing | |
997 | * output. Loopback may be broken on the same incompatibles but | |
998 | * it's unlikely to do more than allow the null byte out. | |
999 | */ | |
1000 | sio_setreg(com, com_data, 0); | |
1001 | DELAY((1 + 2) * 1000000 / (SIO_TEST_SPEED / 10)); | |
1002 | ||
1003 | /* | |
1004 | * Turn off loopback mode so that the interrupt gate works again | |
1005 | * (MCR_IENABLE was hidden). This should leave the device driving | |
1006 | * an interrupt line high. It doesn't matter if the interrupt | |
1007 | * line oscillates while we are not looking at it, since interrupts | |
1008 | * are disabled. | |
1009 | */ | |
1010 | /* EXTRA DELAY? */ | |
1011 | sio_setreg(com, com_mcr, mcr_image); | |
1012 | ||
1013 | /* | |
1014 | * Some pcmcia cards have the "TXRDY bug", so we check everyone | |
1015 | * for IIR_TXRDY implementation ( Palido 321s, DC-1S... ) | |
1016 | */ | |
1017 | if (COM_NOPROBE(flags)) { | |
1018 | /* Reading IIR register twice */ | |
1019 | for (fn = 0; fn < 2; fn ++) { | |
1020 | DELAY(10000); | |
1021 | failures[6] = sio_getreg(com, com_iir); | |
1022 | } | |
1023 | /* Check IIR_TXRDY clear ? */ | |
1024 | result = 0; | |
1025 | if (failures[6] & IIR_TXRDY) { | |
1026 | /* Nop, Double check with clearing IER */ | |
1027 | sio_setreg(com, com_ier, 0); | |
1028 | if (sio_getreg(com, com_iir) & IIR_NOPEND) { | |
1029 | /* Ok. we're familia this gang */ | |
1030 | SET_FLAG(dev, COM_C_IIR_TXRDYBUG); | |
1031 | } else { | |
1032 | /* Unknown, Just omit this chip.. XXX */ | |
1033 | result = ENXIO; | |
1034 | sio_setreg(com, com_mcr, 0); | |
1035 | } | |
1036 | } else { | |
1037 | /* OK. this is well-known guys */ | |
1038 | CLR_FLAG(dev, COM_C_IIR_TXRDYBUG); | |
1039 | } | |
1040 | sio_setreg(com, com_ier, 0); | |
1041 | sio_setreg(com, com_cfcr, CFCR_8BITS); | |
1042 | enable_intr(); | |
1043 | bus_release_resource(dev, SYS_RES_IOPORT, rid, port); | |
1044 | return (iobase == siocniobase ? 0 : result); | |
1045 | } | |
1046 | ||
1047 | /* | |
1048 | * Check that | |
1049 | * o the CFCR, IER and MCR in UART hold the values written to them | |
1050 | * (the values happen to be all distinct - this is good for | |
1051 | * avoiding false positive tests from bus echoes). | |
1052 | * o an output interrupt is generated and its vector is correct. | |
1053 | * o the interrupt goes away when the IIR in the UART is read. | |
1054 | */ | |
1055 | /* EXTRA DELAY? */ | |
1056 | failures[0] = sio_getreg(com, com_cfcr) - CFCR_8BITS; | |
1057 | failures[1] = sio_getreg(com, com_ier) - IER_ETXRDY; | |
1058 | failures[2] = sio_getreg(com, com_mcr) - mcr_image; | |
1059 | DELAY(10000); /* Some internal modems need this time */ | |
1060 | irqmap[1] = isa_irq_pending(); | |
1061 | failures[4] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_TXRDY; | |
1062 | DELAY(1000); /* XXX */ | |
1063 | irqmap[2] = isa_irq_pending(); | |
1064 | failures[6] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_NOPEND; | |
1065 | ||
1066 | /* | |
1067 | * Turn off all device interrupts and check that they go off properly. | |
1068 | * Leave MCR_IENABLE alone. For ports without a master port, it gates | |
1069 | * the OUT2 output of the UART to | |
1070 | * the ICU input. Closing the gate would give a floating ICU input | |
1071 | * (unless there is another device driving it) and spurious interrupts. | |
1072 | * (On the system that this was first tested on, the input floats high | |
1073 | * and gives a (masked) interrupt as soon as the gate is closed.) | |
1074 | */ | |
1075 | sio_setreg(com, com_ier, 0); | |
1076 | sio_setreg(com, com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ | |
1077 | failures[7] = sio_getreg(com, com_ier); | |
1078 | DELAY(1000); /* XXX */ | |
1079 | irqmap[3] = isa_irq_pending(); | |
1080 | failures[9] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_NOPEND; | |
1081 | ||
1082 | enable_intr(); | |
1083 | ||
1084 | irqs = irqmap[1] & ~irqmap[0]; | |
1085 | if (bus_get_resource(idev, SYS_RES_IRQ, 0, &xirq, NULL) == 0 && | |
1086 | ((1 << xirq) & irqs) == 0) | |
1087 | printf( | |
1088 | "sio%d: configured irq %ld not in bitmap of probed irqs %#x\n", | |
1089 | device_get_unit(dev), xirq, irqs); | |
1090 | if (bootverbose) | |
1091 | printf("sio%d: irq maps: %#x %#x %#x %#x\n", | |
1092 | device_get_unit(dev), | |
1093 | irqmap[0], irqmap[1], irqmap[2], irqmap[3]); | |
1094 | ||
1095 | result = 0; | |
1096 | for (fn = 0; fn < sizeof failures; ++fn) | |
1097 | if (failures[fn]) { | |
1098 | sio_setreg(com, com_mcr, 0); | |
1099 | result = ENXIO; | |
1100 | if (bootverbose) { | |
1101 | printf("sio%d: probe failed test(s):", | |
1102 | device_get_unit(dev)); | |
1103 | for (fn = 0; fn < sizeof failures; ++fn) | |
1104 | if (failures[fn]) | |
1105 | printf(" %d", fn); | |
1106 | printf("\n"); | |
1107 | } | |
1108 | break; | |
1109 | } | |
1110 | bus_release_resource(dev, SYS_RES_IOPORT, rid, port); | |
1111 | return (iobase == siocniobase ? 0 : result); | |
1112 | } | |
1113 | ||
1114 | #ifdef COM_ESP | |
1115 | static int | |
1116 | espattach(com, esp_port) | |
1117 | struct com_s *com; | |
1118 | Port_t esp_port; | |
1119 | { | |
1120 | u_char dips; | |
1121 | u_char val; | |
1122 | ||
1123 | /* | |
1124 | * Check the ESP-specific I/O port to see if we're an ESP | |
1125 | * card. If not, return failure immediately. | |
1126 | */ | |
1127 | if ((inb(esp_port) & 0xf3) == 0) { | |
1128 | printf(" port 0x%x is not an ESP board?\n", esp_port); | |
1129 | return (0); | |
1130 | } | |
1131 | ||
1132 | /* | |
1133 | * We've got something that claims to be a Hayes ESP card. | |
1134 | * Let's hope so. | |
1135 | */ | |
1136 | ||
1137 | /* Get the dip-switch configuration */ | |
1138 | outb(esp_port + ESP_CMD1, ESP_GETDIPS); | |
1139 | dips = inb(esp_port + ESP_STATUS1); | |
1140 | ||
1141 | /* | |
1142 | * Bits 0,1 of dips say which COM port we are. | |
1143 | */ | |
1144 | if (rman_get_start(com->ioportres) == likely_com_ports[dips & 0x03]) | |
1145 | printf(" : ESP"); | |
1146 | else { | |
1147 | printf(" esp_port has com %d\n", dips & 0x03); | |
1148 | return (0); | |
1149 | } | |
1150 | ||
1151 | /* | |
1152 | * Check for ESP version 2.0 or later: bits 4,5,6 = 010. | |
1153 | */ | |
1154 | outb(esp_port + ESP_CMD1, ESP_GETTEST); | |
1155 | val = inb(esp_port + ESP_STATUS1); /* clear reg 1 */ | |
1156 | val = inb(esp_port + ESP_STATUS2); | |
1157 | if ((val & 0x70) < 0x20) { | |
1158 | printf("-old (%o)", val & 0x70); | |
1159 | return (0); | |
1160 | } | |
1161 | ||
1162 | /* | |
1163 | * Check for ability to emulate 16550: bit 7 == 1 | |
1164 | */ | |
1165 | if ((dips & 0x80) == 0) { | |
1166 | printf(" slave"); | |
1167 | return (0); | |
1168 | } | |
1169 | ||
1170 | /* | |
1171 | * Okay, we seem to be a Hayes ESP card. Whee. | |
1172 | */ | |
1173 | com->esp = TRUE; | |
1174 | com->esp_port = esp_port; | |
1175 | return (1); | |
1176 | } | |
1177 | #endif /* COM_ESP */ | |
1178 | ||
1179 | static int | |
1180 | sio_isa_attach(dev) | |
1181 | device_t dev; | |
1182 | { | |
1183 | return (sioattach(dev, 0, 0UL)); | |
1184 | } | |
1185 | ||
1186 | static int | |
1187 | sioattach(dev, xrid, rclk) | |
1188 | device_t dev; | |
1189 | int xrid; | |
1190 | u_long rclk; | |
1191 | { | |
1192 | struct com_s *com; | |
1193 | #ifdef COM_ESP | |
1194 | Port_t *espp; | |
1195 | #endif | |
1196 | Port_t iobase; | |
1197 | int minorbase; | |
1198 | int unit; | |
1199 | u_int flags; | |
1200 | int rid; | |
1201 | struct resource *port; | |
1202 | int ret; | |
1203 | ||
1204 | rid = xrid; | |
1205 | port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, | |
1206 | 0, ~0, IO_COMSIZE, RF_ACTIVE); | |
1207 | if (!port) | |
1208 | return (ENXIO); | |
1209 | ||
1210 | iobase = rman_get_start(port); | |
1211 | unit = device_get_unit(dev); | |
1212 | com = device_get_softc(dev); | |
1213 | flags = device_get_flags(dev); | |
1214 | ||
1215 | if (unit >= sio_numunits) | |
1216 | sio_numunits = unit + 1; | |
1217 | /* | |
1218 | * sioprobe() has initialized the device registers as follows: | |
1219 | * o cfcr = CFCR_8BITS. | |
1220 | * It is most important that CFCR_DLAB is off, so that the | |
1221 | * data port is not hidden when we enable interrupts. | |
1222 | * o ier = 0. | |
1223 | * Interrupts are only enabled when the line is open. | |
1224 | * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible | |
1225 | * interrupt control register or the config specifies no irq. | |
1226 | * Keeping MCR_DTR and MCR_RTS off might stop the external | |
1227 | * device from sending before we are ready. | |
1228 | */ | |
1229 | bzero(com, sizeof *com); | |
1230 | com->unit = unit; | |
1231 | com->ioportres = port; | |
1232 | com->bst = rman_get_bustag(port); | |
1233 | com->bsh = rman_get_bushandle(port); | |
1234 | com->cfcr_image = CFCR_8BITS; | |
1235 | com->dtr_wait = 3 * hz; | |
1236 | com->loses_outints = COM_LOSESOUTINTS(flags) != 0; | |
1237 | com->no_irq = bus_get_resource(dev, SYS_RES_IRQ, 0, NULL, NULL) != 0; | |
1238 | com->tx_fifo_size = 1; | |
1239 | com->obufs[0].l_head = com->obuf1; | |
1240 | com->obufs[1].l_head = com->obuf2; | |
1241 | ||
1242 | com->data_port = iobase + com_data; | |
1243 | com->int_id_port = iobase + com_iir; | |
1244 | com->modem_ctl_port = iobase + com_mcr; | |
1245 | com->mcr_image = inb(com->modem_ctl_port); | |
1246 | com->line_status_port = iobase + com_lsr; | |
1247 | com->modem_status_port = iobase + com_msr; | |
1248 | com->intr_ctl_port = iobase + com_ier; | |
1249 | ||
1250 | if (rclk == 0) | |
1251 | rclk = DEFAULT_RCLK; | |
1252 | com->rclk = rclk; | |
1253 | ||
1254 | /* | |
1255 | * We don't use all the flags from <sys/ttydefaults.h> since they | |
1256 | * are only relevant for logins. It's important to have echo off | |
1257 | * initially so that the line doesn't start blathering before the | |
1258 | * echo flag can be turned off. | |
1259 | */ | |
1260 | com->it_in.c_iflag = 0; | |
1261 | com->it_in.c_oflag = 0; | |
1262 | com->it_in.c_cflag = TTYDEF_CFLAG; | |
1263 | com->it_in.c_lflag = 0; | |
1264 | if (unit == comconsole) { | |
1265 | com->it_in.c_iflag = TTYDEF_IFLAG; | |
1266 | com->it_in.c_oflag = TTYDEF_OFLAG; | |
1267 | com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; | |
1268 | com->it_in.c_lflag = TTYDEF_LFLAG; | |
1269 | com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; | |
1270 | com->lt_out.c_ispeed = com->lt_out.c_ospeed = | |
1271 | com->lt_in.c_ispeed = com->lt_in.c_ospeed = | |
1272 | com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; | |
1273 | } else | |
1274 | com->it_in.c_ispeed = com->it_in.c_ospeed = TTYDEF_SPEED; | |
1275 | if (siosetwater(com, com->it_in.c_ispeed) != 0) { | |
1276 | enable_intr(); | |
1277 | /* | |
1278 | * Leave i/o resources allocated if this is a `cn'-level | |
1279 | * console, so that other devices can't snarf them. | |
1280 | */ | |
1281 | if (iobase != siocniobase) | |
1282 | bus_release_resource(dev, SYS_RES_IOPORT, rid, port); | |
1283 | return (ENOMEM); | |
1284 | } | |
1285 | enable_intr(); | |
1286 | termioschars(&com->it_in); | |
1287 | com->it_out = com->it_in; | |
1288 | ||
1289 | /* attempt to determine UART type */ | |
1290 | printf("sio%d: type", unit); | |
1291 | ||
1292 | ||
1293 | #ifdef COM_MULTIPORT | |
1294 | if (!COM_ISMULTIPORT(flags) && !COM_IIR_TXRDYBUG(flags)) | |
1295 | #else | |
1296 | if (!COM_IIR_TXRDYBUG(flags)) | |
1297 | #endif | |
1298 | { | |
1299 | u_char scr; | |
1300 | u_char scr1; | |
1301 | u_char scr2; | |
1302 | ||
1303 | scr = sio_getreg(com, com_scr); | |
1304 | sio_setreg(com, com_scr, 0xa5); | |
1305 | scr1 = sio_getreg(com, com_scr); | |
1306 | sio_setreg(com, com_scr, 0x5a); | |
1307 | scr2 = sio_getreg(com, com_scr); | |
1308 | sio_setreg(com, com_scr, scr); | |
1309 | if (scr1 != 0xa5 || scr2 != 0x5a) { | |
1310 | printf(" 8250"); | |
1311 | goto determined_type; | |
1312 | } | |
1313 | } | |
1314 | sio_setreg(com, com_fifo, FIFO_ENABLE | FIFO_RX_HIGH); | |
1315 | DELAY(100); | |
1316 | com->st16650a = 0; | |
1317 | switch (inb(com->int_id_port) & IIR_FIFO_MASK) { | |
1318 | case FIFO_RX_LOW: | |
1319 | printf(" 16450"); | |
1320 | break; | |
1321 | case FIFO_RX_MEDL: | |
1322 | printf(" 16450?"); | |
1323 | break; | |
1324 | case FIFO_RX_MEDH: | |
1325 | printf(" 16550?"); | |
1326 | break; | |
1327 | case FIFO_RX_HIGH: | |
1328 | if (COM_NOFIFO(flags)) { | |
1329 | printf(" 16550A fifo disabled"); | |
1330 | } else { | |
1331 | com->hasfifo = TRUE; | |
1332 | if (COM_ST16650A(flags)) { | |
1333 | com->st16650a = 1; | |
1334 | com->tx_fifo_size = 32; | |
1335 | printf(" ST16650A"); | |
1336 | } else if (COM_TI16754(flags)) { | |
1337 | com->tx_fifo_size = 64; | |
1338 | printf(" TI16754"); | |
1339 | } else { | |
1340 | com->tx_fifo_size = COM_FIFOSIZE(flags); | |
1341 | printf(" 16550A"); | |
1342 | } | |
1343 | } | |
1344 | #ifdef COM_ESP | |
1345 | for (espp = likely_esp_ports; *espp != 0; espp++) | |
1346 | if (espattach(com, *espp)) { | |
1347 | com->tx_fifo_size = 1024; | |
1348 | break; | |
1349 | } | |
1350 | #endif | |
1351 | if (!com->st16650a && !COM_TI16754(flags)) { | |
1352 | if (!com->tx_fifo_size) | |
1353 | com->tx_fifo_size = 16; | |
1354 | else | |
1355 | printf(" lookalike with %d bytes FIFO", | |
1356 | com->tx_fifo_size); | |
1357 | } | |
1358 | ||
1359 | break; | |
1360 | } | |
1361 | ||
1362 | #ifdef COM_ESP | |
1363 | if (com->esp) { | |
1364 | /* | |
1365 | * Set 16550 compatibility mode. | |
1366 | * We don't use the ESP_MODE_SCALE bit to increase the | |
1367 | * fifo trigger levels because we can't handle large | |
1368 | * bursts of input. | |
1369 | * XXX flow control should be set in comparam(), not here. | |
1370 | */ | |
1371 | outb(com->esp_port + ESP_CMD1, ESP_SETMODE); | |
1372 | outb(com->esp_port + ESP_CMD2, ESP_MODE_RTS | ESP_MODE_FIFO); | |
1373 | ||
1374 | /* Set RTS/CTS flow control. */ | |
1375 | outb(com->esp_port + ESP_CMD1, ESP_SETFLOWTYPE); | |
1376 | outb(com->esp_port + ESP_CMD2, ESP_FLOW_RTS); | |
1377 | outb(com->esp_port + ESP_CMD2, ESP_FLOW_CTS); | |
1378 | ||
1379 | /* Set flow-control levels. */ | |
1380 | outb(com->esp_port + ESP_CMD1, ESP_SETRXFLOW); | |
1381 | outb(com->esp_port + ESP_CMD2, HIBYTE(768)); | |
1382 | outb(com->esp_port + ESP_CMD2, LOBYTE(768)); | |
1383 | outb(com->esp_port + ESP_CMD2, HIBYTE(512)); | |
1384 | outb(com->esp_port + ESP_CMD2, LOBYTE(512)); | |
1385 | } | |
1386 | #endif /* COM_ESP */ | |
1387 | sio_setreg(com, com_fifo, 0); | |
1388 | determined_type: ; | |
1389 | ||
1390 | #ifdef COM_MULTIPORT | |
1391 | if (COM_ISMULTIPORT(flags)) { | |
1392 | device_t masterdev; | |
1393 | ||
1394 | com->multiport = TRUE; | |
1395 | printf(" (multiport"); | |
1396 | if (unit == COM_MPMASTER(flags)) | |
1397 | printf(" master"); | |
1398 | printf(")"); | |
1399 | masterdev = devclass_get_device(sio_devclass, | |
1400 | COM_MPMASTER(flags)); | |
1401 | com->no_irq = (masterdev == NULL || bus_get_resource(masterdev, | |
1402 | SYS_RES_IRQ, 0, NULL, NULL) != 0); | |
1403 | } | |
1404 | #endif /* COM_MULTIPORT */ | |
1405 | if (unit == comconsole) | |
1406 | printf(", console"); | |
1407 | if (COM_IIR_TXRDYBUG(flags)) | |
1408 | printf(" with a bogus IIR_TXRDY register"); | |
1409 | printf("\n"); | |
1410 | ||
1411 | if (!sio_registered) { | |
1412 | register_swi(SWI_TTY, siopoll); | |
1413 | sio_registered = TRUE; | |
1414 | } | |
1415 | minorbase = UNIT_TO_MINOR(unit); | |
1416 | make_dev(&sio_cdevsw, minorbase, | |
1417 | UID_ROOT, GID_WHEEL, 0600, "ttyd%r", unit); | |
1418 | make_dev(&sio_cdevsw, minorbase | CONTROL_INIT_STATE, | |
1419 | UID_ROOT, GID_WHEEL, 0600, "ttyid%r", unit); | |
1420 | make_dev(&sio_cdevsw, minorbase | CONTROL_LOCK_STATE, | |
1421 | UID_ROOT, GID_WHEEL, 0600, "ttyld%r", unit); | |
1422 | make_dev(&sio_cdevsw, minorbase | CALLOUT_MASK, | |
1423 | UID_UUCP, GID_DIALER, 0660, "cuaa%r", unit); | |
1424 | make_dev(&sio_cdevsw, minorbase | CALLOUT_MASK | CONTROL_INIT_STATE, | |
1425 | UID_UUCP, GID_DIALER, 0660, "cuaia%r", unit); | |
1426 | make_dev(&sio_cdevsw, minorbase | CALLOUT_MASK | CONTROL_LOCK_STATE, | |
1427 | UID_UUCP, GID_DIALER, 0660, "cuala%r", unit); | |
1428 | com->flags = flags; | |
1429 | com->pps.ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; | |
1430 | pps_init(&com->pps); | |
1431 | ||
1432 | rid = 0; | |
1433 | com->irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0ul, ~0ul, 1, | |
1434 | RF_ACTIVE); | |
1435 | if (com->irqres) { | |
1436 | ret = BUS_SETUP_INTR(device_get_parent(dev), dev, com->irqres, | |
1437 | INTR_TYPE_TTY | INTR_TYPE_FAST, | |
1438 | siointr, com, &com->cookie); | |
1439 | if (ret) { | |
1440 | ret = BUS_SETUP_INTR(device_get_parent(dev), dev, | |
1441 | com->irqres, INTR_TYPE_TTY, | |
1442 | siointr, com, &com->cookie); | |
1443 | if (ret == 0) | |
1444 | device_printf(dev, "unable to activate interrupt in fast mode - using normal mode\n"); | |
1445 | } | |
1446 | if (ret) | |
1447 | device_printf(dev, "could not activate interrupt\n"); | |
1448 | #if defined(DDB) && (defined(BREAK_TO_DEBUGGER) || \ | |
1449 | defined(ALT_BREAK_TO_DEBUGGER)) | |
1450 | /* | |
1451 | * Enable interrupts for early break-to-debugger support | |
1452 | * on the console. | |
1453 | */ | |
1454 | if (ret == 0 && unit == comconsole) | |
1455 | outb(siocniobase + com_ier, IER_ERXRDY | IER_ERLS | | |
1456 | IER_EMSC); | |
1457 | #endif | |
1458 | } | |
1459 | ||
1460 | return (0); | |
1461 | } | |
1462 | ||
1463 | static int | |
1464 | sioopen(dev, flag, mode, p) | |
1465 | dev_t dev; | |
1466 | int flag; | |
1467 | int mode; | |
1468 | struct proc *p; | |
1469 | { | |
1470 | struct com_s *com; | |
1471 | int error; | |
1472 | int mynor; | |
1473 | int s; | |
1474 | struct tty *tp; | |
1475 | int unit; | |
1476 | ||
1477 | mynor = minor(dev); | |
1478 | unit = MINOR_TO_UNIT(mynor); | |
1479 | com = com_addr(unit); | |
1480 | if (com == NULL) | |
1481 | return (ENXIO); | |
1482 | if (com->gone) | |
1483 | return (ENXIO); | |
1484 | if (mynor & CONTROL_MASK) | |
1485 | return (0); | |
1486 | tp = dev->si_tty = com->tp = ttymalloc(com->tp); | |
1487 | s = spltty(); | |
1488 | /* | |
1489 | * We jump to this label after all non-interrupted sleeps to pick | |
1490 | * up any changes of the device state. | |
1491 | */ | |
1492 | open_top: | |
1493 | while (com->state & CS_DTR_OFF) { | |
1494 | error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "siodtr", 0); | |
1495 | if (com_addr(unit) == NULL) | |
1496 | return (ENXIO); | |
1497 | if (error != 0 || com->gone) | |
1498 | goto out; | |
1499 | } | |
1500 | if (tp->t_state & TS_ISOPEN) { | |
1501 | /* | |
1502 | * The device is open, so everything has been initialized. | |
1503 | * Handle conflicts. | |
1504 | */ | |
1505 | if (mynor & CALLOUT_MASK) { | |
1506 | if (!com->active_out) { | |
1507 | error = EBUSY; | |
1508 | goto out; | |
1509 | } | |
1510 | } else { | |
1511 | if (com->active_out) { | |
1512 | if (flag & O_NONBLOCK) { | |
1513 | error = EBUSY; | |
1514 | goto out; | |
1515 | } | |
1516 | error = tsleep(&com->active_out, | |
1517 | TTIPRI | PCATCH, "siobi", 0); | |
1518 | if (com_addr(unit) == NULL) | |
1519 | return (ENXIO); | |
1520 | if (error != 0 || com->gone) | |
1521 | goto out; | |
1522 | goto open_top; | |
1523 | } | |
1524 | } | |
1525 | if (tp->t_state & TS_XCLUDE && | |
1526 | suser(p)) { | |
1527 | error = EBUSY; | |
1528 | goto out; | |
1529 | } | |
1530 | } else { | |
1531 | /* | |
1532 | * The device isn't open, so there are no conflicts. | |
1533 | * Initialize it. Initialization is done twice in many | |
1534 | * cases: to preempt sleeping callin opens if we are | |
1535 | * callout, and to complete a callin open after DCD rises. | |
1536 | */ | |
1537 | tp->t_oproc = comstart; | |
1538 | tp->t_param = comparam; | |
1539 | tp->t_stop = comstop; | |
1540 | tp->t_dev = dev; | |
1541 | tp->t_termios = mynor & CALLOUT_MASK | |
1542 | ? com->it_out : com->it_in; | |
1543 | (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); | |
1544 | com->poll = com->no_irq; | |
1545 | com->poll_output = com->loses_outints; | |
1546 | ++com->wopeners; | |
1547 | error = comparam(tp, &tp->t_termios); | |
1548 | --com->wopeners; | |
1549 | if (error != 0) | |
1550 | goto out; | |
1551 | /* | |
1552 | * XXX we should goto open_top if comparam() slept. | |
1553 | */ | |
1554 | if (com->hasfifo) { | |
1555 | /* | |
1556 | * (Re)enable and drain fifos. | |
1557 | * | |
1558 | * Certain SMC chips cause problems if the fifos | |
1559 | * are enabled while input is ready. Turn off the | |
1560 | * fifo if necessary to clear the input. We test | |
1561 | * the input ready bit after enabling the fifos | |
1562 | * since we've already enabled them in comparam() | |
1563 | * and to handle races between enabling and fresh | |
1564 | * input. | |
1565 | */ | |
1566 | while (TRUE) { | |
1567 | sio_setreg(com, com_fifo, | |
1568 | FIFO_RCV_RST | FIFO_XMT_RST | |
1569 | | com->fifo_image); | |
1570 | /* | |
1571 | * XXX the delays are for superstitious | |
1572 | * historical reasons. It must be less than | |
1573 | * the character time at the maximum | |
1574 | * supported speed (87 usec at 115200 bps | |
1575 | * 8N1). Otherwise we might loop endlessly | |
1576 | * if data is streaming in. We used to use | |
1577 | * delays of 100. That usually worked | |
1578 | * because DELAY(100) used to usually delay | |
1579 | * for about 85 usec instead of 100. | |
1580 | */ | |
1581 | DELAY(50); | |
1582 | if (!(inb(com->line_status_port) & LSR_RXRDY)) | |
1583 | break; | |
1584 | sio_setreg(com, com_fifo, 0); | |
1585 | DELAY(50); | |
1586 | (void) inb(com->data_port); | |
1587 | } | |
1588 | } | |
1589 | ||
1590 | disable_intr(); | |
1591 | (void) inb(com->line_status_port); | |
1592 | (void) inb(com->data_port); | |
1593 | com->prev_modem_status = com->last_modem_status | |
1594 | = inb(com->modem_status_port); | |
1595 | if (COM_IIR_TXRDYBUG(com->flags)) { | |
1596 | outb(com->intr_ctl_port, IER_ERXRDY | IER_ERLS | |
1597 | | IER_EMSC); | |
1598 | } else { | |
1599 | outb(com->intr_ctl_port, IER_ERXRDY | IER_ETXRDY | |
1600 | | IER_ERLS | IER_EMSC); | |
1601 | } | |
1602 | enable_intr(); | |
1603 | /* | |
1604 | * Handle initial DCD. Callout devices get a fake initial | |
1605 | * DCD (trapdoor DCD). If we are callout, then any sleeping | |
1606 | * callin opens get woken up and resume sleeping on "siobi" | |
1607 | * instead of "siodcd". | |
1608 | */ | |
1609 | /* | |
1610 | * XXX `mynor & CALLOUT_MASK' should be | |
1611 | * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where | |
1612 | * TRAPDOOR_CARRIER is the default initial state for callout | |
1613 | * devices and SOFT_CARRIER is like CLOCAL except it hides | |
1614 | * the true carrier. | |
1615 | */ | |
1616 | if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) | |
1617 | (*linesw[tp->t_line].l_modem)(tp, 1); | |
1618 | } | |
1619 | /* | |
1620 | * Wait for DCD if necessary. | |
1621 | */ | |
1622 | if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) | |
1623 | && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { | |
1624 | ++com->wopeners; | |
1625 | error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0); | |
1626 | if (com_addr(unit) == NULL) | |
1627 | return (ENXIO); | |
1628 | --com->wopeners; | |
1629 | if (error != 0 || com->gone) | |
1630 | goto out; | |
1631 | goto open_top; | |
1632 | } | |
1633 | error = (*linesw[tp->t_line].l_open)(dev, tp); | |
1634 | disc_optim(tp, &tp->t_termios, com); | |
1635 | if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) | |
1636 | com->active_out = TRUE; | |
1637 | siosettimeout(); | |
1638 | out: | |
1639 | splx(s); | |
1640 | if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) | |
1641 | comhardclose(com); | |
1642 | return (error); | |
1643 | } | |
1644 | ||
1645 | static int | |
1646 | sioclose(dev, flag, mode, p) | |
1647 | dev_t dev; | |
1648 | int flag; | |
1649 | int mode; | |
1650 | struct proc *p; | |
1651 | { | |
1652 | struct com_s *com; | |
1653 | int mynor; | |
1654 | int s; | |
1655 | struct tty *tp; | |
1656 | ||
1657 | mynor = minor(dev); | |
1658 | if (mynor & CONTROL_MASK) | |
1659 | return (0); | |
1660 | com = com_addr(MINOR_TO_UNIT(mynor)); | |
1661 | if (com == NULL) | |
1662 | return (ENODEV); | |
1663 | tp = com->tp; | |
1664 | s = spltty(); | |
1665 | (*linesw[tp->t_line].l_close)(tp, flag); | |
1666 | disc_optim(tp, &tp->t_termios, com); | |
1667 | comstop(tp, FREAD | FWRITE); | |
1668 | comhardclose(com); | |
1669 | ttyclose(tp); | |
1670 | siosettimeout(); | |
1671 | splx(s); | |
1672 | if (com->gone) { | |
1673 | printf("sio%d: gone\n", com->unit); | |
1674 | s = spltty(); | |
1675 | if (com->ibuf != NULL) | |
1676 | free(com->ibuf, M_DEVBUF); | |
1677 | bzero(tp, sizeof *tp); | |
1678 | splx(s); | |
1679 | } | |
1680 | return (0); | |
1681 | } | |
1682 | ||
1683 | static void | |
1684 | comhardclose(com) | |
1685 | struct com_s *com; | |
1686 | { | |
1687 | int s; | |
1688 | struct tty *tp; | |
1689 | int unit; | |
1690 | ||
1691 | unit = com->unit; | |
1692 | s = spltty(); | |
1693 | com->poll = FALSE; | |
1694 | com->poll_output = FALSE; | |
1695 | com->do_timestamp = FALSE; | |
1696 | com->do_dcd_timestamp = FALSE; | |
1697 | com->pps.ppsparam.mode = 0; | |
1698 | sio_setreg(com, com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); | |
1699 | tp = com->tp; | |
1700 | ||
1701 | #if defined(DDB) && (defined(BREAK_TO_DEBUGGER) || \ | |
1702 | defined(ALT_BREAK_TO_DEBUGGER)) | |
1703 | /* | |
1704 | * Leave interrupts enabled and don't clear DTR if this is the | |
1705 | * console. This allows us to detect break-to-debugger events | |
1706 | * while the console device is closed. | |
1707 | */ | |
1708 | if (com->unit != comconsole) | |
1709 | #endif | |
1710 | { | |
1711 | sio_setreg(com, com_ier, 0); | |
1712 | if (tp->t_cflag & HUPCL | |
1713 | /* | |
1714 | * XXX we will miss any carrier drop between here and the | |
1715 | * next open. Perhaps we should watch DCD even when the | |
1716 | * port is closed; it is not sufficient to check it at | |
1717 | * the next open because it might go up and down while | |
1718 | * we're not watching. | |
1719 | */ | |
1720 | || (!com->active_out | |
1721 | && !(com->prev_modem_status & MSR_DCD) | |
1722 | && !(com->it_in.c_cflag & CLOCAL)) | |
1723 | || !(tp->t_state & TS_ISOPEN)) { | |
1724 | (void)commctl(com, TIOCM_DTR, DMBIC); | |
1725 | if (com->dtr_wait != 0 && !(com->state & CS_DTR_OFF)) { | |
1726 | timeout(siodtrwakeup, com, com->dtr_wait); | |
1727 | com->state |= CS_DTR_OFF; | |
1728 | } | |
1729 | } | |
1730 | } | |
1731 | if (com->hasfifo) { | |
1732 | /* | |
1733 | * Disable fifos so that they are off after controlled | |
1734 | * reboots. Some BIOSes fail to detect 16550s when the | |
1735 | * fifos are enabled. | |
1736 | */ | |
1737 | sio_setreg(com, com_fifo, 0); | |
1738 | } | |
1739 | com->active_out = FALSE; | |
1740 | wakeup(&com->active_out); | |
1741 | wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ | |
1742 | splx(s); | |
1743 | } | |
1744 | ||
1745 | static int | |
1746 | sioread(dev, uio, flag) | |
1747 | dev_t dev; | |
1748 | struct uio *uio; | |
1749 | int flag; | |
1750 | { | |
1751 | int mynor; | |
1752 | struct com_s *com; | |
1753 | ||
1754 | mynor = minor(dev); | |
1755 | if (mynor & CONTROL_MASK) | |
1756 | return (ENODEV); | |
1757 | com = com_addr(MINOR_TO_UNIT(mynor)); | |
1758 | if (com == NULL || com->gone) | |
1759 | return (ENODEV); | |
1760 | return ((*linesw[com->tp->t_line].l_read)(com->tp, uio, flag)); | |
1761 | } | |
1762 | ||
1763 | static int | |
1764 | siowrite(dev, uio, flag) | |
1765 | dev_t dev; | |
1766 | struct uio *uio; | |
1767 | int flag; | |
1768 | { | |
1769 | int mynor; | |
1770 | struct com_s *com; | |
1771 | int unit; | |
1772 | ||
1773 | mynor = minor(dev); | |
1774 | if (mynor & CONTROL_MASK) | |
1775 | return (ENODEV); | |
1776 | ||
1777 | unit = MINOR_TO_UNIT(mynor); | |
1778 | com = com_addr(unit); | |
1779 | if (com == NULL || com->gone) | |
1780 | return (ENODEV); | |
1781 | /* | |
1782 | * (XXX) We disallow virtual consoles if the physical console is | |
1783 | * a serial port. This is in case there is a display attached that | |
1784 | * is not the console. In that situation we don't need/want the X | |
1785 | * server taking over the console. | |
1786 | */ | |
1787 | if (constty != NULL && unit == comconsole) | |
1788 | constty = NULL; | |
1789 | return ((*linesw[com->tp->t_line].l_write)(com->tp, uio, flag)); | |
1790 | } | |
1791 | ||
1792 | static void | |
1793 | siobusycheck(chan) | |
1794 | void *chan; | |
1795 | { | |
1796 | struct com_s *com; | |
1797 | int s; | |
1798 | ||
1799 | com = (struct com_s *)chan; | |
1800 | ||
1801 | /* | |
1802 | * Clear TS_BUSY if low-level output is complete. | |
1803 | * spl locking is sufficient because siointr1() does not set CS_BUSY. | |
1804 | * If siointr1() clears CS_BUSY after we look at it, then we'll get | |
1805 | * called again. Reading the line status port outside of siointr1() | |
1806 | * is safe because CS_BUSY is clear so there are no output interrupts | |
1807 | * to lose. | |
1808 | */ | |
1809 | s = spltty(); | |
1810 | if (com->state & CS_BUSY) | |
1811 | com->extra_state &= ~CSE_BUSYCHECK; /* False alarm. */ | |
1812 | else if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) | |
1813 | == (LSR_TSRE | LSR_TXRDY)) { | |
1814 | com->tp->t_state &= ~TS_BUSY; | |
1815 | ttwwakeup(com->tp); | |
1816 | com->extra_state &= ~CSE_BUSYCHECK; | |
1817 | } else | |
1818 | timeout(siobusycheck, com, hz / 100); | |
1819 | splx(s); | |
1820 | } | |
1821 | ||
1822 | static u_int | |
1823 | siodivisor(rclk, speed) | |
1824 | u_long rclk; | |
1825 | speed_t speed; | |
1826 | { | |
1827 | long actual_speed; | |
1828 | u_int divisor; | |
1829 | int error; | |
1830 | ||
1831 | if (speed == 0 || speed > (ULONG_MAX - 1) / 8) | |
1832 | return (0); | |
1833 | divisor = (rclk / (8UL * speed) + 1) / 2; | |
1834 | if (divisor == 0 || divisor >= 65536) | |
1835 | return (0); | |
1836 | actual_speed = rclk / (16UL * divisor); | |
1837 | ||
1838 | /* 10 times error in percent: */ | |
1839 | error = ((actual_speed - (long)speed) * 2000 / (long)speed + 1) / 2; | |
1840 | ||
1841 | /* 3.0% maximum error tolerance: */ | |
1842 | if (error < -30 || error > 30) | |
1843 | return (0); | |
1844 | ||
1845 | return (divisor); | |
1846 | } | |
1847 | ||
1848 | static void | |
1849 | siodtrwakeup(chan) | |
1850 | void *chan; | |
1851 | { | |
1852 | struct com_s *com; | |
1853 | ||
1854 | com = (struct com_s *)chan; | |
1855 | com->state &= ~CS_DTR_OFF; | |
1856 | wakeup(&com->dtr_wait); | |
1857 | } | |
1858 | ||
1859 | static void | |
1860 | sioinput(com) | |
1861 | struct com_s *com; | |
1862 | { | |
1863 | u_char *buf; | |
1864 | int incc; | |
1865 | u_char line_status; | |
1866 | int recv_data; | |
1867 | struct tty *tp; | |
1868 | ||
1869 | buf = com->ibuf; | |
1870 | tp = com->tp; | |
1871 | if (!(tp->t_state & TS_ISOPEN) || !(tp->t_cflag & CREAD)) { | |
1872 | com_events -= (com->iptr - com->ibuf); | |
1873 | com->iptr = com->ibuf; | |
1874 | return; | |
1875 | } | |
1876 | if (tp->t_state & TS_CAN_BYPASS_L_RINT) { | |
1877 | /* | |
1878 | * Avoid the grotesquely inefficient lineswitch routine | |
1879 | * (ttyinput) in "raw" mode. It usually takes about 450 | |
1880 | * instructions (that's without canonical processing or echo!). | |
1881 | * slinput is reasonably fast (usually 40 instructions plus | |
1882 | * call overhead). | |
1883 | */ | |
1884 | do { | |
1885 | enable_intr(); | |
1886 | incc = com->iptr - buf; | |
1887 | if (tp->t_rawq.c_cc + incc > tp->t_ihiwat | |
1888 | && (com->state & CS_RTS_IFLOW | |
1889 | || tp->t_iflag & IXOFF) | |
1890 | && !(tp->t_state & TS_TBLOCK)) | |
1891 | ttyblock(tp); | |
1892 | com->delta_error_counts[CE_TTY_BUF_OVERFLOW] | |
1893 | += b_to_q((char *)buf, incc, &tp->t_rawq); | |
1894 | buf += incc; | |
1895 | tk_nin += incc; | |
1896 | tk_rawcc += incc; | |
1897 | tp->t_rawcc += incc; | |
1898 | ttwakeup(tp); | |
1899 | if (tp->t_state & TS_TTSTOP | |
1900 | && (tp->t_iflag & IXANY | |
1901 | || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { | |
1902 | tp->t_state &= ~TS_TTSTOP; | |
1903 | tp->t_lflag &= ~FLUSHO; | |
1904 | comstart(tp); | |
1905 | } | |
1906 | disable_intr(); | |
1907 | } while (buf < com->iptr); | |
1908 | } else { | |
1909 | do { | |
1910 | enable_intr(); | |
1911 | line_status = buf[com->ierroff]; | |
1912 | recv_data = *buf++; | |
1913 | if (line_status | |
1914 | & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { | |
1915 | if (line_status & LSR_BI) | |
1916 | recv_data |= TTY_BI; | |
1917 | if (line_status & LSR_FE) | |
1918 | recv_data |= TTY_FE; | |
1919 | if (line_status & LSR_OE) | |
1920 | recv_data |= TTY_OE; | |
1921 | if (line_status & LSR_PE) | |
1922 | recv_data |= TTY_PE; | |
1923 | } | |
1924 | (*linesw[tp->t_line].l_rint)(recv_data, tp); | |
1925 | disable_intr(); | |
1926 | } while (buf < com->iptr); | |
1927 | } | |
1928 | com_events -= (com->iptr - com->ibuf); | |
1929 | com->iptr = com->ibuf; | |
1930 | ||
1931 | /* | |
1932 | * There is now room for another low-level buffer full of input, | |
1933 | * so enable RTS if it is now disabled and there is room in the | |
1934 | * high-level buffer. | |
1935 | */ | |
1936 | if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & MCR_RTS) && | |
1937 | !(tp->t_state & TS_TBLOCK)) | |
1938 | outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); | |
1939 | } | |
1940 | ||
1941 | void | |
1942 | siointr(arg) | |
1943 | void *arg; | |
1944 | { | |
1945 | #ifndef COM_MULTIPORT | |
1946 | COM_LOCK(); | |
1947 | siointr1((struct com_s *) arg); | |
1948 | COM_UNLOCK(); | |
1949 | #else /* COM_MULTIPORT */ | |
1950 | bool_t possibly_more_intrs; | |
1951 | int unit; | |
1952 | struct com_s *com; | |
1953 | ||
1954 | /* | |
1955 | * Loop until there is no activity on any port. This is necessary | |
1956 | * to get an interrupt edge more than to avoid another interrupt. | |
1957 | * If the IRQ signal is just an OR of the IRQ signals from several | |
1958 | * devices, then the edge from one may be lost because another is | |
1959 | * on. | |
1960 | */ | |
1961 | COM_LOCK(); | |
1962 | do { | |
1963 | possibly_more_intrs = FALSE; | |
1964 | for (unit = 0; unit < sio_numunits; ++unit) { | |
1965 | com = com_addr(unit); | |
1966 | /* | |
1967 | * XXX COM_LOCK(); | |
1968 | * would it work here, or be counter-productive? | |
1969 | */ | |
1970 | if (com != NULL | |
1971 | && !com->gone | |
1972 | && (inb(com->int_id_port) & IIR_IMASK) | |
1973 | != IIR_NOPEND) { | |
1974 | siointr1(com); | |
1975 | possibly_more_intrs = TRUE; | |
1976 | } | |
1977 | /* XXX COM_UNLOCK(); */ | |
1978 | } | |
1979 | } while (possibly_more_intrs); | |
1980 | COM_UNLOCK(); | |
1981 | #endif /* COM_MULTIPORT */ | |
1982 | } | |
1983 | ||
1984 | static void | |
1985 | siointr1(com) | |
1986 | struct com_s *com; | |
1987 | { | |
1988 | u_char line_status; | |
1989 | u_char modem_status; | |
1990 | u_char *ioptr; | |
1991 | u_char recv_data; | |
1992 | u_char int_ctl; | |
1993 | u_char int_ctl_new; | |
1994 | struct timecounter *tc; | |
1995 | u_int count; | |
1996 | ||
1997 | int_ctl = inb(com->intr_ctl_port); | |
1998 | int_ctl_new = int_ctl; | |
1999 | ||
2000 | while (!com->gone) { | |
2001 | if (com->pps.ppsparam.mode & PPS_CAPTUREBOTH) { | |
2002 | modem_status = inb(com->modem_status_port); | |
2003 | if ((modem_status ^ com->last_modem_status) & MSR_DCD) { | |
2004 | tc = timecounter; | |
2005 | count = tc->tc_get_timecount(tc); | |
2006 | pps_event(&com->pps, tc, count, | |
2007 | (modem_status & MSR_DCD) ? | |
2008 | PPS_CAPTUREASSERT : PPS_CAPTURECLEAR); | |
2009 | } | |
2010 | } | |
2011 | line_status = inb(com->line_status_port); | |
2012 | ||
2013 | /* input event? (check first to help avoid overruns) */ | |
2014 | while (line_status & LSR_RCV_MASK) { | |
2015 | /* break/unnattached error bits or real input? */ | |
2016 | if (!(line_status & LSR_RXRDY)) | |
2017 | recv_data = 0; | |
2018 | else | |
2019 | recv_data = inb(com->data_port); | |
2020 | #if defined(DDB) && defined(ALT_BREAK_TO_DEBUGGER) | |
2021 | /* | |
2022 | * Solaris implements a new BREAK which is initiated | |
2023 | * by a character sequence CR ~ ^b which is similar | |
2024 | * to a familiar pattern used on Sun servers by the | |
2025 | * Remote Console. | |
2026 | */ | |
2027 | #define KEY_CRTLB 2 /* ^B */ | |
2028 | #define KEY_CR 13 /* CR '\r' */ | |
2029 | #define KEY_TILDE 126 /* ~ */ | |
2030 | ||
2031 | if (com->unit == comconsole) { | |
2032 | static int brk_state1 = 0, brk_state2 = 0; | |
2033 | if (recv_data == KEY_CR) { | |
2034 | brk_state1 = recv_data; | |
2035 | brk_state2 = 0; | |
2036 | } else if (brk_state1 == KEY_CR && (recv_data == KEY_TILDE || recv_data == KEY_CRTLB)) { | |
2037 | if (recv_data == KEY_TILDE) | |
2038 | brk_state2 = recv_data; | |
2039 | else if (brk_state2 == KEY_TILDE && recv_data == KEY_CRTLB) { | |
2040 | breakpoint(); | |
2041 | brk_state1 = brk_state2 = 0; | |
2042 | goto cont; | |
2043 | } else | |
2044 | brk_state2 = 0; | |
2045 | } else | |
2046 | brk_state1 = 0; | |
2047 | } | |
2048 | #endif | |
2049 | if (line_status & (LSR_BI | LSR_FE | LSR_PE)) { | |
2050 | /* | |
2051 | * Don't store BI if IGNBRK or FE/PE if IGNPAR. | |
2052 | * Otherwise, push the work to a higher level | |
2053 | * (to handle PARMRK) if we're bypassing. | |
2054 | * Otherwise, convert BI/FE and PE+INPCK to 0. | |
2055 | * | |
2056 | * This makes bypassing work right in the | |
2057 | * usual "raw" case (IGNBRK set, and IGNPAR | |
2058 | * and INPCK clear). | |
2059 | * | |
2060 | * Note: BI together with FE/PE means just BI. | |
2061 | */ | |
2062 | if (line_status & LSR_BI) { | |
2063 | #if defined(DDB) && defined(BREAK_TO_DEBUGGER) | |
2064 | if (com->unit == comconsole) { | |
2065 | breakpoint(); | |
2066 | goto cont; | |
2067 | } | |
2068 | #endif | |
2069 | if (com->tp == NULL | |
2070 | || com->tp->t_iflag & IGNBRK) | |
2071 | goto cont; | |
2072 | } else { | |
2073 | if (com->tp == NULL | |
2074 | || com->tp->t_iflag & IGNPAR) | |
2075 | goto cont; | |
2076 | } | |
2077 | if (com->tp->t_state & TS_CAN_BYPASS_L_RINT | |
2078 | && (line_status & (LSR_BI | LSR_FE) | |
2079 | || com->tp->t_iflag & INPCK)) | |
2080 | recv_data = 0; | |
2081 | } | |
2082 | ++com->bytes_in; | |
2083 | if (com->hotchar != 0 && recv_data == com->hotchar) | |
2084 | setsofttty(); | |
2085 | ioptr = com->iptr; | |
2086 | if (ioptr >= com->ibufend) | |
2087 | CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); | |
2088 | else { | |
2089 | if (com->do_timestamp) | |
2090 | microtime(&com->timestamp); | |
2091 | ++com_events; | |
2092 | schedsofttty(); | |
2093 | #if 0 /* for testing input latency vs efficiency */ | |
2094 | if (com->iptr - com->ibuf == 8) | |
2095 | setsofttty(); | |
2096 | #endif | |
2097 | ioptr[0] = recv_data; | |
2098 | ioptr[com->ierroff] = line_status; | |
2099 | com->iptr = ++ioptr; | |
2100 | if (ioptr == com->ihighwater | |
2101 | && com->state & CS_RTS_IFLOW) | |
2102 | outb(com->modem_ctl_port, | |
2103 | com->mcr_image &= ~MCR_RTS); | |
2104 | if (line_status & LSR_OE) | |
2105 | CE_RECORD(com, CE_OVERRUN); | |
2106 | } | |
2107 | cont: | |
2108 | /* | |
2109 | * "& 0x7F" is to avoid the gcc-1.40 generating a slow | |
2110 | * jump from the top of the loop to here | |
2111 | */ | |
2112 | line_status = inb(com->line_status_port) & 0x7F; | |
2113 | } | |
2114 | ||
2115 | /* modem status change? (always check before doing output) */ | |
2116 | modem_status = inb(com->modem_status_port); | |
2117 | if (modem_status != com->last_modem_status) { | |
2118 | if (com->do_dcd_timestamp | |
2119 | && !(com->last_modem_status & MSR_DCD) | |
2120 | && modem_status & MSR_DCD) | |
2121 | microtime(&com->dcd_timestamp); | |
2122 | ||
2123 | /* | |
2124 | * Schedule high level to handle DCD changes. Note | |
2125 | * that we don't use the delta bits anywhere. Some | |
2126 | * UARTs mess them up, and it's easy to remember the | |
2127 | * previous bits and calculate the delta. | |
2128 | */ | |
2129 | com->last_modem_status = modem_status; | |
2130 | if (!(com->state & CS_CHECKMSR)) { | |
2131 | com_events += LOTS_OF_EVENTS; | |
2132 | com->state |= CS_CHECKMSR; | |
2133 | setsofttty(); | |
2134 | } | |
2135 | ||
2136 | /* handle CTS change immediately for crisp flow ctl */ | |
2137 | if (com->state & CS_CTS_OFLOW) { | |
2138 | if (modem_status & MSR_CTS) | |
2139 | com->state |= CS_ODEVREADY; | |
2140 | else | |
2141 | com->state &= ~CS_ODEVREADY; | |
2142 | } | |
2143 | } | |
2144 | ||
2145 | /* output queued and everything ready? */ | |
2146 | if (line_status & LSR_TXRDY | |
2147 | && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { | |
2148 | ioptr = com->obufq.l_head; | |
2149 | if (com->tx_fifo_size > 1) { | |
2150 | u_int ocount; | |
2151 | ||
2152 | ocount = com->obufq.l_tail - ioptr; | |
2153 | if (ocount > com->tx_fifo_size) | |
2154 | ocount = com->tx_fifo_size; | |
2155 | com->bytes_out += ocount; | |
2156 | do | |
2157 | outb(com->data_port, *ioptr++); | |
2158 | while (--ocount != 0); | |
2159 | } else { | |
2160 | outb(com->data_port, *ioptr++); | |
2161 | ++com->bytes_out; | |
2162 | } | |
2163 | com->obufq.l_head = ioptr; | |
2164 | if (COM_IIR_TXRDYBUG(com->flags)) { | |
2165 | int_ctl_new = int_ctl | IER_ETXRDY; | |
2166 | } | |
2167 | if (ioptr >= com->obufq.l_tail) { | |
2168 | struct lbq *qp; | |
2169 | ||
2170 | qp = com->obufq.l_next; | |
2171 | qp->l_queued = FALSE; | |
2172 | qp = qp->l_next; | |
2173 | if (qp != NULL) { | |
2174 | com->obufq.l_head = qp->l_head; | |
2175 | com->obufq.l_tail = qp->l_tail; | |
2176 | com->obufq.l_next = qp; | |
2177 | } else { | |
2178 | /* output just completed */ | |
2179 | if (COM_IIR_TXRDYBUG(com->flags)) { | |
2180 | int_ctl_new = int_ctl & ~IER_ETXRDY; | |
2181 | } | |
2182 | com->state &= ~CS_BUSY; | |
2183 | } | |
2184 | if (!(com->state & CS_ODONE)) { | |
2185 | com_events += LOTS_OF_EVENTS; | |
2186 | com->state |= CS_ODONE; | |
2187 | setsofttty(); /* handle at high level ASAP */ | |
2188 | } | |
2189 | } | |
2190 | if (COM_IIR_TXRDYBUG(com->flags) && (int_ctl != int_ctl_new)) { | |
2191 | outb(com->intr_ctl_port, int_ctl_new); | |
2192 | } | |
2193 | } | |
2194 | ||
2195 | /* finished? */ | |
2196 | #ifndef COM_MULTIPORT | |
2197 | if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND) | |
2198 | #endif /* COM_MULTIPORT */ | |
2199 | return; | |
2200 | } | |
2201 | } | |
2202 | ||
2203 | static int | |
2204 | sioioctl(dev, cmd, data, flag, p) | |
2205 | dev_t dev; | |
2206 | u_long cmd; | |
2207 | caddr_t data; | |
2208 | int flag; | |
2209 | struct proc *p; | |
2210 | { | |
2211 | struct com_s *com; | |
2212 | int error; | |
2213 | int mynor; | |
2214 | int s; | |
2215 | struct tty *tp; | |
2216 | #if defined(COMPAT_43) || defined(COMPAT_SUNOS) | |
2217 | u_long oldcmd; | |
2218 | struct termios term; | |
2219 | #endif | |
2220 | ||
2221 | mynor = minor(dev); | |
2222 | com = com_addr(MINOR_TO_UNIT(mynor)); | |
2223 | if (com == NULL || com->gone) | |
2224 | return (ENODEV); | |
2225 | if (mynor & CONTROL_MASK) { | |
2226 | struct termios *ct; | |
2227 | ||
2228 | switch (mynor & CONTROL_MASK) { | |
2229 | case CONTROL_INIT_STATE: | |
2230 | ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; | |
2231 | break; | |
2232 | case CONTROL_LOCK_STATE: | |
2233 | ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; | |
2234 | break; | |
2235 | default: | |
2236 | return (ENODEV); /* /dev/nodev */ | |
2237 | } | |
2238 | switch (cmd) { | |
2239 | case TIOCSETA: | |
2240 | error = suser(p); | |
2241 | if (error != 0) | |
2242 | return (error); | |
2243 | *ct = *(struct termios *)data; | |
2244 | return (0); | |
2245 | case TIOCGETA: | |
2246 | *(struct termios *)data = *ct; | |
2247 | return (0); | |
2248 | case TIOCGETD: | |
2249 | *(int *)data = TTYDISC; | |
2250 | return (0); | |
2251 | case TIOCGWINSZ: | |
2252 | bzero(data, sizeof(struct winsize)); | |
2253 | return (0); | |
2254 | default: | |
2255 | return (ENOTTY); | |
2256 | } | |
2257 | } | |
2258 | tp = com->tp; | |
2259 | #if defined(COMPAT_43) || defined(COMPAT_SUNOS) | |
2260 | term = tp->t_termios; | |
2261 | oldcmd = cmd; | |
2262 | error = ttsetcompat(tp, &cmd, data, &term); | |
2263 | if (error != 0) | |
2264 | return (error); | |
2265 | if (cmd != oldcmd) | |
2266 | data = (caddr_t)&term; | |
2267 | #endif | |
2268 | if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { | |
2269 | int cc; | |
2270 | struct termios *dt = (struct termios *)data; | |
2271 | struct termios *lt = mynor & CALLOUT_MASK | |
2272 | ? &com->lt_out : &com->lt_in; | |
2273 | ||
2274 | dt->c_iflag = (tp->t_iflag & lt->c_iflag) | |
2275 | | (dt->c_iflag & ~lt->c_iflag); | |
2276 | dt->c_oflag = (tp->t_oflag & lt->c_oflag) | |
2277 | | (dt->c_oflag & ~lt->c_oflag); | |
2278 | dt->c_cflag = (tp->t_cflag & lt->c_cflag) | |
2279 | | (dt->c_cflag & ~lt->c_cflag); | |
2280 | dt->c_lflag = (tp->t_lflag & lt->c_lflag) | |
2281 | | (dt->c_lflag & ~lt->c_lflag); | |
2282 | for (cc = 0; cc < NCCS; ++cc) | |
2283 | if (lt->c_cc[cc] != 0) | |
2284 | dt->c_cc[cc] = tp->t_cc[cc]; | |
2285 | if (lt->c_ispeed != 0) | |
2286 | dt->c_ispeed = tp->t_ispeed; | |
2287 | if (lt->c_ospeed != 0) | |
2288 | dt->c_ospeed = tp->t_ospeed; | |
2289 | } | |
2290 | error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); | |
2291 | if (error != ENOIOCTL) | |
2292 | return (error); | |
2293 | s = spltty(); | |
2294 | error = ttioctl(tp, cmd, data, flag); | |
2295 | disc_optim(tp, &tp->t_termios, com); | |
2296 | if (error != ENOIOCTL) { | |
2297 | splx(s); | |
2298 | return (error); | |
2299 | } | |
2300 | switch (cmd) { | |
2301 | case TIOCSBRK: | |
2302 | sio_setreg(com, com_cfcr, com->cfcr_image |= CFCR_SBREAK); | |
2303 | break; | |
2304 | case TIOCCBRK: | |
2305 | sio_setreg(com, com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); | |
2306 | break; | |
2307 | case TIOCSDTR: | |
2308 | (void)commctl(com, TIOCM_DTR, DMBIS); | |
2309 | break; | |
2310 | case TIOCCDTR: | |
2311 | (void)commctl(com, TIOCM_DTR, DMBIC); | |
2312 | break; | |
2313 | /* | |
2314 | * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set. The | |
2315 | * changes get undone on the next call to comparam(). | |
2316 | */ | |
2317 | case TIOCMSET: | |
2318 | (void)commctl(com, *(int *)data, DMSET); | |
2319 | break; | |
2320 | case TIOCMBIS: | |
2321 | (void)commctl(com, *(int *)data, DMBIS); | |
2322 | break; | |
2323 | case TIOCMBIC: | |
2324 | (void)commctl(com, *(int *)data, DMBIC); | |
2325 | break; | |
2326 | case TIOCMGET: | |
2327 | *(int *)data = commctl(com, 0, DMGET); | |
2328 | break; | |
2329 | case TIOCMSDTRWAIT: | |
2330 | /* must be root since the wait applies to following logins */ | |
2331 | error = suser(p); | |
2332 | if (error != 0) { | |
2333 | splx(s); | |
2334 | return (error); | |
2335 | } | |
2336 | com->dtr_wait = *(int *)data * hz / 100; | |
2337 | break; | |
2338 | case TIOCMGDTRWAIT: | |
2339 | *(int *)data = com->dtr_wait * 100 / hz; | |
2340 | break; | |
2341 | case TIOCTIMESTAMP: | |
2342 | com->do_timestamp = TRUE; | |
2343 | *(struct timeval *)data = com->timestamp; | |
2344 | break; | |
2345 | case TIOCDCDTIMESTAMP: | |
2346 | com->do_dcd_timestamp = TRUE; | |
2347 | *(struct timeval *)data = com->dcd_timestamp; | |
2348 | break; | |
2349 | default: | |
2350 | splx(s); | |
2351 | error = pps_ioctl(cmd, data, &com->pps); | |
2352 | if (error == ENODEV) | |
2353 | error = ENOTTY; | |
2354 | return (error); | |
2355 | } | |
2356 | splx(s); | |
2357 | return (0); | |
2358 | } | |
2359 | ||
2360 | static void | |
2361 | siopoll() | |
2362 | { | |
2363 | int unit; | |
2364 | ||
2365 | if (com_events == 0) | |
2366 | return; | |
2367 | repeat: | |
2368 | for (unit = 0; unit < sio_numunits; ++unit) { | |
2369 | struct com_s *com; | |
2370 | int incc; | |
2371 | struct tty *tp; | |
2372 | ||
2373 | com = com_addr(unit); | |
2374 | if (com == NULL) | |
2375 | continue; | |
2376 | tp = com->tp; | |
2377 | if (tp == NULL || com->gone) { | |
2378 | /* | |
2379 | * Discard any events related to never-opened or | |
2380 | * going-away devices. | |
2381 | */ | |
2382 | disable_intr(); | |
2383 | incc = com->iptr - com->ibuf; | |
2384 | com->iptr = com->ibuf; | |
2385 | if (com->state & CS_CHECKMSR) { | |
2386 | incc += LOTS_OF_EVENTS; | |
2387 | com->state &= ~CS_CHECKMSR; | |
2388 | } | |
2389 | com_events -= incc; | |
2390 | enable_intr(); | |
2391 | continue; | |
2392 | } | |
2393 | if (com->iptr != com->ibuf) { | |
2394 | disable_intr(); | |
2395 | sioinput(com); | |
2396 | enable_intr(); | |
2397 | } | |
2398 | if (com->state & CS_CHECKMSR) { | |
2399 | u_char delta_modem_status; | |
2400 | ||
2401 | disable_intr(); | |
2402 | delta_modem_status = com->last_modem_status | |
2403 | ^ com->prev_modem_status; | |
2404 | com->prev_modem_status = com->last_modem_status; | |
2405 | com_events -= LOTS_OF_EVENTS; | |
2406 | com->state &= ~CS_CHECKMSR; | |
2407 | enable_intr(); | |
2408 | if (delta_modem_status & MSR_DCD) | |
2409 | (*linesw[tp->t_line].l_modem) | |
2410 | (tp, com->prev_modem_status & MSR_DCD); | |
2411 | } | |
2412 | if (com->state & CS_ODONE) { | |
2413 | disable_intr(); | |
2414 | com_events -= LOTS_OF_EVENTS; | |
2415 | com->state &= ~CS_ODONE; | |
2416 | enable_intr(); | |
2417 | if (!(com->state & CS_BUSY) | |
2418 | && !(com->extra_state & CSE_BUSYCHECK)) { | |
2419 | timeout(siobusycheck, com, hz / 100); | |
2420 | com->extra_state |= CSE_BUSYCHECK; | |
2421 | } | |
2422 | (*linesw[tp->t_line].l_start)(tp); | |
2423 | } | |
2424 | if (com_events == 0) | |
2425 | break; | |
2426 | } | |
2427 | if (com_events >= LOTS_OF_EVENTS) | |
2428 | goto repeat; | |
2429 | } | |
2430 | ||
2431 | static int | |
2432 | comparam(tp, t) | |
2433 | struct tty *tp; | |
2434 | struct termios *t; | |
2435 | { | |
2436 | u_int cfcr; | |
2437 | int cflag; | |
2438 | struct com_s *com; | |
2439 | u_int divisor; | |
2440 | u_char dlbh; | |
2441 | u_char dlbl; | |
2442 | int s; | |
2443 | int unit; | |
2444 | ||
2445 | unit = DEV_TO_UNIT(tp->t_dev); | |
2446 | com = com_addr(unit); | |
2447 | if (com == NULL) | |
2448 | return (ENODEV); | |
2449 | ||
2450 | /* do historical conversions */ | |
2451 | if (t->c_ispeed == 0) | |
2452 | t->c_ispeed = t->c_ospeed; | |
2453 | ||
2454 | /* check requested parameters */ | |
2455 | if (t->c_ospeed == 0) | |
2456 | divisor = 0; | |
2457 | else { | |
2458 | if (t->c_ispeed != t->c_ospeed) | |
2459 | return (EINVAL); | |
2460 | divisor = siodivisor(com->rclk, t->c_ispeed); | |
2461 | if (divisor == 0) | |
2462 | return (EINVAL); | |
2463 | } | |
2464 | ||
2465 | /* parameters are OK, convert them to the com struct and the device */ | |
2466 | s = spltty(); | |
2467 | if (divisor == 0) | |
2468 | (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ | |
2469 | else | |
2470 | (void)commctl(com, TIOCM_DTR, DMBIS); | |
2471 | cflag = t->c_cflag; | |
2472 | switch (cflag & CSIZE) { | |
2473 | case CS5: | |
2474 | cfcr = CFCR_5BITS; | |
2475 | break; | |
2476 | case CS6: | |
2477 | cfcr = CFCR_6BITS; | |
2478 | break; | |
2479 | case CS7: | |
2480 | cfcr = CFCR_7BITS; | |
2481 | break; | |
2482 | default: | |
2483 | cfcr = CFCR_8BITS; | |
2484 | break; | |
2485 | } | |
2486 | if (cflag & PARENB) { | |
2487 | cfcr |= CFCR_PENAB; | |
2488 | if (!(cflag & PARODD)) | |
2489 | cfcr |= CFCR_PEVEN; | |
2490 | } | |
2491 | if (cflag & CSTOPB) | |
2492 | cfcr |= CFCR_STOPB; | |
2493 | ||
2494 | if (com->hasfifo && divisor != 0) { | |
2495 | /* | |
2496 | * Use a fifo trigger level low enough so that the input | |
2497 | * latency from the fifo is less than about 16 msec and | |
2498 | * the total latency is less than about 30 msec. These | |
2499 | * latencies are reasonable for humans. Serial comms | |
2500 | * protocols shouldn't expect anything better since modem | |
2501 | * latencies are larger. | |
2502 | * | |
2503 | * Interrupts can be held up for long periods of time | |
2504 | * due to inefficiencies in other parts of the kernel, | |
2505 | * certain video cards, etc. Setting the FIFO trigger | |
2506 | * point to MEDH instead of HIGH gives us 694uS of slop | |
2507 | * (8 character times) instead of 173uS (2 character times) | |
2508 | * @ 115200 bps. | |
2509 | */ | |
2510 | com->fifo_image = t->c_ospeed <= 4800 | |
2511 | ? FIFO_ENABLE : FIFO_ENABLE | FIFO_RX_MEDH; | |
2512 | #ifdef COM_ESP | |
2513 | /* | |
2514 | * The Hayes ESP card needs the fifo DMA mode bit set | |
2515 | * in compatibility mode. If not, it will interrupt | |
2516 | * for each character received. | |
2517 | */ | |
2518 | if (com->esp) | |
2519 | com->fifo_image |= FIFO_DMA_MODE; | |
2520 | #endif | |
2521 | sio_setreg(com, com_fifo, com->fifo_image); | |
2522 | } | |
2523 | ||
2524 | /* | |
2525 | * This returns with interrupts disabled so that we can complete | |
2526 | * the speed change atomically. Keeping interrupts disabled is | |
2527 | * especially important while com_data is hidden. | |
2528 | */ | |
2529 | (void) siosetwater(com, t->c_ispeed); | |
2530 | ||
2531 | if (divisor != 0) { | |
2532 | sio_setreg(com, com_cfcr, cfcr | CFCR_DLAB); | |
2533 | /* | |
2534 | * Only set the divisor registers if they would change, | |
2535 | * since on some 16550 incompatibles (UMC8669F), setting | |
2536 | * them while input is arriving them loses sync until | |
2537 | * data stops arriving. | |
2538 | */ | |
2539 | dlbl = divisor & 0xFF; | |
2540 | if (sio_getreg(com, com_dlbl) != dlbl) | |
2541 | sio_setreg(com, com_dlbl, dlbl); | |
2542 | dlbh = divisor >> 8; | |
2543 | if (sio_getreg(com, com_dlbh) != dlbh) | |
2544 | sio_setreg(com, com_dlbh, dlbh); | |
2545 | } | |
2546 | ||
2547 | sio_setreg(com, com_cfcr, com->cfcr_image = cfcr); | |
2548 | ||
2549 | if (!(tp->t_state & TS_TTSTOP)) | |
2550 | com->state |= CS_TTGO; | |
2551 | ||
2552 | if (cflag & CRTS_IFLOW) { | |
2553 | if (com->st16650a) { | |
2554 | sio_setreg(com, com_cfcr, 0xbf); | |
2555 | sio_setreg(com, com_fifo, | |
2556 | sio_getreg(com, com_fifo) | 0x40); | |
2557 | } | |
2558 | com->state |= CS_RTS_IFLOW; | |
2559 | /* | |
2560 | * If CS_RTS_IFLOW just changed from off to on, the change | |
2561 | * needs to be propagated to MCR_RTS. This isn't urgent, | |
2562 | * so do it later by calling comstart() instead of repeating | |
2563 | * a lot of code from comstart() here. | |
2564 | */ | |
2565 | } else if (com->state & CS_RTS_IFLOW) { | |
2566 | com->state &= ~CS_RTS_IFLOW; | |
2567 | /* | |
2568 | * CS_RTS_IFLOW just changed from on to off. Force MCR_RTS | |
2569 | * on here, since comstart() won't do it later. | |
2570 | */ | |
2571 | outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); | |
2572 | if (com->st16650a) { | |
2573 | sio_setreg(com, com_cfcr, 0xbf); | |
2574 | sio_setreg(com, com_fifo, | |
2575 | sio_getreg(com, com_fifo) & ~0x40); | |
2576 | } | |
2577 | } | |
2578 | ||
2579 | ||
2580 | /* | |
2581 | * Set up state to handle output flow control. | |
2582 | * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? | |
2583 | * Now has 10+ msec latency, while CTS flow has 50- usec latency. | |
2584 | */ | |
2585 | com->state |= CS_ODEVREADY; | |
2586 | com->state &= ~CS_CTS_OFLOW; | |
2587 | if (cflag & CCTS_OFLOW) { | |
2588 | com->state |= CS_CTS_OFLOW; | |
2589 | if (!(com->last_modem_status & MSR_CTS)) | |
2590 | com->state &= ~CS_ODEVREADY; | |
2591 | if (com->st16650a) { | |
2592 | sio_setreg(com, com_cfcr, 0xbf); | |
2593 | sio_setreg(com, com_fifo, | |
2594 | sio_getreg(com, com_fifo) | 0x80); | |
2595 | } | |
2596 | } else { | |
2597 | if (com->st16650a) { | |
2598 | sio_setreg(com, com_cfcr, 0xbf); | |
2599 | sio_setreg(com, com_fifo, | |
2600 | sio_getreg(com, com_fifo) & ~0x80); | |
2601 | } | |
2602 | } | |
2603 | ||
2604 | sio_setreg(com, com_cfcr, com->cfcr_image); | |
2605 | ||
2606 | /* XXX shouldn't call functions while intrs are disabled. */ | |
2607 | disc_optim(tp, t, com); | |
2608 | /* | |
2609 | * Recover from fiddling with CS_TTGO. We used to call siointr1() | |
2610 | * unconditionally, but that defeated the careful discarding of | |
2611 | * stale input in sioopen(). | |
2612 | */ | |
2613 | if (com->state >= (CS_BUSY | CS_TTGO)) | |
2614 | siointr1(com); | |
2615 | ||
2616 | enable_intr(); | |
2617 | splx(s); | |
2618 | comstart(tp); | |
2619 | if (com->ibufold != NULL) { | |
2620 | free(com->ibufold, M_DEVBUF); | |
2621 | com->ibufold = NULL; | |
2622 | } | |
2623 | return (0); | |
2624 | } | |
2625 | ||
2626 | static int | |
2627 | siosetwater(com, speed) | |
2628 | struct com_s *com; | |
2629 | speed_t speed; | |
2630 | { | |
2631 | int cp4ticks; | |
2632 | u_char *ibuf; | |
2633 | int ibufsize; | |
2634 | struct tty *tp; | |
2635 | ||
2636 | /* | |
2637 | * Make the buffer size large enough to handle a softtty interrupt | |
2638 | * latency of about 2 ticks without loss of throughput or data | |
2639 | * (about 3 ticks if input flow control is not used or not honoured, | |
2640 | * but a bit less for CS5-CS7 modes). | |
2641 | */ | |
2642 | cp4ticks = speed / 10 / hz * 4; | |
2643 | for (ibufsize = 128; ibufsize < cp4ticks;) | |
2644 | ibufsize <<= 1; | |
2645 | if (ibufsize == com->ibufsize) { | |
2646 | disable_intr(); | |
2647 | return (0); | |
2648 | } | |
2649 | ||
2650 | /* | |
2651 | * Allocate input buffer. The extra factor of 2 in the size is | |
2652 | * to allow for an error byte for each input byte. | |
2653 | */ | |
2654 | ibuf = malloc(2 * ibufsize, M_DEVBUF, M_NOWAIT); | |
2655 | if (ibuf == NULL) { | |
2656 | disable_intr(); | |
2657 | return (ENOMEM); | |
2658 | } | |
2659 | ||
2660 | /* Initialize non-critical variables. */ | |
2661 | com->ibufold = com->ibuf; | |
2662 | com->ibufsize = ibufsize; | |
2663 | tp = com->tp; | |
2664 | if (tp != NULL) { | |
2665 | tp->t_ififosize = 2 * ibufsize; | |
2666 | tp->t_ispeedwat = (speed_t)-1; | |
2667 | tp->t_ospeedwat = (speed_t)-1; | |
2668 | } | |
2669 | ||
2670 | /* | |
2671 | * Read current input buffer, if any. Continue with interrupts | |
2672 | * disabled. | |
2673 | */ | |
2674 | disable_intr(); | |
2675 | if (com->iptr != com->ibuf) | |
2676 | sioinput(com); | |
2677 | ||
2678 | /*- | |
2679 | * Initialize critical variables, including input buffer watermarks. | |
2680 | * The external device is asked to stop sending when the buffer | |
2681 | * exactly reaches high water, or when the high level requests it. | |
2682 | * The high level is notified immediately (rather than at a later | |
2683 | * clock tick) when this watermark is reached. | |
2684 | * The buffer size is chosen so the watermark should almost never | |
2685 | * be reached. | |
2686 | * The low watermark is invisibly 0 since the buffer is always | |
2687 | * emptied all at once. | |
2688 | */ | |
2689 | com->iptr = com->ibuf = ibuf; | |
2690 | com->ibufend = ibuf + ibufsize; | |
2691 | com->ierroff = ibufsize; | |
2692 | com->ihighwater = ibuf + 3 * ibufsize / 4; | |
2693 | return (0); | |
2694 | } | |
2695 | ||
2696 | static void | |
2697 | comstart(tp) | |
2698 | struct tty *tp; | |
2699 | { | |
2700 | struct com_s *com; | |
2701 | int s; | |
2702 | int unit; | |
2703 | ||
2704 | unit = DEV_TO_UNIT(tp->t_dev); | |
2705 | com = com_addr(unit); | |
2706 | if (com == NULL) | |
2707 | return; | |
2708 | s = spltty(); | |
2709 | disable_intr(); | |
2710 | if (tp->t_state & TS_TTSTOP) | |
2711 | com->state &= ~CS_TTGO; | |
2712 | else | |
2713 | com->state |= CS_TTGO; | |
2714 | if (tp->t_state & TS_TBLOCK) { | |
2715 | if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) | |
2716 | outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); | |
2717 | } else { | |
2718 | if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater | |
2719 | && com->state & CS_RTS_IFLOW) | |
2720 | outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); | |
2721 | } | |
2722 | enable_intr(); | |
2723 | if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { | |
2724 | ttwwakeup(tp); | |
2725 | splx(s); | |
2726 | return; | |
2727 | } | |
2728 | if (tp->t_outq.c_cc != 0) { | |
2729 | struct lbq *qp; | |
2730 | struct lbq *next; | |
2731 | ||
2732 | if (!com->obufs[0].l_queued) { | |
2733 | com->obufs[0].l_tail | |
2734 | = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, | |
2735 | sizeof com->obuf1); | |
2736 | com->obufs[0].l_next = NULL; | |
2737 | com->obufs[0].l_queued = TRUE; | |
2738 | disable_intr(); | |
2739 | if (com->state & CS_BUSY) { | |
2740 | qp = com->obufq.l_next; | |
2741 | while ((next = qp->l_next) != NULL) | |
2742 | qp = next; | |
2743 | qp->l_next = &com->obufs[0]; | |
2744 | } else { | |
2745 | com->obufq.l_head = com->obufs[0].l_head; | |
2746 | com->obufq.l_tail = com->obufs[0].l_tail; | |
2747 | com->obufq.l_next = &com->obufs[0]; | |
2748 | com->state |= CS_BUSY; | |
2749 | } | |
2750 | enable_intr(); | |
2751 | } | |
2752 | if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { | |
2753 | com->obufs[1].l_tail | |
2754 | = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, | |
2755 | sizeof com->obuf2); | |
2756 | com->obufs[1].l_next = NULL; | |
2757 | com->obufs[1].l_queued = TRUE; | |
2758 | disable_intr(); | |
2759 | if (com->state & CS_BUSY) { | |
2760 | qp = com->obufq.l_next; | |
2761 | while ((next = qp->l_next) != NULL) | |
2762 | qp = next; | |
2763 | qp->l_next = &com->obufs[1]; | |
2764 | } else { | |
2765 | com->obufq.l_head = com->obufs[1].l_head; | |
2766 | com->obufq.l_tail = com->obufs[1].l_tail; | |
2767 | com->obufq.l_next = &com->obufs[1]; | |
2768 | com->state |= CS_BUSY; | |
2769 | } | |
2770 | enable_intr(); | |
2771 | } | |
2772 | tp->t_state |= TS_BUSY; | |
2773 | } | |
2774 | disable_intr(); | |
2775 | if (com->state >= (CS_BUSY | CS_TTGO)) | |
2776 | siointr1(com); /* fake interrupt to start output */ | |
2777 | enable_intr(); | |
2778 | ttwwakeup(tp); | |
2779 | splx(s); | |
2780 | } | |
2781 | ||
2782 | static void | |
2783 | comstop(tp, rw) | |
2784 | struct tty *tp; | |
2785 | int rw; | |
2786 | { | |
2787 | struct com_s *com; | |
2788 | ||
2789 | com = com_addr(DEV_TO_UNIT(tp->t_dev)); | |
2790 | if (com == NULL || com->gone) | |
2791 | return; | |
2792 | disable_intr(); | |
2793 | if (rw & FWRITE) { | |
2794 | if (com->hasfifo) | |
2795 | #ifdef COM_ESP | |
2796 | /* XXX avoid h/w bug. */ | |
2797 | if (!com->esp) | |
2798 | #endif | |
2799 | sio_setreg(com, com_fifo, | |
2800 | FIFO_XMT_RST | com->fifo_image); | |
2801 | com->obufs[0].l_queued = FALSE; | |
2802 | com->obufs[1].l_queued = FALSE; | |
2803 | if (com->state & CS_ODONE) | |
2804 | com_events -= LOTS_OF_EVENTS; | |
2805 | com->state &= ~(CS_ODONE | CS_BUSY); | |
2806 | com->tp->t_state &= ~TS_BUSY; | |
2807 | } | |
2808 | if (rw & FREAD) { | |
2809 | if (com->hasfifo) | |
2810 | #ifdef COM_ESP | |
2811 | /* XXX avoid h/w bug. */ | |
2812 | if (!com->esp) | |
2813 | #endif | |
2814 | sio_setreg(com, com_fifo, | |
2815 | FIFO_RCV_RST | com->fifo_image); | |
2816 | com_events -= (com->iptr - com->ibuf); | |
2817 | com->iptr = com->ibuf; | |
2818 | } | |
2819 | enable_intr(); | |
2820 | comstart(tp); | |
2821 | } | |
2822 | ||
2823 | static int | |
2824 | commctl(com, bits, how) | |
2825 | struct com_s *com; | |
2826 | int bits; | |
2827 | int how; | |
2828 | { | |
2829 | int mcr; | |
2830 | int msr; | |
2831 | ||
2832 | if (how == DMGET) { | |
2833 | bits = TIOCM_LE; /* XXX - always enabled while open */ | |
2834 | mcr = com->mcr_image; | |
2835 | if (mcr & MCR_DTR) | |
2836 | bits |= TIOCM_DTR; | |
2837 | if (mcr & MCR_RTS) | |
2838 | bits |= TIOCM_RTS; | |
2839 | msr = com->prev_modem_status; | |
2840 | if (msr & MSR_CTS) | |
2841 | bits |= TIOCM_CTS; | |
2842 | if (msr & MSR_DCD) | |
2843 | bits |= TIOCM_CD; | |
2844 | if (msr & MSR_DSR) | |
2845 | bits |= TIOCM_DSR; | |
2846 | /* | |
2847 | * XXX - MSR_RI is naturally volatile, and we make MSR_TERI | |
2848 | * more volatile by reading the modem status a lot. Perhaps | |
2849 | * we should latch both bits until the status is read here. | |
2850 | */ | |
2851 | if (msr & (MSR_RI | MSR_TERI)) | |
2852 | bits |= TIOCM_RI; | |
2853 | return (bits); | |
2854 | } | |
2855 | mcr = 0; | |
2856 | if (bits & TIOCM_DTR) | |
2857 | mcr |= MCR_DTR; | |
2858 | if (bits & TIOCM_RTS) | |
2859 | mcr |= MCR_RTS; | |
2860 | if (com->gone) | |
2861 | return(0); | |
2862 | disable_intr(); | |
2863 | switch (how) { | |
2864 | case DMSET: | |
2865 | outb(com->modem_ctl_port, | |
2866 | com->mcr_image = mcr | (com->mcr_image & MCR_IENABLE)); | |
2867 | break; | |
2868 | case DMBIS: | |
2869 | outb(com->modem_ctl_port, com->mcr_image |= mcr); | |
2870 | break; | |
2871 | case DMBIC: | |
2872 | outb(com->modem_ctl_port, com->mcr_image &= ~mcr); | |
2873 | break; | |
2874 | } | |
2875 | enable_intr(); | |
2876 | return (0); | |
2877 | } | |
2878 | ||
2879 | static void | |
2880 | siosettimeout() | |
2881 | { | |
2882 | struct com_s *com; | |
2883 | bool_t someopen; | |
2884 | int unit; | |
2885 | ||
2886 | /* | |
2887 | * Set our timeout period to 1 second if no polled devices are open. | |
2888 | * Otherwise set it to max(1/200, 1/hz). | |
2889 | * Enable timeouts iff some device is open. | |
2890 | */ | |
2891 | untimeout(comwakeup, (void *)NULL, sio_timeout_handle); | |
2892 | sio_timeout = hz; | |
2893 | someopen = FALSE; | |
2894 | for (unit = 0; unit < sio_numunits; ++unit) { | |
2895 | com = com_addr(unit); | |
2896 | if (com != NULL && com->tp != NULL | |
2897 | && com->tp->t_state & TS_ISOPEN && !com->gone) { | |
2898 | someopen = TRUE; | |
2899 | if (com->poll || com->poll_output) { | |
2900 | sio_timeout = hz > 200 ? hz / 200 : 1; | |
2901 | break; | |
2902 | } | |
2903 | } | |
2904 | } | |
2905 | if (someopen) { | |
2906 | sio_timeouts_until_log = hz / sio_timeout; | |
2907 | sio_timeout_handle = timeout(comwakeup, (void *)NULL, | |
2908 | sio_timeout); | |
2909 | } else { | |
2910 | /* Flush error messages, if any. */ | |
2911 | sio_timeouts_until_log = 1; | |
2912 | comwakeup((void *)NULL); | |
2913 | untimeout(comwakeup, (void *)NULL, sio_timeout_handle); | |
2914 | } | |
2915 | } | |
2916 | ||
2917 | static void | |
2918 | comwakeup(chan) | |
2919 | void *chan; | |
2920 | { | |
2921 | struct com_s *com; | |
2922 | int unit; | |
2923 | ||
2924 | sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); | |
2925 | ||
2926 | /* | |
2927 | * Recover from lost output interrupts. | |
2928 | * Poll any lines that don't use interrupts. | |
2929 | */ | |
2930 | for (unit = 0; unit < sio_numunits; ++unit) { | |
2931 | com = com_addr(unit); | |
2932 | if (com != NULL && !com->gone | |
2933 | && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { | |
2934 | disable_intr(); | |
2935 | siointr1(com); | |
2936 | enable_intr(); | |
2937 | } | |
2938 | } | |
2939 | ||
2940 | /* | |
2941 | * Check for and log errors, but not too often. | |
2942 | */ | |
2943 | if (--sio_timeouts_until_log > 0) | |
2944 | return; | |
2945 | sio_timeouts_until_log = hz / sio_timeout; | |
2946 | for (unit = 0; unit < sio_numunits; ++unit) { | |
2947 | int errnum; | |
2948 | ||
2949 | com = com_addr(unit); | |
2950 | if (com == NULL) | |
2951 | continue; | |
2952 | if (com->gone) | |
2953 | continue; | |
2954 | for (errnum = 0; errnum < CE_NTYPES; ++errnum) { | |
2955 | u_int delta; | |
2956 | u_long total; | |
2957 | ||
2958 | disable_intr(); | |
2959 | delta = com->delta_error_counts[errnum]; | |
2960 | com->delta_error_counts[errnum] = 0; | |
2961 | enable_intr(); | |
2962 | if (delta == 0) | |
2963 | continue; | |
2964 | total = com->error_counts[errnum] += delta; | |
2965 | log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n", | |
2966 | unit, delta, error_desc[errnum], | |
2967 | delta == 1 ? "" : "s", total); | |
2968 | } | |
2969 | } | |
2970 | } | |
2971 | ||
2972 | static void | |
2973 | disc_optim(tp, t, com) | |
2974 | struct tty *tp; | |
2975 | struct termios *t; | |
2976 | struct com_s *com; | |
2977 | { | |
2978 | if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) | |
2979 | && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) | |
2980 | && (!(t->c_iflag & PARMRK) | |
2981 | || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) | |
2982 | && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) | |
2983 | && linesw[tp->t_line].l_rint == ttyinput) | |
2984 | tp->t_state |= TS_CAN_BYPASS_L_RINT; | |
2985 | else | |
2986 | tp->t_state &= ~TS_CAN_BYPASS_L_RINT; | |
2987 | com->hotchar = linesw[tp->t_line].l_hotchar; | |
2988 | } | |
2989 | ||
2990 | /* | |
2991 | * Following are all routines needed for SIO to act as console | |
2992 | */ | |
2993 | #include <sys/cons.h> | |
2994 | ||
2995 | struct siocnstate { | |
2996 | u_char dlbl; | |
2997 | u_char dlbh; | |
2998 | u_char ier; | |
2999 | u_char cfcr; | |
3000 | u_char mcr; | |
3001 | }; | |
3002 | ||
3003 | static speed_t siocngetspeed __P((Port_t, u_long rclk)); | |
3004 | static void siocnclose __P((struct siocnstate *sp, Port_t iobase)); | |
3005 | static void siocnopen __P((struct siocnstate *sp, Port_t iobase, int speed)); | |
3006 | static void siocntxwait __P((Port_t iobase)); | |
3007 | ||
3008 | static cn_probe_t siocnprobe; | |
3009 | static cn_init_t siocninit; | |
3010 | static cn_checkc_t siocncheckc; | |
3011 | static cn_getc_t siocngetc; | |
3012 | static cn_putc_t siocnputc; | |
3013 | ||
3014 | #ifdef __i386__ | |
3015 | CONS_DRIVER(sio, siocnprobe, siocninit, NULL, siocngetc, siocncheckc, | |
3016 | siocnputc, NULL); | |
3017 | #endif | |
3018 | ||
3019 | /* To get the GDB related variables */ | |
3020 | #if DDB > 0 | |
3021 | #include <ddb/ddb.h> | |
3022 | #endif | |
3023 | ||
3024 | static void | |
3025 | siocntxwait(iobase) | |
3026 | Port_t iobase; | |
3027 | { | |
3028 | int timo; | |
3029 | ||
3030 | /* | |
3031 | * Wait for any pending transmission to finish. Required to avoid | |
3032 | * the UART lockup bug when the speed is changed, and for normal | |
3033 | * transmits. | |
3034 | */ | |
3035 | timo = 100000; | |
3036 | while ((inb(iobase + com_lsr) & (LSR_TSRE | LSR_TXRDY)) | |
3037 | != (LSR_TSRE | LSR_TXRDY) && --timo != 0) | |
3038 | ; | |
3039 | } | |
3040 | ||
3041 | /* | |
3042 | * Read the serial port specified and try to figure out what speed | |
3043 | * it's currently running at. We're assuming the serial port has | |
3044 | * been initialized and is basicly idle. This routine is only intended | |
3045 | * to be run at system startup. | |
3046 | * | |
3047 | * If the value read from the serial port doesn't make sense, return 0. | |
3048 | */ | |
3049 | ||
3050 | static speed_t | |
3051 | siocngetspeed(iobase, rclk) | |
3052 | Port_t iobase; | |
3053 | u_long rclk; | |
3054 | { | |
3055 | u_int divisor; | |
3056 | u_char dlbh; | |
3057 | u_char dlbl; | |
3058 | u_char cfcr; | |
3059 | ||
3060 | cfcr = inb(iobase + com_cfcr); | |
3061 | outb(iobase + com_cfcr, CFCR_DLAB | cfcr); | |
3062 | ||
3063 | dlbl = inb(iobase + com_dlbl); | |
3064 | dlbh = inb(iobase + com_dlbh); | |
3065 | ||
3066 | outb(iobase + com_cfcr, cfcr); | |
3067 | ||
3068 | divisor = dlbh << 8 | dlbl; | |
3069 | ||
3070 | /* XXX there should be more sanity checking. */ | |
3071 | if (divisor == 0) | |
3072 | return (CONSPEED); | |
3073 | return (rclk / (16UL * divisor)); | |
3074 | } | |
3075 | ||
3076 | static void | |
3077 | siocnopen(sp, iobase, speed) | |
3078 | struct siocnstate *sp; | |
3079 | Port_t iobase; | |
3080 | int speed; | |
3081 | { | |
3082 | u_int divisor; | |
3083 | u_char dlbh; | |
3084 | u_char dlbl; | |
3085 | ||
3086 | /* | |
3087 | * Save all the device control registers except the fifo register | |
3088 | * and set our default ones (cs8 -parenb speed=comdefaultrate). | |
3089 | * We can't save the fifo register since it is read-only. | |
3090 | */ | |
3091 | sp->ier = inb(iobase + com_ier); | |
3092 | outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */ | |
3093 | siocntxwait(iobase); | |
3094 | sp->cfcr = inb(iobase + com_cfcr); | |
3095 | outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); | |
3096 | sp->dlbl = inb(iobase + com_dlbl); | |
3097 | sp->dlbh = inb(iobase + com_dlbh); | |
3098 | /* | |
3099 | * Only set the divisor registers if they would change, since on | |
3100 | * some 16550 incompatibles (Startech), setting them clears the | |
3101 | * data input register. This also reduces the effects of the | |
3102 | * UMC8669F bug. | |
3103 | */ | |
3104 | divisor = siodivisor(comdefaultrclk, speed); | |
3105 | dlbl = divisor & 0xFF; | |
3106 | if (sp->dlbl != dlbl) | |
3107 | outb(iobase + com_dlbl, dlbl); | |
3108 | dlbh = divisor >> 8; | |
3109 | if (sp->dlbh != dlbh) | |
3110 | outb(iobase + com_dlbh, dlbh); | |
3111 | outb(iobase + com_cfcr, CFCR_8BITS); | |
3112 | sp->mcr = inb(iobase + com_mcr); | |
3113 | /* | |
3114 | * We don't want interrupts, but must be careful not to "disable" | |
3115 | * them by clearing the MCR_IENABLE bit, since that might cause | |
3116 | * an interrupt by floating the IRQ line. | |
3117 | */ | |
3118 | outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS); | |
3119 | } | |
3120 | ||
3121 | static void | |
3122 | siocnclose(sp, iobase) | |
3123 | struct siocnstate *sp; | |
3124 | Port_t iobase; | |
3125 | { | |
3126 | /* | |
3127 | * Restore the device control registers. | |
3128 | */ | |
3129 | siocntxwait(iobase); | |
3130 | outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); | |
3131 | if (sp->dlbl != inb(iobase + com_dlbl)) | |
3132 | outb(iobase + com_dlbl, sp->dlbl); | |
3133 | if (sp->dlbh != inb(iobase + com_dlbh)) | |
3134 | outb(iobase + com_dlbh, sp->dlbh); | |
3135 | outb(iobase + com_cfcr, sp->cfcr); | |
3136 | /* | |
3137 | * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them. | |
3138 | */ | |
3139 | outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); | |
3140 | outb(iobase + com_ier, sp->ier); | |
3141 | } | |
3142 | ||
3143 | static void | |
3144 | siocnprobe(cp) | |
3145 | struct consdev *cp; | |
3146 | { | |
3147 | speed_t boot_speed; | |
3148 | u_char cfcr; | |
3149 | u_int divisor; | |
3150 | int s, unit; | |
3151 | struct siocnstate sp; | |
3152 | ||
3153 | /* | |
3154 | * Find our first enabled console, if any. If it is a high-level | |
3155 | * console device, then initialize it and return successfully. | |
3156 | * If it is a low-level console device, then initialize it and | |
3157 | * return unsuccessfully. It must be initialized in both cases | |
3158 | * for early use by console drivers and debuggers. Initializing | |
3159 | * the hardware is not necessary in all cases, since the i/o | |
3160 | * routines initialize it on the fly, but it is necessary if | |
3161 | * input might arrive while the hardware is switched back to an | |
3162 | * uninitialized state. We can't handle multiple console devices | |
3163 | * yet because our low-level routines don't take a device arg. | |
3164 | * We trust the user to set the console flags properly so that we | |
3165 | * don't need to probe. | |
3166 | */ | |
3167 | cp->cn_pri = CN_DEAD; | |
3168 | ||
3169 | for (unit = 0; unit < 16; unit++) { /* XXX need to know how many */ | |
3170 | int flags; | |
3171 | int disabled; | |
3172 | if (resource_int_value("sio", unit, "disabled", &disabled) == 0) { | |
3173 | if (disabled) | |
3174 | continue; | |
3175 | } | |
3176 | if (resource_int_value("sio", unit, "flags", &flags)) | |
3177 | continue; | |
3178 | if (COM_CONSOLE(flags) || COM_DEBUGGER(flags)) { | |
3179 | int port; | |
3180 | Port_t iobase; | |
3181 | ||
3182 | if (resource_int_value("sio", unit, "port", &port)) | |
3183 | continue; | |
3184 | iobase = port; | |
3185 | s = spltty(); | |
3186 | if (boothowto & RB_SERIAL) { | |
3187 | boot_speed = | |
3188 | siocngetspeed(iobase, comdefaultrclk); | |
3189 | if (boot_speed) | |
3190 | comdefaultrate = boot_speed; | |
3191 | } | |
3192 | ||
3193 | /* | |
3194 | * Initialize the divisor latch. We can't rely on | |
3195 | * siocnopen() to do this the first time, since it | |
3196 | * avoids writing to the latch if the latch appears | |
3197 | * to have the correct value. Also, if we didn't | |
3198 | * just read the speed from the hardware, then we | |
3199 | * need to set the speed in hardware so that | |
3200 | * switching it later is null. | |
3201 | */ | |
3202 | cfcr = inb(iobase + com_cfcr); | |
3203 | outb(iobase + com_cfcr, CFCR_DLAB | cfcr); | |
3204 | divisor = siodivisor(comdefaultrclk, comdefaultrate); | |
3205 | outb(iobase + com_dlbl, divisor & 0xff); | |
3206 | outb(iobase + com_dlbh, divisor >> 8); | |
3207 | outb(iobase + com_cfcr, cfcr); | |
3208 | ||
3209 | siocnopen(&sp, iobase, comdefaultrate); | |
3210 | ||
3211 | splx(s); | |
3212 | if (COM_CONSOLE(flags) && !COM_LLCONSOLE(flags)) { | |
3213 | cp->cn_dev = makedev(CDEV_MAJOR, unit); | |
3214 | cp->cn_pri = COM_FORCECONSOLE(flags) | |
3215 | || boothowto & RB_SERIAL | |
3216 | ? CN_REMOTE : CN_NORMAL; | |
3217 | siocniobase = iobase; | |
3218 | siocnunit = unit; | |
3219 | } | |
3220 | if (COM_DEBUGGER(flags)) { | |
3221 | printf("sio%d: gdb debugging port\n", unit); | |
3222 | siogdbiobase = iobase; | |
3223 | siogdbunit = unit; | |
3224 | #if DDB > 0 | |
3225 | gdbdev = makedev(CDEV_MAJOR, unit); | |
3226 | gdb_getc = siocngetc; | |
3227 | gdb_putc = siocnputc; | |
3228 | #endif | |
3229 | } | |
3230 | } | |
3231 | } | |
3232 | #ifdef __i386__ | |
3233 | #if DDB > 0 | |
3234 | /* | |
3235 | * XXX Ugly Compatability. | |
3236 | * If no gdb port has been specified, set it to be the console | |
3237 | * as some configuration files don't specify the gdb port. | |
3238 | */ | |
3239 | if (gdbdev == NODEV && (boothowto & RB_GDB)) { | |
3240 | printf("Warning: no GDB port specified. Defaulting to sio%d.\n", | |
3241 | siocnunit); | |
3242 | printf("Set flag 0x80 on desired GDB port in your\n"); | |
3243 | printf("configuration file (currently sio only).\n"); | |
3244 | siogdbiobase = siocniobase; | |
3245 | siogdbunit = siocnunit; | |
3246 | gdbdev = makedev(CDEV_MAJOR, siocnunit); | |
3247 | gdb_getc = siocngetc; | |
3248 | gdb_putc = siocnputc; | |
3249 | } | |
3250 | #endif | |
3251 | #endif | |
3252 | } | |
3253 | ||
3254 | #ifdef __alpha__ | |
3255 | ||
3256 | CONS_DRIVER(sio, NULL, NULL, NULL, siocngetc, siocncheckc, siocnputc, NULL); | |
3257 | ||
3258 | int | |
3259 | siocnattach(port, speed) | |
3260 | int port; | |
3261 | int speed; | |
3262 | { | |
3263 | int s; | |
3264 | u_char cfcr; | |
3265 | u_int divisor; | |
3266 | struct siocnstate sp; | |
3267 | ||
3268 | siocniobase = port; | |
3269 | comdefaultrate = speed; | |
3270 | sio_consdev.cn_pri = CN_NORMAL; | |
3271 | sio_consdev.cn_dev = makedev(CDEV_MAJOR, 0); | |
3272 | ||
3273 | s = spltty(); | |
3274 | ||
3275 | /* | |
3276 | * Initialize the divisor latch. We can't rely on | |
3277 | * siocnopen() to do this the first time, since it | |
3278 | * avoids writing to the latch if the latch appears | |
3279 | * to have the correct value. Also, if we didn't | |
3280 | * just read the speed from the hardware, then we | |
3281 | * need to set the speed in hardware so that | |
3282 | * switching it later is null. | |
3283 | */ | |
3284 | cfcr = inb(siocniobase + com_cfcr); | |
3285 | outb(siocniobase + com_cfcr, CFCR_DLAB | cfcr); | |
3286 | divisor = siodivisor(comdefaultrclk, comdefaultrate); | |
3287 | outb(siocniobase + com_dlbl, divisor & 0xff); | |
3288 | outb(siocniobase + com_dlbh, divisor >> 8); | |
3289 | outb(siocniobase + com_cfcr, cfcr); | |
3290 | ||
3291 | siocnopen(&sp, siocniobase, comdefaultrate); | |
3292 | splx(s); | |
3293 | ||
3294 | cn_tab = &sio_consdev; | |
3295 | return (0); | |
3296 | } | |
3297 | ||
3298 | int | |
3299 | siogdbattach(port, speed) | |
3300 | int port; | |
3301 | int speed; | |
3302 | { | |
3303 | int s; | |
3304 | u_char cfcr; | |
3305 | u_int divisor; | |
3306 | struct siocnstate sp; | |
3307 | ||
3308 | siogdbiobase = port; | |
3309 | gdbdefaultrate = speed; | |
3310 | ||
3311 | s = spltty(); | |
3312 | ||
3313 | /* | |
3314 | * Initialize the divisor latch. We can't rely on | |
3315 | * siocnopen() to do this the first time, since it | |
3316 | * avoids writing to the latch if the latch appears | |
3317 | * to have the correct value. Also, if we didn't | |
3318 | * just read the speed from the hardware, then we | |
3319 | * need to set the speed in hardware so that | |
3320 | * switching it later is null. | |
3321 | */ | |
3322 | cfcr = inb(siogdbiobase + com_cfcr); | |
3323 | outb(siogdbiobase + com_cfcr, CFCR_DLAB | cfcr); | |
3324 | divisor = siodivisor(comdefaultrclk, gdbdefaultrate); | |
3325 | outb(siogdbiobase + com_dlbl, divisor & 0xff); | |
3326 | outb(siogdbiobase + com_dlbh, divisor >> 8); | |
3327 | outb(siogdbiobase + com_cfcr, cfcr); | |
3328 | ||
3329 | siocnopen(&sp, siogdbiobase, gdbdefaultrate); | |
3330 | splx(s); | |
3331 | ||
3332 | return (0); | |
3333 | } | |
3334 | ||
3335 | #endif | |
3336 | ||
3337 | static void | |
3338 | siocninit(cp) | |
3339 | struct consdev *cp; | |
3340 | { | |
3341 | comconsole = DEV_TO_UNIT(cp->cn_dev); | |
3342 | } | |
3343 | ||
3344 | static int | |
3345 | siocncheckc(dev) | |
3346 | dev_t dev; | |
3347 | { | |
3348 | int c; | |
3349 | Port_t iobase; | |
3350 | int s; | |
3351 | struct siocnstate sp; | |
3352 | ||
3353 | if (minor(dev) == siogdbunit) | |
3354 | iobase = siogdbiobase; | |
3355 | else | |
3356 | iobase = siocniobase; | |
3357 | s = spltty(); | |
3358 | siocnopen(&sp, iobase, comdefaultrate); | |
3359 | if (inb(iobase + com_lsr) & LSR_RXRDY) | |
3360 | c = inb(iobase + com_data); | |
3361 | else | |
3362 | c = -1; | |
3363 | siocnclose(&sp, iobase); | |
3364 | splx(s); | |
3365 | return (c); | |
3366 | } | |
3367 | ||
3368 | ||
3369 | int | |
3370 | siocngetc(dev) | |
3371 | dev_t dev; | |
3372 | { | |
3373 | int c; | |
3374 | Port_t iobase; | |
3375 | int s; | |
3376 | struct siocnstate sp; | |
3377 | ||
3378 | if (minor(dev) == siogdbunit) | |
3379 | iobase = siogdbiobase; | |
3380 | else | |
3381 | iobase = siocniobase; | |
3382 | s = spltty(); | |
3383 | siocnopen(&sp, iobase, comdefaultrate); | |
3384 | while (!(inb(iobase + com_lsr) & LSR_RXRDY)) | |
3385 | ; | |
3386 | c = inb(iobase + com_data); | |
3387 | siocnclose(&sp, iobase); | |
3388 | splx(s); | |
3389 | return (c); | |
3390 | } | |
3391 | ||
3392 | void | |
3393 | siocnputc(dev, c) | |
3394 | dev_t dev; | |
3395 | int c; | |
3396 | { | |
3397 | int s; | |
3398 | struct siocnstate sp; | |
3399 | Port_t iobase; | |
3400 | ||
3401 | if (minor(dev) == siogdbunit) | |
3402 | iobase = siogdbiobase; | |
3403 | else | |
3404 | iobase = siocniobase; | |
3405 | s = spltty(); | |
3406 | siocnopen(&sp, iobase, comdefaultrate); | |
3407 | siocntxwait(iobase); | |
3408 | outb(iobase + com_data, c); | |
3409 | siocnclose(&sp, iobase); | |
3410 | splx(s); | |
3411 | } | |
3412 | ||
3413 | #ifdef __alpha__ | |
3414 | int | |
3415 | siogdbgetc() | |
3416 | { | |
3417 | int c; | |
3418 | Port_t iobase; | |
3419 | int s; | |
3420 | struct siocnstate sp; | |
3421 | ||
3422 | iobase = siogdbiobase; | |
3423 | s = spltty(); | |
3424 | siocnopen(&sp, iobase, gdbdefaultrate); | |
3425 | while (!(inb(iobase + com_lsr) & LSR_RXRDY)) | |
3426 | ; | |
3427 | c = inb(iobase + com_data); | |
3428 | siocnclose(&sp, iobase); | |
3429 | splx(s); | |
3430 | return (c); | |
3431 | } | |
3432 | ||
3433 | void | |
3434 | siogdbputc(c) | |
3435 | int c; | |
3436 | { | |
3437 | int s; | |
3438 | struct siocnstate sp; | |
3439 | ||
3440 | s = spltty(); | |
3441 | siocnopen(&sp, siogdbiobase, gdbdefaultrate); | |
3442 | siocntxwait(siogdbiobase); | |
3443 | outb(siogdbiobase + com_data, c); | |
3444 | siocnclose(&sp, siogdbiobase); | |
3445 | splx(s); | |
3446 | } | |
3447 | #endif | |
3448 | ||
3449 | DRIVER_MODULE(sio, isa, sio_isa_driver, sio_devclass, 0, 0); | |
3450 | #if NCARD > 0 | |
3451 | DRIVER_MODULE(sio, pccard, sio_pccard_driver, sio_devclass, 0, 0); | |
3452 | #endif | |
3453 | #if NPCI > 0 | |
3454 | DRIVER_MODULE(sio, pci, sio_pci_driver, sio_devclass, 0, 0); | |
3455 | #endif | |
3456 | #if NPUC > 0 | |
3457 | DRIVER_MODULE(sio, puc, sio_puc_driver, sio_devclass, 0, 0); | |
3458 | #endif |