| Commit | Line | Data |
|---|---|---|
| 984263bc MD |
1 | /*- |
| 2 | * cyclades cyclom-y serial driver | |
| 3 | * Andrew Herbert <andrew@werple.apana.org.au>, 17 August 1993 | |
| 4 | * | |
| 5 | * Copyright (c) 1993 Andrew Herbert. | |
| 6 | * All rights reserved. | |
| 7 | * | |
| 8 | * Redistribution and use in source and binary forms, with or without | |
| 9 | * modification, are permitted provided that the following conditions | |
| 10 | * are met: | |
| 11 | * 1. Redistributions of source code must retain the above copyright | |
| 12 | * notice, this list of conditions and the following disclaimer. | |
| 13 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 14 | * notice, this list of conditions and the following disclaimer in the | |
| 15 | * documentation and/or other materials provided with the distribution. | |
| 16 | * 3. The name Andrew Herbert may not be used to endorse or promote products | |
| 17 | * derived from this software without specific prior written permission. | |
| 18 | * | |
| 19 | * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
| 20 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
| 21 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | |
| 22 | * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
| 23 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| 24 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| 25 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
| 26 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
| 27 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
| 28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 29 | * | |
| 30 | * $FreeBSD: src/sys/i386/isa/cy.c,v 1.97.2.2 2001/08/22 13:04:58 bde Exp $ | |
| 32eda400 | 31 | * $DragonFly: src/sys/dev/serial/cy/cy.c,v 1.30 2008/10/20 13:16:58 sephe Exp $ |
| 984263bc MD |
32 | */ |
| 33 | ||
| 34 | #include "opt_compat.h" | |
| 1f2de5d4 | 35 | #include "use_cy.h" |
| 984263bc MD |
36 | |
| 37 | /* | |
| 38 | * TODO: | |
| 39 | * Atomic COR change. | |
| 40 | * Consoles. | |
| 41 | */ | |
| 42 | ||
| 43 | /* | |
| 44 | * Temporary compile-time configuration options. | |
| 45 | */ | |
| 46 | #define RxFifoThreshold (CD1400_RX_FIFO_SIZE / 2) | |
| 47 | /* Number of chars in the receiver FIFO before an | |
| 48 | * an interrupt is generated. Should depend on | |
| 49 | * line speed. Needs to be about 6 on a 486DX33 | |
| 50 | * for 4 active ports at 115200 bps. Why doesn't | |
| 51 | * 10 work? | |
| 52 | */ | |
| 53 | #define PollMode /* Use polling-based irq service routine, not the | |
| 54 | * hardware svcack lines. Must be defined for | |
| 55 | * Cyclom-16Y boards. Less efficient for Cyclom-8Ys, | |
| 56 | * and stops 4 * 115200 bps from working. | |
| 57 | */ | |
| 58 | #undef Smarts /* Enable slightly more CD1400 intelligence. Mainly | |
| 59 | * the output CR/LF processing, plus we can avoid a | |
| 60 | * few checks usually done in ttyinput(). | |
| 61 | * | |
| 62 | * XXX not fully implemented, and not particularly | |
| 63 | * worthwhile. | |
| 64 | */ | |
| 65 | #undef CyDebug /* Include debugging code (not very expensive). */ | |
| 66 | ||
| 67 | /* These will go away. */ | |
| 68 | #undef SOFT_CTS_OFLOW | |
| 69 | #define SOFT_HOTCHAR | |
| 70 | ||
| 71 | #include <sys/param.h> | |
| 72 | #include <sys/systm.h> | |
| 73 | #include <sys/tty.h> | |
| 74 | #include <sys/proc.h> | |
| 895c1f85 | 75 | #include <sys/priv.h> |
| 984263bc MD |
76 | #include <sys/conf.h> |
| 77 | #include <sys/dkstat.h> | |
| 78 | #include <sys/fcntl.h> | |
| 79 | #include <sys/interrupt.h> | |
| 80 | #include <sys/kernel.h> | |
| 81 | #include <sys/malloc.h> | |
| 82 | #include <sys/syslog.h> | |
| 8d77660e | 83 | #include <sys/thread2.h> |
| 984263bc | 84 | #include <machine/clock.h> |
| 984263bc MD |
85 | #ifndef SMP |
| 86 | #include <machine/lock.h> | |
| 87 | #endif | |
| 88 | #include <machine/psl.h> | |
| 89 | ||
| 21ce0dfa | 90 | #include <bus/isa/isa_device.h> |
| 1f2de5d4 | 91 | #include "cyreg.h" |
| a9295349 | 92 | #include <machine_base/isa/ic/cd1400.h> |
| 984263bc MD |
93 | |
| 94 | #ifdef SMP | |
| 7b95be2a MD |
95 | #define disable_intr() com_lock() |
| 96 | #define enable_intr() com_unlock() | |
| 32eda400 SZ |
97 | #else |
| 98 | #define disable_intr() ((void)0) | |
| 99 | #define enable_intr() ((void)0) | |
| 984263bc MD |
100 | #endif /* SMP */ |
| 101 | ||
| 102 | /* | |
| 103 | * Dictionary so that I can name everything *sio* or *com* to compare with | |
| 104 | * sio.c. There is also lots of ugly formatting and unnecessary ifdefs to | |
| 105 | * simplify the comparision. These will go away. | |
| 106 | */ | |
| 107 | #define LSR_BI CD1400_RDSR_BREAK | |
| 108 | #define LSR_FE CD1400_RDSR_FE | |
| 109 | #define LSR_OE CD1400_RDSR_OE | |
| 110 | #define LSR_PE CD1400_RDSR_PE | |
| 111 | #define MCR_DTR CD1400_MSVR2_DTR | |
| 112 | #define MCR_RTS CD1400_MSVR1_RTS | |
| 113 | #define MSR_CTS CD1400_MSVR2_CTS | |
| 114 | #define MSR_DCD CD1400_MSVR2_CD | |
| 115 | #define MSR_DSR CD1400_MSVR2_DSR | |
| 116 | #define MSR_RI CD1400_MSVR2_RI | |
| 117 | #define NSIO (NCY * CY_MAX_PORTS) | |
| 118 | #define comconsole cyconsole | |
| 119 | #define comdefaultrate cydefaultrate | |
| 120 | #define com_events cy_events | |
| 121 | #define comhardclose cyhardclose | |
| 122 | #define commctl cymctl | |
| 123 | #define comparam cyparam | |
| 124 | #define comspeed cyspeed | |
| 125 | #define comstart cystart | |
| 126 | #define comwakeup cywakeup | |
| 127 | #define nsio_tty ncy_tty | |
| 128 | #define p_com_addr p_cy_addr | |
| 129 | #define sioattach cyattach | |
| 130 | #define sioclose cyclose | |
| 131 | #define siodriver cydriver | |
| 132 | #define siodtrwakeup cydtrwakeup | |
| 133 | #define sioinput cyinput | |
| 134 | #define siointr cyintr | |
| 135 | #define siointr1 cyintr1 | |
| 136 | #define sioioctl cyioctl | |
| 137 | #define sioopen cyopen | |
| 138 | #define siopoll cypoll | |
| 139 | #define sioprobe cyprobe | |
| 140 | #define siosettimeout cysettimeout | |
| 141 | #define siosetwater cysetwater | |
| 142 | #define comstop cystop | |
| 143 | #define siowrite cywrite | |
| 144 | #define sio_registered cy_registered | |
| 145 | #define sio_timeout cy_timeout | |
| 146 | #define sio_timeout_handle cy_timeout_handle | |
| 147 | #define sio_timeouts_until_log cy_timeouts_until_log | |
| 148 | #define sio_tty cy_tty | |
| 149 | ||
| 150 | #define CY_MAX_PORTS (CD1400_NO_OF_CHANNELS * CY_MAX_CD1400s) | |
| 151 | ||
| 152 | /* We encode the cyclom unit number (cyu) in spare bits in the IVR's. */ | |
| 153 | #define CD1400_xIVR_CHAN_SHIFT 3 | |
| 154 | #define CD1400_xIVR_CHAN 0x1F | |
| 155 | ||
| 156 | /* | |
| 157 | * ETC states. com->etc may also contain a hardware ETC command value, | |
| 158 | * meaning that execution of that command is pending. | |
| 159 | */ | |
| 160 | #define ETC_NONE 0 /* we depend on bzero() setting this */ | |
| 161 | #define ETC_BREAK_STARTING 1 | |
| 162 | #define ETC_BREAK_STARTED 2 | |
| 163 | #define ETC_BREAK_ENDING 3 | |
| 164 | #define ETC_BREAK_ENDED 4 | |
| 165 | ||
| 166 | #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ | |
| 167 | ||
| 168 | #define CALLOUT_MASK 0x80 | |
| 169 | #define CONTROL_MASK 0x60 | |
| 170 | #define CONTROL_INIT_STATE 0x20 | |
| 171 | #define CONTROL_LOCK_STATE 0x40 | |
| 172 | #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) | |
| 173 | #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) | |
| 174 | /* | |
| 175 | * Not all of the magic is parametrized in the following macros. 16 and | |
| 176 | * 0xff are related to the bitfields in a udev_t. CY_MAX_PORTS must be | |
| 177 | * ((0xff & ~MINOR_MAGIC_MASK) + 1) for things to work. | |
| 178 | */ | |
| 179 | #define MINOR_TO_UNIT(mynor) (((mynor) >> 16) * CY_MAX_PORTS \ | |
| 180 | | (((mynor) & 0xff) & ~MINOR_MAGIC_MASK)) | |
| 181 | #define UNIT_TO_MINOR(unit) (((unit) / CY_MAX_PORTS) << 16 \ | |
| 182 | | (((unit) & 0xff) & ~MINOR_MAGIC_MASK)) | |
| 183 | ||
| 184 | /* | |
| 185 | * com state bits. | |
| 186 | * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher | |
| 187 | * than the other bits so that they can be tested as a group without masking | |
| 188 | * off the low bits. | |
| 189 | * | |
| 190 | * The following com and tty flags correspond closely: | |
| 191 | * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and | |
| 192 | * comstop()) | |
| 193 | * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart()) | |
| 194 | * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) | |
| 195 | * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) | |
| 196 | * TS_FLUSH is not used. | |
| 197 | * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. | |
| 198 | * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). | |
| 199 | */ | |
| 200 | #define CS_BUSY 0x80 /* output in progress */ | |
| 201 | #define CS_TTGO 0x40 /* output not stopped by XOFF */ | |
| 202 | #define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ | |
| 203 | #define CS_CHECKMSR 1 /* check of MSR scheduled */ | |
| 204 | #define CS_CTS_OFLOW 2 /* use CTS output flow control */ | |
| 205 | #define CS_DTR_OFF 0x10 /* DTR held off */ | |
| 206 | #define CS_ODONE 4 /* output completed */ | |
| 207 | #define CS_RTS_IFLOW 8 /* use RTS input flow control */ | |
| 208 | #define CSE_ODONE 1 /* output transmitted */ | |
| 209 | ||
| 210 | static char const * const error_desc[] = { | |
| 211 | #define CE_OVERRUN 0 | |
| 212 | "silo overflow", | |
| 213 | #define CE_INTERRUPT_BUF_OVERFLOW 1 | |
| 214 | "interrupt-level buffer overflow", | |
| 215 | #define CE_TTY_BUF_OVERFLOW 2 | |
| 216 | "tty-level buffer overflow", | |
| 217 | }; | |
| 218 | ||
| 219 | #define CE_NTYPES 3 | |
| 220 | #define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) | |
| 221 | ||
| 222 | /* types. XXX - should be elsewhere */ | |
| 223 | typedef u_char bool_t; /* boolean */ | |
| 224 | typedef u_char volatile *cy_addr; | |
| 225 | ||
| 226 | /* queue of linear buffers */ | |
| 227 | struct lbq { | |
| 228 | u_char *l_head; /* next char to process */ | |
| 229 | u_char *l_tail; /* one past the last char to process */ | |
| 230 | struct lbq *l_next; /* next in queue */ | |
| 231 | bool_t l_queued; /* nonzero if queued */ | |
| 232 | }; | |
| 233 | ||
| 234 | /* com device structure */ | |
| 235 | struct com_s { | |
| 236 | u_char state; /* miscellaneous flag bits */ | |
| 237 | bool_t active_out; /* nonzero if the callout device is open */ | |
| 238 | #if 0 | |
| 239 | u_char cfcr_image; /* copy of value written to CFCR */ | |
| 240 | #endif | |
| 241 | u_char etc; /* pending Embedded Transmit Command */ | |
| 242 | u_char extra_state; /* more flag bits, separate for order trick */ | |
| 243 | #if 0 | |
| 244 | u_char fifo_image; /* copy of value written to FIFO */ | |
| 245 | #endif | |
| 246 | u_char gfrcr_image; /* copy of value read from GFRCR */ | |
| 247 | #if 0 | |
| 248 | bool_t hasfifo; /* nonzero for 16550 UARTs */ | |
| 249 | bool_t loses_outints; /* nonzero if device loses output interrupts */ | |
| 250 | #endif | |
| 251 | u_char mcr_dtr; /* MCR bit that is wired to DTR */ | |
| 252 | u_char mcr_image; /* copy of value written to MCR */ | |
| 253 | u_char mcr_rts; /* MCR bit that is wired to RTS */ | |
| 254 | #if 0 | |
| 255 | #ifdef COM_MULTIPORT | |
| 256 | bool_t multiport; /* is this unit part of a multiport device? */ | |
| 257 | #endif /* COM_MULTIPORT */ | |
| 258 | bool_t no_irq; /* nonzero if irq is not attached */ | |
| 259 | bool_t poll; /* nonzero if polling is required */ | |
| 260 | bool_t poll_output; /* nonzero if polling for output is required */ | |
| 261 | #endif | |
| 262 | int unit; /* unit number */ | |
| 263 | int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ | |
| ed81d395 | 264 | struct callout dtr_ch; |
| 984263bc MD |
265 | #if 0 |
| 266 | u_int tx_fifo_size; | |
| 267 | #endif | |
| 268 | u_int wopeners; /* # processes waiting for DCD in open() */ | |
| 269 | ||
| 270 | /* | |
| 271 | * The high level of the driver never reads status registers directly | |
| 272 | * because there would be too many side effects to handle conveniently. | |
| 273 | * Instead, it reads copies of the registers stored here by the | |
| 274 | * interrupt handler. | |
| 275 | */ | |
| 276 | u_char last_modem_status; /* last MSR read by intr handler */ | |
| 277 | u_char prev_modem_status; /* last MSR handled by high level */ | |
| 278 | ||
| 279 | u_char hotchar; /* ldisc-specific char to be handled ASAP */ | |
| 280 | u_char *ibuf; /* start of input buffer */ | |
| 281 | u_char *ibufend; /* end of input buffer */ | |
| 282 | u_char *ibufold; /* old input buffer, to be freed */ | |
| 283 | u_char *ihighwater; /* threshold in input buffer */ | |
| 284 | u_char *iptr; /* next free spot in input buffer */ | |
| 285 | int ibufsize; /* size of ibuf (not include error bytes) */ | |
| 286 | int ierroff; /* offset of error bytes in ibuf */ | |
| 287 | ||
| 288 | struct lbq obufq; /* head of queue of output buffers */ | |
| 289 | struct lbq obufs[2]; /* output buffers */ | |
| 290 | ||
| 291 | int cy_align; /* index for register alignment */ | |
| 292 | cy_addr cy_iobase; /* base address of this port's cyclom */ | |
| 293 | cy_addr iobase; /* base address of this port's cd1400 */ | |
| 294 | int mcr_rts_reg; /* cd1400 reg number of reg holding mcr_rts */ | |
| 295 | ||
| 296 | struct tty *tp; /* cross reference */ | |
| 297 | ||
| 298 | /* Initial state. */ | |
| 299 | struct termios it_in; /* should be in struct tty */ | |
| 300 | struct termios it_out; | |
| 301 | ||
| 302 | /* Lock state. */ | |
| 303 | struct termios lt_in; /* should be in struct tty */ | |
| 304 | struct termios lt_out; | |
| 305 | ||
| 306 | bool_t do_timestamp; | |
| 307 | bool_t do_dcd_timestamp; | |
| 308 | struct timeval timestamp; | |
| 309 | struct timeval dcd_timestamp; | |
| 310 | ||
| 311 | u_long bytes_in; /* statistics */ | |
| 312 | u_long bytes_out; | |
| 313 | u_int delta_error_counts[CE_NTYPES]; | |
| 314 | u_long error_counts[CE_NTYPES]; | |
| 315 | ||
| 316 | u_int recv_exception; /* exception chars received */ | |
| 317 | u_int mdm; /* modem signal changes */ | |
| 318 | #ifdef CyDebug | |
| 319 | u_int start_count; /* no. of calls to comstart() */ | |
| 320 | u_int start_real; /* no. of calls that did something */ | |
| 321 | #endif | |
| 322 | u_char car; /* CD1400 CAR shadow (if first unit in cd) */ | |
| 323 | u_char channel_control;/* CD1400 CCR control command shadow */ | |
| 324 | u_char cor[3]; /* CD1400 COR1-3 shadows */ | |
| 325 | u_char intr_enable; /* CD1400 SRER shadow */ | |
| 326 | ||
| 327 | /* | |
| 328 | * Data area for output buffers. Someday we should build the output | |
| 329 | * buffer queue without copying data. | |
| 330 | */ | |
| 331 | u_char obuf1[256]; | |
| 332 | u_char obuf2[256]; | |
| 333 | }; | |
| 334 | ||
| 335 | /* PCI driver entry point. */ | |
| 5ca58d54 | 336 | int cyattach_common (cy_addr cy_iobase, int cy_align); |
| 1b51b0fa | 337 | void siointr(void *); |
| 984263bc | 338 | |
| 5ca58d54 RG |
339 | static int cy_units (cy_addr cy_iobase, int cy_align); |
| 340 | static int sioattach (struct isa_device *dev); | |
| 341 | static void cd1400_channel_cmd (struct com_s *com, int cmd); | |
| 342 | static void cd1400_channel_cmd_wait (struct com_s *com); | |
| 343 | static void cd_etc (struct com_s *com, int etc); | |
| 344 | static int cd_getreg (struct com_s *com, int reg); | |
| 345 | static void cd_setreg (struct com_s *com, int reg, int val); | |
| 984263bc | 346 | static timeout_t siodtrwakeup; |
| 5ca58d54 RG |
347 | static void comhardclose (struct com_s *com); |
| 348 | static void sioinput (struct com_s *com); | |
| 984263bc | 349 | #if 0 |
| 5ca58d54 | 350 | static void siointr1 (struct com_s *com); |
| 984263bc | 351 | #endif |
| 5ca58d54 RG |
352 | static int commctl (struct com_s *com, int bits, int how); |
| 353 | static int comparam (struct tty *tp, struct termios *t); | |
| 7b95be2a | 354 | static inthand2_t siopoll; |
| 5ca58d54 RG |
355 | static int sioprobe (struct isa_device *dev); |
| 356 | static void siosettimeout (void); | |
| 357 | static int siosetwater (struct com_s *com, speed_t speed); | |
| 358 | static int comspeed (speed_t speed, u_long cy_clock, | |
| 359 | int *prescaler_io); | |
| 360 | static void comstart (struct tty *tp); | |
| 361 | static void comstop (struct tty *tp, int rw); | |
| 984263bc | 362 | static timeout_t comwakeup; |
| 5ca58d54 RG |
363 | static void disc_optim (struct tty *tp, struct termios *t, |
| 364 | struct com_s *com); | |
| 984263bc MD |
365 | |
| 366 | #ifdef CyDebug | |
| 5ca58d54 | 367 | void cystatus (int unit); |
| 984263bc MD |
368 | #endif |
| 369 | ||
| 370 | static char driver_name[] = "cy"; | |
| 371 | ||
| 372 | /* table and macro for fast conversion from a unit number to its com struct */ | |
| 373 | static struct com_s *p_com_addr[NSIO]; | |
| 374 | #define com_addr(unit) (p_com_addr[unit]) | |
| 375 | ||
| 376 | struct isa_driver siodriver = { | |
| 377 | sioprobe, sioattach, driver_name | |
| 378 | }; | |
| 379 | ||
| 380 | static d_open_t sioopen; | |
| 381 | static d_close_t sioclose; | |
| 382 | static d_write_t siowrite; | |
| 383 | static d_ioctl_t sioioctl; | |
| 384 | ||
| 385 | #define CDEV_MAJOR 48 | |
| fef8985e MD |
386 | static struct dev_ops sio_ops = { |
| 387 | { driver_name, CDEV_MAJOR, D_TTY | D_KQFILTER }, | |
| 388 | .d_open = sioopen, | |
| 389 | .d_close = sioclose, | |
| 390 | .d_read = ttyread, | |
| 391 | .d_write = siowrite, | |
| 392 | .d_ioctl = sioioctl, | |
| 393 | .d_poll = ttypoll, | |
| a32446b7 MD |
394 | .d_kqfilter = ttykqfilter, |
| 395 | .d_revoke = ttyrevoke | |
| 984263bc MD |
396 | }; |
| 397 | ||
| 398 | static int comconsole = -1; | |
| 399 | static speed_t comdefaultrate = TTYDEF_SPEED; | |
| 400 | static u_int com_events; /* input chars + weighted output completions */ | |
| 401 | static bool_t sio_registered; | |
| 402 | static int sio_timeout; | |
| 403 | static int sio_timeouts_until_log; | |
| e8b273ff | 404 | static struct callout sio_timeout_handle; |
| 984263bc MD |
405 | #if 0 /* XXX */ |
| 406 | static struct tty *sio_tty[NSIO]; | |
| 407 | #else | |
| 408 | static struct tty sio_tty[NSIO]; | |
| 409 | #endif | |
| 410 | static const int nsio_tty = NSIO; | |
| 411 | ||
| 412 | #ifdef CyDebug | |
| 413 | static u_int cd_inbs; | |
| 414 | static u_int cy_inbs; | |
| 415 | static u_int cd_outbs; | |
| 416 | static u_int cy_outbs; | |
| 417 | static u_int cy_svrr_probes; | |
| 418 | static u_int cy_timeouts; | |
| 419 | #endif | |
| 420 | ||
| 421 | static int cy_chip_offset[] = { | |
| 422 | 0x0000, 0x0400, 0x0800, 0x0c00, 0x0200, 0x0600, 0x0a00, 0x0e00, | |
| 423 | }; | |
| 424 | static int cy_nr_cd1400s[NCY]; | |
| 425 | static int cy_total_devices; | |
| 426 | #undef RxFifoThreshold | |
| 427 | static int volatile RxFifoThreshold = (CD1400_RX_FIFO_SIZE / 2); | |
| 428 | ||
| 429 | static int | |
| ce63e0fa | 430 | sioprobe(struct isa_device *dev) |
| 984263bc MD |
431 | { |
| 432 | cy_addr iobase; | |
| 433 | ||
| 434 | iobase = (cy_addr)dev->id_maddr; | |
| 435 | ||
| 436 | /* Cyclom-16Y hardware reset (Cyclom-8Ys don't care) */ | |
| 437 | cy_inb(iobase, CY16_RESET, 0); /* XXX? */ | |
| 438 | DELAY(500); /* wait for the board to get its act together */ | |
| 439 | ||
| 440 | /* this is needed to get the board out of reset */ | |
| 441 | cy_outb(iobase, CY_CLEAR_INTR, 0, 0); | |
| 442 | DELAY(500); | |
| 443 | ||
| 444 | return (cy_units(iobase, 0) == 0 ? 0 : -1); | |
| 445 | } | |
| 446 | ||
| 447 | static int | |
| ce63e0fa | 448 | cy_units(cy_addr cy_iobase, int cy_align) |
| 984263bc MD |
449 | { |
| 450 | int cyu; | |
| f15db79e | 451 | u_char firmware_version = 0; /* assign to avoid warning */ |
| 984263bc MD |
452 | int i; |
| 453 | cy_addr iobase; | |
| 454 | ||
| 455 | for (cyu = 0; cyu < CY_MAX_CD1400s; ++cyu) { | |
| 456 | iobase = cy_iobase + (cy_chip_offset[cyu] << cy_align); | |
| 457 | ||
| 458 | /* wait for chip to become ready for new command */ | |
| 459 | for (i = 0; i < 10; i++) { | |
| 460 | DELAY(50); | |
| 461 | if (!cd_inb(iobase, CD1400_CCR, cy_align)) | |
| 462 | break; | |
| 463 | } | |
| 464 | ||
| 465 | /* clear the GFRCR register */ | |
| 466 | cd_outb(iobase, CD1400_GFRCR, cy_align, 0); | |
| 467 | ||
| 468 | /* issue a reset command */ | |
| 469 | cd_outb(iobase, CD1400_CCR, cy_align, | |
| 470 | CD1400_CCR_CMDRESET | CD1400_CCR_FULLRESET); | |
| 471 | ||
| 472 | /* wait for the CD1400 to initialize itself */ | |
| 473 | for (i = 0; i < 200; i++) { | |
| 474 | DELAY(50); | |
| 475 | ||
| 476 | /* retrieve firmware version */ | |
| 477 | firmware_version = cd_inb(iobase, CD1400_GFRCR, | |
| 478 | cy_align); | |
| 479 | if ((firmware_version & 0xf0) == 0x40) | |
| 480 | break; | |
| 481 | } | |
| 482 | ||
| 483 | /* | |
| 484 | * Anything in the 0x40-0x4F range is fine. | |
| 485 | * If one CD1400 is bad then we don't support higher | |
| 486 | * numbered good ones on this board. | |
| 487 | */ | |
| 488 | if ((firmware_version & 0xf0) != 0x40) | |
| 489 | break; | |
| 490 | } | |
| 491 | return (cyu); | |
| 492 | } | |
| 493 | ||
| 494 | static int | |
| ce63e0fa | 495 | sioattach(struct isa_device *isdp) |
| 984263bc MD |
496 | { |
| 497 | int adapter; | |
| 498 | ||
| 499 | adapter = cyattach_common((cy_addr) isdp->id_maddr, 0); | |
| 500 | if (adapter < 0) | |
| 501 | return (0); | |
| 502 | ||
| 503 | /* | |
| 504 | * XXX | |
| 505 | * This kludge is to allow ISA/PCI device specifications in the | |
| 506 | * kernel config file to be in any order. | |
| 507 | */ | |
| 508 | if (isdp->id_unit != adapter) { | |
| e3869ec7 | 509 | kprintf("cy%d: attached as cy%d\n", isdp->id_unit, adapter); |
| 984263bc MD |
510 | isdp->id_unit = adapter; /* XXX */ |
| 511 | } | |
| 1b51b0fa | 512 | isdp->id_intr = (inthand2_t *)siointr; |
| 984263bc MD |
513 | /* isdp->id_ri_flags |= RI_FAST; XXX unimplemented - use newbus! */ |
| 514 | return (1); | |
| 515 | } | |
| 516 | ||
| 517 | int | |
| ce63e0fa | 518 | cyattach_common(cy_addr cy_iobase, int cy_align) |
| 984263bc MD |
519 | { |
| 520 | int adapter; | |
| 521 | int cyu; | |
| 522 | u_char firmware_version; | |
| 523 | cy_addr iobase; | |
| 524 | int minorbase; | |
| 525 | int ncyu; | |
| 526 | int unit; | |
| 527 | ||
| 528 | adapter = cy_total_devices; | |
| 529 | if ((u_int)adapter >= NCY) { | |
| e3869ec7 | 530 | kprintf( |
| 984263bc MD |
531 | "cy%d: can't attach adapter: insufficient cy devices configured\n", |
| 532 | adapter); | |
| 533 | return (-1); | |
| 534 | } | |
| 535 | ncyu = cy_units(cy_iobase, cy_align); | |
| 536 | if (ncyu == 0) | |
| 537 | return (-1); | |
| 538 | cy_nr_cd1400s[adapter] = ncyu; | |
| 539 | cy_total_devices++; | |
| 540 | ||
| 541 | unit = adapter * CY_MAX_PORTS; | |
| 542 | for (cyu = 0; cyu < ncyu; ++cyu) { | |
| 543 | int cdu; | |
| 544 | ||
| 545 | iobase = (cy_addr) (cy_iobase | |
| 546 | + (cy_chip_offset[cyu] << cy_align)); | |
| 547 | firmware_version = cd_inb(iobase, CD1400_GFRCR, cy_align); | |
| 548 | ||
| 549 | /* Set up a receive timeout period of than 1+ ms. */ | |
| 550 | cd_outb(iobase, CD1400_PPR, cy_align, | |
| 551 | howmany(CY_CLOCK(firmware_version) | |
| 552 | / CD1400_PPR_PRESCALER, 1000)); | |
| 553 | ||
| 554 | for (cdu = 0; cdu < CD1400_NO_OF_CHANNELS; ++cdu, ++unit) { | |
| 555 | struct com_s *com; | |
| 984263bc | 556 | |
| efda3bd0 | 557 | com = kmalloc(sizeof *com, M_DEVBUF, M_WAITOK | M_ZERO); |
| 984263bc | 558 | com->unit = unit; |
| ed81d395 | 559 | callout_init(&com->dtr_ch); |
| 984263bc MD |
560 | com->gfrcr_image = firmware_version; |
| 561 | if (CY_RTS_DTR_SWAPPED(firmware_version)) { | |
| 562 | com->mcr_dtr = MCR_RTS; | |
| 563 | com->mcr_rts = MCR_DTR; | |
| 564 | com->mcr_rts_reg = CD1400_MSVR2; | |
| 565 | } else { | |
| 566 | com->mcr_dtr = MCR_DTR; | |
| 567 | com->mcr_rts = MCR_RTS; | |
| 568 | com->mcr_rts_reg = CD1400_MSVR1; | |
| 569 | } | |
| 570 | com->dtr_wait = 3 * hz; | |
| 571 | com->obufs[0].l_head = com->obuf1; | |
| 572 | com->obufs[1].l_head = com->obuf2; | |
| 573 | ||
| 574 | com->cy_align = cy_align; | |
| 575 | com->cy_iobase = cy_iobase; | |
| 576 | com->iobase = iobase; | |
| 577 | com->car = ~CD1400_CAR_CHAN; | |
| 578 | ||
| 579 | /* | |
| 580 | * We don't use all the flags from <sys/ttydefaults.h> since they | |
| 581 | * are only relevant for logins. It's important to have echo off | |
| 582 | * initially so that the line doesn't start blathering before the | |
| 583 | * echo flag can be turned off. | |
| 584 | */ | |
| 585 | com->it_in.c_iflag = 0; | |
| 586 | com->it_in.c_oflag = 0; | |
| 587 | com->it_in.c_cflag = TTYDEF_CFLAG; | |
| 588 | com->it_in.c_lflag = 0; | |
| 589 | if (unit == comconsole) { | |
| 590 | com->it_in.c_iflag = TTYDEF_IFLAG; | |
| 591 | com->it_in.c_oflag = TTYDEF_OFLAG; | |
| 592 | com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; | |
| 593 | com->it_in.c_lflag = TTYDEF_LFLAG; | |
| 594 | com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; | |
| 595 | } | |
| 596 | if (siosetwater(com, com->it_in.c_ispeed) != 0) { | |
| 597 | enable_intr(); | |
| efda3bd0 | 598 | kfree(com, M_DEVBUF); |
| 984263bc MD |
599 | return (0); |
| 600 | } | |
| 601 | enable_intr(); | |
| 602 | termioschars(&com->it_in); | |
| 603 | com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; | |
| 604 | com->it_out = com->it_in; | |
| 605 | ||
| 8d77660e | 606 | crit_enter(); |
| 984263bc | 607 | com_addr(unit) = com; |
| 8d77660e | 608 | crit_exit(); |
| 984263bc MD |
609 | |
| 610 | if (!sio_registered) { | |
| e8b273ff | 611 | callout_init(&sio_timeout_handle); |
| 477d3c1c | 612 | register_swi(SWI_TTY, siopoll, NULL, "cy", NULL); |
| 984263bc MD |
613 | sio_registered = TRUE; |
| 614 | } | |
| 615 | minorbase = UNIT_TO_MINOR(unit); | |
| fef8985e | 616 | make_dev(&sio_ops, minorbase, |
| 984263bc MD |
617 | UID_ROOT, GID_WHEEL, 0600, "ttyc%r%r", adapter, |
| 618 | unit % CY_MAX_PORTS); | |
| fef8985e | 619 | make_dev(&sio_ops, minorbase | CONTROL_INIT_STATE, |
| 984263bc MD |
620 | UID_ROOT, GID_WHEEL, 0600, "ttyic%r%r", adapter, |
| 621 | unit % CY_MAX_PORTS); | |
| fef8985e | 622 | make_dev(&sio_ops, minorbase | CONTROL_LOCK_STATE, |
| 984263bc MD |
623 | UID_ROOT, GID_WHEEL, 0600, "ttylc%r%r", adapter, |
| 624 | unit % CY_MAX_PORTS); | |
| fef8985e | 625 | make_dev(&sio_ops, minorbase | CALLOUT_MASK, |
| 984263bc MD |
626 | UID_UUCP, GID_DIALER, 0660, "cuac%r%r", adapter, |
| 627 | unit % CY_MAX_PORTS); | |
| fef8985e | 628 | make_dev(&sio_ops, minorbase | CALLOUT_MASK | CONTROL_INIT_STATE, |
| 984263bc MD |
629 | UID_UUCP, GID_DIALER, 0660, "cuaic%r%r", adapter, |
| 630 | unit % CY_MAX_PORTS); | |
| fef8985e | 631 | make_dev(&sio_ops, minorbase | CALLOUT_MASK | CONTROL_LOCK_STATE, |
| 984263bc MD |
632 | UID_UUCP, GID_DIALER, 0660, "cualc%r%r", adapter, |
| 633 | unit % CY_MAX_PORTS); | |
| ed81d395 MD |
634 | |
| 635 | /* for(cdu...), for(cyu...) terminating blocks */ | |
| 984263bc MD |
636 | } |
| 637 | } | |
| 638 | ||
| 639 | /* ensure an edge for the next interrupt */ | |
| 640 | cy_outb(cy_iobase, CY_CLEAR_INTR, cy_align, 0); | |
| 641 | ||
| 642 | return (adapter); | |
| 643 | } | |
| 644 | ||
| 645 | static int | |
| fef8985e | 646 | sioopen(struct dev_open_args *ap) |
| 984263bc | 647 | { |
| b13267a5 | 648 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc MD |
649 | struct com_s *com; |
| 650 | int error; | |
| 651 | int mynor; | |
| 984263bc MD |
652 | struct tty *tp; |
| 653 | int unit; | |
| 654 | ||
| 655 | mynor = minor(dev); | |
| 656 | unit = MINOR_TO_UNIT(mynor); | |
| 657 | if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL) | |
| 658 | return (ENXIO); | |
| 659 | if (mynor & CONTROL_MASK) | |
| 660 | return (0); | |
| 661 | #if 0 /* XXX */ | |
| 662 | tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]); | |
| 663 | #else | |
| 664 | tp = com->tp = &sio_tty[unit]; | |
| 665 | #endif | |
| 666 | dev->si_tty = tp; | |
| 8d77660e | 667 | crit_enter(); |
| 984263bc MD |
668 | /* |
| 669 | * We jump to this label after all non-interrupted sleeps to pick | |
| 670 | * up any changes of the device state. | |
| 671 | */ | |
| 672 | open_top: | |
| 673 | while (com->state & CS_DTR_OFF) { | |
| 377d4740 | 674 | error = tsleep(&com->dtr_wait, PCATCH, "cydtr", 0); |
| 984263bc MD |
675 | if (error != 0) |
| 676 | goto out; | |
| 677 | } | |
| 678 | if (tp->t_state & TS_ISOPEN) { | |
| 679 | /* | |
| 680 | * The device is open, so everything has been initialized. | |
| 681 | * Handle conflicts. | |
| 682 | */ | |
| 683 | if (mynor & CALLOUT_MASK) { | |
| 684 | if (!com->active_out) { | |
| 685 | error = EBUSY; | |
| 686 | goto out; | |
| 687 | } | |
| 688 | } else { | |
| 689 | if (com->active_out) { | |
| fef8985e | 690 | if (ap->a_oflags & O_NONBLOCK) { |
| 984263bc MD |
691 | error = EBUSY; |
| 692 | goto out; | |
| 693 | } | |
| 694 | error = tsleep(&com->active_out, | |
| 377d4740 | 695 | PCATCH, "cybi", 0); |
| 984263bc MD |
696 | if (error != 0) |
| 697 | goto out; | |
| 698 | goto open_top; | |
| 699 | } | |
| 700 | } | |
| 701 | if (tp->t_state & TS_XCLUDE && | |
| 895c1f85 | 702 | priv_check_cred(ap->a_cred, PRIV_ROOT, 0)) { |
| 984263bc MD |
703 | error = EBUSY; |
| 704 | goto out; | |
| 705 | } | |
| 706 | } else { | |
| 707 | /* | |
| 708 | * The device isn't open, so there are no conflicts. | |
| 709 | * Initialize it. Initialization is done twice in many | |
| 710 | * cases: to preempt sleeping callin opens if we are | |
| 711 | * callout, and to complete a callin open after DCD rises. | |
| 712 | */ | |
| 713 | tp->t_oproc = comstart; | |
| 714 | tp->t_stop = comstop; | |
| 715 | tp->t_param = comparam; | |
| 716 | tp->t_dev = dev; | |
| 717 | tp->t_termios = mynor & CALLOUT_MASK | |
| 718 | ? com->it_out : com->it_in; | |
| 719 | ||
| 720 | /* Encode per-board unit in LIVR for access in intr routines. */ | |
| 721 | cd_setreg(com, CD1400_LIVR, | |
| 722 | (unit & CD1400_xIVR_CHAN) << CD1400_xIVR_CHAN_SHIFT); | |
| 723 | ||
| 724 | (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); | |
| 725 | #if 0 | |
| 726 | com->poll = com->no_irq; | |
| 727 | com->poll_output = com->loses_outints; | |
| 728 | #endif | |
| 729 | ++com->wopeners; | |
| 730 | error = comparam(tp, &tp->t_termios); | |
| 731 | --com->wopeners; | |
| 732 | if (error != 0) | |
| 733 | goto out; | |
| 734 | #if 0 | |
| 735 | if (com->hasfifo) { | |
| 736 | /* | |
| 737 | * (Re)enable and flush fifos. | |
| 738 | * | |
| 739 | * Certain SMC chips cause problems if the fifos | |
| 740 | * are enabled while input is ready. Turn off the | |
| 741 | * fifo if necessary to clear the input. We test | |
| 742 | * the input ready bit after enabling the fifos | |
| 743 | * since we've already enabled them in comparam() | |
| 744 | * and to handle races between enabling and fresh | |
| 745 | * input. | |
| 746 | */ | |
| 747 | while (TRUE) { | |
| 748 | outb(iobase + com_fifo, | |
| 749 | FIFO_RCV_RST | FIFO_XMT_RST | |
| 750 | | com->fifo_image); | |
| 751 | DELAY(100); | |
| 752 | if (!(inb(com->line_status_port) & LSR_RXRDY)) | |
| 753 | break; | |
| 754 | outb(iobase + com_fifo, 0); | |
| 755 | DELAY(100); | |
| 756 | (void) inb(com->data_port); | |
| 757 | } | |
| 758 | } | |
| 759 | ||
| 760 | disable_intr(); | |
| 761 | (void) inb(com->line_status_port); | |
| 762 | (void) inb(com->data_port); | |
| 763 | com->prev_modem_status = com->last_modem_status | |
| 764 | = inb(com->modem_status_port); | |
| 765 | outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS | |
| 766 | | IER_EMSC); | |
| 767 | enable_intr(); | |
| 768 | #else /* !0 */ | |
| 769 | /* | |
| 770 | * Flush fifos. This requires a full channel reset which | |
| 771 | * also disables the transmitter and receiver. Recover | |
| 772 | * from this. | |
| 773 | */ | |
| 774 | cd1400_channel_cmd(com, | |
| 775 | CD1400_CCR_CMDRESET | CD1400_CCR_CHANRESET); | |
| 776 | cd1400_channel_cmd(com, com->channel_control); | |
| 777 | ||
| 778 | disable_intr(); | |
| 779 | com->prev_modem_status = com->last_modem_status | |
| 780 | = cd_getreg(com, CD1400_MSVR2); | |
| 781 | cd_setreg(com, CD1400_SRER, | |
| 782 | com->intr_enable | |
| 783 | = CD1400_SRER_MDMCH | CD1400_SRER_RXDATA); | |
| 784 | enable_intr(); | |
| 785 | #endif /* 0 */ | |
| 786 | /* | |
| 787 | * Handle initial DCD. Callout devices get a fake initial | |
| 788 | * DCD (trapdoor DCD). If we are callout, then any sleeping | |
| 789 | * callin opens get woken up and resume sleeping on "cybi" | |
| 790 | * instead of "cydcd". | |
| 791 | */ | |
| 792 | /* | |
| 793 | * XXX `mynor & CALLOUT_MASK' should be | |
| 794 | * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where | |
| 795 | * TRAPDOOR_CARRIER is the default initial state for callout | |
| 796 | * devices and SOFT_CARRIER is like CLOCAL except it hides | |
| 797 | * the true carrier. | |
| 798 | */ | |
| 799 | if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) | |
| 800 | (*linesw[tp->t_line].l_modem)(tp, 1); | |
| 801 | } | |
| 802 | /* | |
| 803 | * Wait for DCD if necessary. | |
| 804 | */ | |
| 805 | if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) | |
| fef8985e | 806 | && !(tp->t_cflag & CLOCAL) && !(ap->a_oflags & O_NONBLOCK)) { |
| 984263bc | 807 | ++com->wopeners; |
| 377d4740 | 808 | error = tsleep(TSA_CARR_ON(tp), PCATCH, "cydcd", 0); |
| 984263bc MD |
809 | --com->wopeners; |
| 810 | if (error != 0) | |
| 811 | goto out; | |
| 812 | goto open_top; | |
| 813 | } | |
| 814 | error = (*linesw[tp->t_line].l_open)(dev, tp); | |
| 815 | disc_optim(tp, &tp->t_termios, com); | |
| 816 | if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) | |
| 817 | com->active_out = TRUE; | |
| 818 | siosettimeout(); | |
| 819 | out: | |
| 8d77660e | 820 | crit_exit(); |
| 984263bc MD |
821 | if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) |
| 822 | comhardclose(com); | |
| 823 | return (error); | |
| 824 | } | |
| 825 | ||
| 826 | static int | |
| fef8985e | 827 | sioclose(struct dev_close_args *ap) |
| 984263bc | 828 | { |
| b13267a5 | 829 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc MD |
830 | struct com_s *com; |
| 831 | int mynor; | |
| 984263bc MD |
832 | struct tty *tp; |
| 833 | ||
| 834 | mynor = minor(dev); | |
| 835 | if (mynor & CONTROL_MASK) | |
| 836 | return (0); | |
| 837 | com = com_addr(MINOR_TO_UNIT(mynor)); | |
| 838 | tp = com->tp; | |
| 8d77660e | 839 | crit_enter(); |
| 984263bc | 840 | cd_etc(com, CD1400_ETC_STOPBREAK); |
| fef8985e | 841 | (*linesw[tp->t_line].l_close)(tp, ap->a_fflag); |
| 984263bc MD |
842 | disc_optim(tp, &tp->t_termios, com); |
| 843 | comstop(tp, FREAD | FWRITE); | |
| 844 | comhardclose(com); | |
| 845 | ttyclose(tp); | |
| 846 | siosettimeout(); | |
| 8d77660e | 847 | crit_exit(); |
| 984263bc MD |
848 | #ifdef broken /* session holds a ref to the tty; can't deallocate */ |
| 849 | ttyfree(tp); | |
| 850 | com->tp = sio_tty[unit] = NULL; | |
| 851 | #endif | |
| 852 | return (0); | |
| 853 | } | |
| 854 | ||
| 855 | static void | |
| ce63e0fa | 856 | comhardclose(struct com_s *com) |
| 984263bc MD |
857 | { |
| 858 | cy_addr iobase; | |
| 984263bc MD |
859 | struct tty *tp; |
| 860 | int unit; | |
| 861 | ||
| 862 | unit = com->unit; | |
| 863 | iobase = com->iobase; | |
| 8d77660e | 864 | crit_enter(); |
| 984263bc MD |
865 | #if 0 |
| 866 | com->poll = FALSE; | |
| 867 | com->poll_output = FALSE; | |
| 868 | #endif | |
| 869 | com->do_timestamp = 0; | |
| 870 | #if 0 | |
| 871 | outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); | |
| 872 | #else | |
| 873 | /* XXX */ | |
| 874 | disable_intr(); | |
| 875 | com->etc = ETC_NONE; | |
| 876 | cd_setreg(com, CD1400_COR2, com->cor[1] &= ~CD1400_COR2_ETC); | |
| 877 | enable_intr(); | |
| 878 | cd1400_channel_cmd(com, CD1400_CCR_CMDRESET | CD1400_CCR_FTF); | |
| 879 | #endif | |
| 880 | ||
| 881 | { | |
| 882 | #if 0 | |
| 883 | outb(iobase + com_ier, 0); | |
| 884 | #else | |
| 885 | disable_intr(); | |
| 886 | cd_setreg(com, CD1400_SRER, com->intr_enable = 0); | |
| 887 | enable_intr(); | |
| 888 | #endif | |
| 889 | tp = com->tp; | |
| 890 | if ((tp->t_cflag & HUPCL) | |
| 891 | /* | |
| 892 | * XXX we will miss any carrier drop between here and the | |
| 893 | * next open. Perhaps we should watch DCD even when the | |
| 894 | * port is closed; it is not sufficient to check it at | |
| 895 | * the next open because it might go up and down while | |
| 896 | * we're not watching. | |
| 897 | */ | |
| 898 | || (!com->active_out | |
| 899 | && !(com->prev_modem_status & MSR_DCD) | |
| 900 | && !(com->it_in.c_cflag & CLOCAL)) | |
| 901 | || !(tp->t_state & TS_ISOPEN)) { | |
| 902 | (void)commctl(com, TIOCM_DTR, DMBIC); | |
| 903 | ||
| 904 | /* Disable receiver (leave transmitter enabled). */ | |
| 905 | com->channel_control = CD1400_CCR_CMDCHANCTL | |
| 906 | | CD1400_CCR_XMTEN | |
| 907 | | CD1400_CCR_RCVDIS; | |
| 908 | cd1400_channel_cmd(com, com->channel_control); | |
| 909 | ||
| 910 | if (com->dtr_wait != 0 && !(com->state & CS_DTR_OFF)) { | |
| ed81d395 MD |
911 | callout_reset(&com->dtr_ch, com->dtr_wait, |
| 912 | siodtrwakeup, com); | |
| 984263bc MD |
913 | com->state |= CS_DTR_OFF; |
| 914 | } | |
| 915 | } | |
| 916 | } | |
| 917 | #if 0 | |
| 918 | if (com->hasfifo) { | |
| 919 | /* | |
| 920 | * Disable fifos so that they are off after controlled | |
| 921 | * reboots. Some BIOSes fail to detect 16550s when the | |
| 922 | * fifos are enabled. | |
| 923 | */ | |
| 924 | outb(iobase + com_fifo, 0); | |
| 925 | } | |
| 926 | #endif | |
| 927 | com->active_out = FALSE; | |
| 928 | wakeup(&com->active_out); | |
| 929 | wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ | |
| 8d77660e | 930 | crit_exit(); |
| 984263bc MD |
931 | } |
| 932 | ||
| 933 | static int | |
| fef8985e | 934 | siowrite(struct dev_write_args *ap) |
| 984263bc | 935 | { |
| b13267a5 | 936 | cdev_t dev = ap->a_head.a_dev; |
| fef8985e | 937 | struct uio *uio = ap->a_uio; |
| 984263bc MD |
938 | int mynor; |
| 939 | struct tty *tp; | |
| 940 | int unit; | |
| 941 | ||
| 942 | mynor = minor(dev); | |
| 943 | if (mynor & CONTROL_MASK) | |
| 944 | return (ENODEV); | |
| 945 | ||
| 946 | unit = MINOR_TO_UNIT(mynor); | |
| 947 | tp = com_addr(unit)->tp; | |
| 948 | /* | |
| 949 | * (XXX) We disallow virtual consoles if the physical console is | |
| 950 | * a serial port. This is in case there is a display attached that | |
| 951 | * is not the console. In that situation we don't need/want the X | |
| 952 | * server taking over the console. | |
| 953 | */ | |
| 954 | if (constty != NULL && unit == comconsole) | |
| 955 | constty = NULL; | |
| 956 | #ifdef Smarts | |
| 957 | /* XXX duplicate ttwrite(), but without so much output processing on | |
| 958 | * CR & LF chars. Hardly worth the effort, given that high-throughput | |
| 959 | * sessions are raw anyhow. | |
| 960 | */ | |
| 961 | #else | |
| fef8985e | 962 | return ((*linesw[tp->t_line].l_write)(tp, uio, ap->a_ioflag)); |
| 984263bc MD |
963 | #endif |
| 964 | } | |
| 965 | ||
| 966 | static void | |
| ce63e0fa | 967 | siodtrwakeup(void *chan) |
| 984263bc MD |
968 | { |
| 969 | struct com_s *com; | |
| 970 | ||
| 971 | com = (struct com_s *)chan; | |
| 972 | com->state &= ~CS_DTR_OFF; | |
| 973 | wakeup(&com->dtr_wait); | |
| 974 | } | |
| 975 | ||
| 976 | static void | |
| ce63e0fa | 977 | sioinput(struct com_s *com) |
| 984263bc MD |
978 | { |
| 979 | u_char *buf; | |
| 980 | int incc; | |
| 981 | u_char line_status; | |
| 982 | int recv_data; | |
| 983 | struct tty *tp; | |
| 984 | ||
| 985 | buf = com->ibuf; | |
| 986 | tp = com->tp; | |
| 987 | if (!(tp->t_state & TS_ISOPEN)) { | |
| 988 | com_events -= (com->iptr - com->ibuf); | |
| 989 | com->iptr = com->ibuf; | |
| 990 | return; | |
| 991 | } | |
| 992 | if (tp->t_state & TS_CAN_BYPASS_L_RINT) { | |
| 993 | /* | |
| 994 | * Avoid the grotesquely inefficient lineswitch routine | |
| 995 | * (ttyinput) in "raw" mode. It usually takes about 450 | |
| 996 | * instructions (that's without canonical processing or echo!). | |
| 997 | * slinput is reasonably fast (usually 40 instructions plus | |
| 998 | * call overhead). | |
| 999 | */ | |
| 1000 | do { | |
| 1001 | enable_intr(); | |
| 1002 | incc = com->iptr - buf; | |
| 1003 | if (tp->t_rawq.c_cc + incc > tp->t_ihiwat | |
| 1004 | && (com->state & CS_RTS_IFLOW | |
| 1005 | || tp->t_iflag & IXOFF) | |
| 1006 | && !(tp->t_state & TS_TBLOCK)) | |
| 1007 | ttyblock(tp); | |
| 1008 | com->delta_error_counts[CE_TTY_BUF_OVERFLOW] | |
| 1009 | += b_to_q((char *)buf, incc, &tp->t_rawq); | |
| 1010 | buf += incc; | |
| 1011 | tk_nin += incc; | |
| 1012 | tk_rawcc += incc; | |
| 1013 | tp->t_rawcc += incc; | |
| 1014 | ttwakeup(tp); | |
| 1015 | if (tp->t_state & TS_TTSTOP | |
| 1016 | && (tp->t_iflag & IXANY | |
| 1017 | || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { | |
| 1018 | tp->t_state &= ~TS_TTSTOP; | |
| 1019 | tp->t_lflag &= ~FLUSHO; | |
| 1020 | comstart(tp); | |
| 1021 | } | |
| 1022 | disable_intr(); | |
| 1023 | } while (buf < com->iptr); | |
| 1024 | } else { | |
| 1025 | do { | |
| 1026 | enable_intr(); | |
| 1027 | line_status = buf[com->ierroff]; | |
| 1028 | recv_data = *buf++; | |
| 1029 | if (line_status | |
| 1030 | & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { | |
| 1031 | if (line_status & LSR_BI) | |
| 1032 | recv_data |= TTY_BI; | |
| 1033 | if (line_status & LSR_FE) | |
| 1034 | recv_data |= TTY_FE; | |
| 1035 | if (line_status & LSR_OE) | |
| 1036 | recv_data |= TTY_OE; | |
| 1037 | if (line_status & LSR_PE) | |
| 1038 | recv_data |= TTY_PE; | |
| 1039 | } | |
| 1040 | (*linesw[tp->t_line].l_rint)(recv_data, tp); | |
| 1041 | disable_intr(); | |
| 1042 | } while (buf < com->iptr); | |
| 1043 | } | |
| 1044 | com_events -= (com->iptr - com->ibuf); | |
| 1045 | com->iptr = com->ibuf; | |
| 1046 | ||
| 1047 | /* | |
| 1048 | * There is now room for another low-level buffer full of input, | |
| 1049 | * so enable RTS if it is now disabled and there is room in the | |
| 1050 | * high-level buffer. | |
| 1051 | */ | |
| 1052 | if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & com->mcr_rts) && | |
| 1053 | !(tp->t_state & TS_TBLOCK)) | |
| 1054 | #if 0 | |
| 1055 | outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); | |
| 1056 | #else | |
| 1057 | cd_setreg(com, com->mcr_rts_reg, | |
| 1058 | com->mcr_image |= com->mcr_rts); | |
| 1059 | #endif | |
| 1060 | } | |
| 1061 | ||
| 1062 | void | |
| 477d3c1c | 1063 | siointr(void *arg) |
| 984263bc | 1064 | { |
| 477d3c1c | 1065 | int unit = (int)arg; |
| 984263bc MD |
1066 | int baseu; |
| 1067 | int cy_align; | |
| 1068 | cy_addr cy_iobase; | |
| 1069 | int cyu; | |
| 1070 | cy_addr iobase; | |
| 1071 | u_char status; | |
| 1072 | ||
| 7b95be2a | 1073 | com_lock(); /* XXX could this be placed down lower in the loop? */ |
| 984263bc MD |
1074 | |
| 1075 | baseu = unit * CY_MAX_PORTS; | |
| 1076 | cy_align = com_addr(baseu)->cy_align; | |
| 1077 | cy_iobase = com_addr(baseu)->cy_iobase; | |
| 1078 | ||
| 1079 | /* check each CD1400 in turn */ | |
| 1080 | for (cyu = 0; cyu < cy_nr_cd1400s[unit]; ++cyu) { | |
| 1081 | iobase = (cy_addr) (cy_iobase | |
| 1082 | + (cy_chip_offset[cyu] << cy_align)); | |
| 1083 | /* poll to see if it has any work */ | |
| 1084 | status = cd_inb(iobase, CD1400_SVRR, cy_align); | |
| 1085 | if (status == 0) | |
| 1086 | continue; | |
| 1087 | #ifdef CyDebug | |
| 1088 | ++cy_svrr_probes; | |
| 1089 | #endif | |
| 1090 | /* service requests as appropriate, giving priority to RX */ | |
| 1091 | if (status & CD1400_SVRR_RXRDY) { | |
| 1092 | struct com_s *com; | |
| 1093 | u_int count; | |
| 1094 | u_char *ioptr; | |
| 1095 | u_char line_status; | |
| 1096 | u_char recv_data; | |
| 1097 | u_char serv_type; | |
| 1098 | #ifdef PollMode | |
| 1099 | u_char save_rir; | |
| 1100 | #endif | |
| 1101 | ||
| 1102 | #ifdef PollMode | |
| 1103 | save_rir = cd_inb(iobase, CD1400_RIR, cy_align); | |
| 1104 | ||
| 1105 | /* enter rx service */ | |
| 1106 | cd_outb(iobase, CD1400_CAR, cy_align, save_rir); | |
| 1107 | com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS)->car | |
| 1108 | = save_rir & CD1400_CAR_CHAN; | |
| 1109 | ||
| 1110 | serv_type = cd_inb(iobase, CD1400_RIVR, cy_align); | |
| 1111 | com = com_addr(baseu | |
| 1112 | + ((serv_type >> CD1400_xIVR_CHAN_SHIFT) | |
| 1113 | & CD1400_xIVR_CHAN)); | |
| 1114 | #else | |
| 1115 | /* ack receive service */ | |
| 1116 | serv_type = cy_inb(iobase, CY8_SVCACKR, cy_align); | |
| 1117 | ||
| 1118 | com = com_addr(baseu + | |
| 1119 | + ((serv_type >> CD1400_xIVR_CHAN_SHIFT) | |
| 1120 | & CD1400_xIVR_CHAN)); | |
| 1121 | #endif | |
| 1122 | ||
| 1123 | if (serv_type & CD1400_RIVR_EXCEPTION) { | |
| 1124 | ++com->recv_exception; | |
| 1125 | line_status = cd_inb(iobase, CD1400_RDSR, cy_align); | |
| 1126 | /* break/unnattached error bits or real input? */ | |
| 1127 | recv_data = cd_inb(iobase, CD1400_RDSR, cy_align); | |
| 1128 | #ifndef SOFT_HOTCHAR | |
| 1129 | if (line_status & CD1400_RDSR_SPECIAL | |
| 1130 | && com->hotchar != 0) | |
| 1131 | setsofttty(); | |
| 1132 | #endif | |
| 1133 | #if 1 /* XXX "intelligent" PFO error handling would break O error handling */ | |
| 1134 | if (line_status & (LSR_PE|LSR_FE|LSR_BI)) { | |
| 1135 | /* | |
| 1136 | Don't store PE if IGNPAR and BI if IGNBRK, | |
| 1137 | this hack allows "raw" tty optimization | |
| 1138 | works even if IGN* is set. | |
| 1139 | */ | |
| 1140 | if ( com->tp == NULL | |
| 1141 | || !(com->tp->t_state & TS_ISOPEN) | |
| 1142 | || ((line_status & (LSR_PE|LSR_FE)) | |
| 1143 | && (com->tp->t_iflag & IGNPAR)) | |
| 1144 | || ((line_status & LSR_BI) | |
| 1145 | && (com->tp->t_iflag & IGNBRK))) | |
| 1146 | goto cont; | |
| 1147 | if ( (line_status & (LSR_PE|LSR_FE)) | |
| 1148 | && (com->tp->t_state & TS_CAN_BYPASS_L_RINT) | |
| 1149 | && ((line_status & LSR_FE) | |
| 1150 | || ((line_status & LSR_PE) | |
| 1151 | && (com->tp->t_iflag & INPCK)))) | |
| 1152 | recv_data = 0; | |
| 1153 | } | |
| 1154 | #endif /* 1 */ | |
| 1155 | ++com->bytes_in; | |
| 1156 | #ifdef SOFT_HOTCHAR | |
| 1157 | if (com->hotchar != 0 && recv_data == com->hotchar) | |
| 1158 | setsofttty(); | |
| 1159 | #endif | |
| 1160 | ioptr = com->iptr; | |
| 1161 | if (ioptr >= com->ibufend) | |
| 1162 | CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); | |
| 1163 | else { | |
| 1164 | if (com->do_timestamp) | |
| 1165 | microtime(&com->timestamp); | |
| 1166 | ++com_events; | |
| 1167 | ioptr[0] = recv_data; | |
| 1168 | ioptr[com->ierroff] = line_status; | |
| 1169 | com->iptr = ++ioptr; | |
| 1170 | if (ioptr == com->ihighwater | |
| 1171 | && com->state & CS_RTS_IFLOW) | |
| 1172 | #if 0 | |
| 1173 | outb(com->modem_ctl_port, | |
| 1174 | com->mcr_image &= ~MCR_RTS); | |
| 1175 | #else | |
| 1176 | cd_outb(iobase, com->mcr_rts_reg, | |
| 1177 | cy_align, | |
| 1178 | com->mcr_image &= | |
| 1179 | ~com->mcr_rts); | |
| 1180 | #endif | |
| 1181 | if (line_status & LSR_OE) | |
| 1182 | CE_RECORD(com, CE_OVERRUN); | |
| 1183 | } | |
| 1184 | goto cont; | |
| 1185 | } else { | |
| 1186 | int ifree; | |
| 1187 | ||
| 1188 | count = cd_inb(iobase, CD1400_RDCR, cy_align); | |
| 1189 | if (!count) | |
| 1190 | goto cont; | |
| 1191 | com->bytes_in += count; | |
| 1192 | ioptr = com->iptr; | |
| 1193 | ifree = com->ibufend - ioptr; | |
| 1194 | if (count > ifree) { | |
| 1195 | count -= ifree; | |
| 1196 | com_events += ifree; | |
| 1197 | if (ifree != 0) { | |
| 1198 | if (com->do_timestamp) | |
| 1199 | microtime(&com->timestamp); | |
| 1200 | do { | |
| 1201 | recv_data = cd_inb(iobase, | |
| 1202 | CD1400_RDSR, | |
| 1203 | cy_align); | |
| 1204 | #ifdef SOFT_HOTCHAR | |
| 1205 | if (com->hotchar != 0 | |
| 1206 | && recv_data | |
| 1207 | == com->hotchar) | |
| 1208 | setsofttty(); | |
| 1209 | #endif | |
| 1210 | ioptr[0] = recv_data; | |
| 1211 | ioptr[com->ierroff] = 0; | |
| 1212 | ++ioptr; | |
| 1213 | } while (--ifree != 0); | |
| 1214 | } | |
| 1215 | com->delta_error_counts | |
| 1216 | [CE_INTERRUPT_BUF_OVERFLOW] += count; | |
| 1217 | do { | |
| 1218 | recv_data = cd_inb(iobase, CD1400_RDSR, | |
| 1219 | cy_align); | |
| 1220 | #ifdef SOFT_HOTCHAR | |
| 1221 | if (com->hotchar != 0 | |
| 1222 | && recv_data == com->hotchar) | |
| 1223 | setsofttty(); | |
| 1224 | #endif | |
| 1225 | } while (--count != 0); | |
| 1226 | } else { | |
| 1227 | if (com->do_timestamp) | |
| 1228 | microtime(&com->timestamp); | |
| 1229 | if (ioptr <= com->ihighwater | |
| 1230 | && ioptr + count > com->ihighwater | |
| 1231 | && com->state & CS_RTS_IFLOW) | |
| 1232 | #if 0 | |
| 1233 | outb(com->modem_ctl_port, | |
| 1234 | com->mcr_image &= ~MCR_RTS); | |
| 1235 | #else | |
| 1236 | cd_outb(iobase, com->mcr_rts_reg, | |
| 1237 | cy_align, | |
| 1238 | com->mcr_image | |
| 1239 | &= ~com->mcr_rts); | |
| 1240 | #endif | |
| 1241 | com_events += count; | |
| 1242 | do { | |
| 1243 | recv_data = cd_inb(iobase, CD1400_RDSR, | |
| 1244 | cy_align); | |
| 1245 | #ifdef SOFT_HOTCHAR | |
| 1246 | if (com->hotchar != 0 | |
| 1247 | && recv_data == com->hotchar) | |
| 1248 | setsofttty(); | |
| 1249 | #endif | |
| 1250 | ioptr[0] = recv_data; | |
| 1251 | ioptr[com->ierroff] = 0; | |
| 1252 | ++ioptr; | |
| 1253 | } while (--count != 0); | |
| 1254 | } | |
| 1255 | com->iptr = ioptr; | |
| 1256 | } | |
| 1257 | cont: | |
| 1258 | ||
| 1259 | /* terminate service context */ | |
| 1260 | #ifdef PollMode | |
| 1261 | cd_outb(iobase, CD1400_RIR, cy_align, | |
| 1262 | save_rir | |
| 1263 | & ~(CD1400_RIR_RDIREQ | CD1400_RIR_RBUSY)); | |
| 1264 | #else | |
| 1265 | cd_outb(iobase, CD1400_EOSRR, cy_align, 0); | |
| 1266 | #endif | |
| 1267 | } | |
| 1268 | if (status & CD1400_SVRR_MDMCH) { | |
| 1269 | struct com_s *com; | |
| 1270 | u_char modem_status; | |
| 1271 | #ifdef PollMode | |
| 1272 | u_char save_mir; | |
| 1273 | #else | |
| 1274 | u_char vector; | |
| 1275 | #endif | |
| 1276 | ||
| 1277 | #ifdef PollMode | |
| 1278 | save_mir = cd_inb(iobase, CD1400_MIR, cy_align); | |
| 1279 | ||
| 1280 | /* enter modem service */ | |
| 1281 | cd_outb(iobase, CD1400_CAR, cy_align, save_mir); | |
| 1282 | com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS)->car | |
| 1283 | = save_mir & CD1400_CAR_CHAN; | |
| 1284 | ||
| 1285 | com = com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS | |
| 1286 | + (save_mir & CD1400_MIR_CHAN)); | |
| 1287 | #else | |
| 1288 | /* ack modem service */ | |
| 1289 | vector = cy_inb(iobase, CY8_SVCACKM, cy_align); | |
| 1290 | ||
| 1291 | com = com_addr(baseu | |
| 1292 | + ((vector >> CD1400_xIVR_CHAN_SHIFT) | |
| 1293 | & CD1400_xIVR_CHAN)); | |
| 1294 | #endif | |
| 1295 | ++com->mdm; | |
| 1296 | modem_status = cd_inb(iobase, CD1400_MSVR2, cy_align); | |
| 1297 | if (modem_status != com->last_modem_status) { | |
| 1298 | if (com->do_dcd_timestamp | |
| 1299 | && !(com->last_modem_status & MSR_DCD) | |
| 1300 | && modem_status & MSR_DCD) | |
| 1301 | microtime(&com->dcd_timestamp); | |
| 1302 | ||
| 1303 | /* | |
| 1304 | * Schedule high level to handle DCD changes. Note | |
| 1305 | * that we don't use the delta bits anywhere. Some | |
| 1306 | * UARTs mess them up, and it's easy to remember the | |
| 1307 | * previous bits and calculate the delta. | |
| 1308 | */ | |
| 1309 | com->last_modem_status = modem_status; | |
| 1310 | if (!(com->state & CS_CHECKMSR)) { | |
| 1311 | com_events += LOTS_OF_EVENTS; | |
| 1312 | com->state |= CS_CHECKMSR; | |
| 1313 | setsofttty(); | |
| 1314 | } | |
| 1315 | ||
| 1316 | #ifdef SOFT_CTS_OFLOW | |
| 1317 | /* handle CTS change immediately for crisp flow ctl */ | |
| 1318 | if (com->state & CS_CTS_OFLOW) { | |
| 1319 | if (modem_status & MSR_CTS) { | |
| 1320 | com->state |= CS_ODEVREADY; | |
| 1321 | if (com->state >= (CS_BUSY | CS_TTGO | |
| 1322 | | CS_ODEVREADY) | |
| 1323 | && !(com->intr_enable | |
| 1324 | & CD1400_SRER_TXRDY)) | |
| 1325 | cd_outb(iobase, CD1400_SRER, | |
| 1326 | cy_align, | |
| 1327 | com->intr_enable | |
| 1328 | = com->intr_enable | |
| 1329 | & ~CD1400_SRER_TXMPTY | |
| 1330 | | CD1400_SRER_TXRDY); | |
| 1331 | } else { | |
| 1332 | com->state &= ~CS_ODEVREADY; | |
| 1333 | if (com->intr_enable | |
| 1334 | & CD1400_SRER_TXRDY) | |
| 1335 | cd_outb(iobase, CD1400_SRER, | |
| 1336 | cy_align, | |
| 1337 | com->intr_enable | |
| 1338 | = com->intr_enable | |
| 1339 | & ~CD1400_SRER_TXRDY | |
| 1340 | | CD1400_SRER_TXMPTY); | |
| 1341 | } | |
| 1342 | } | |
| 1343 | #endif | |
| 1344 | } | |
| 1345 | ||
| 1346 | /* terminate service context */ | |
| 1347 | #ifdef PollMode | |
| 1348 | cd_outb(iobase, CD1400_MIR, cy_align, | |
| 1349 | save_mir | |
| 1350 | & ~(CD1400_MIR_RDIREQ | CD1400_MIR_RBUSY)); | |
| 1351 | #else | |
| 1352 | cd_outb(iobase, CD1400_EOSRR, cy_align, 0); | |
| 1353 | #endif | |
| 1354 | } | |
| 1355 | if (status & CD1400_SVRR_TXRDY) { | |
| 1356 | struct com_s *com; | |
| 1357 | #ifdef PollMode | |
| 1358 | u_char save_tir; | |
| 1359 | #else | |
| 1360 | u_char vector; | |
| 1361 | #endif | |
| 1362 | ||
| 1363 | #ifdef PollMode | |
| 1364 | save_tir = cd_inb(iobase, CD1400_TIR, cy_align); | |
| 1365 | ||
| 1366 | /* enter tx service */ | |
| 1367 | cd_outb(iobase, CD1400_CAR, cy_align, save_tir); | |
| 1368 | com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS)->car | |
| 1369 | = save_tir & CD1400_CAR_CHAN; | |
| 1370 | ||
| 1371 | com = com_addr(baseu | |
| 1372 | + cyu * CD1400_NO_OF_CHANNELS | |
| 1373 | + (save_tir & CD1400_TIR_CHAN)); | |
| 1374 | #else | |
| 1375 | /* ack transmit service */ | |
| 1376 | vector = cy_inb(iobase, CY8_SVCACKT, cy_align); | |
| 1377 | ||
| 1378 | com = com_addr(baseu | |
| 1379 | + ((vector >> CD1400_xIVR_CHAN_SHIFT) | |
| 1380 | & CD1400_xIVR_CHAN)); | |
| 1381 | #endif | |
| 1382 | ||
| 1383 | if (com->etc != ETC_NONE) { | |
| 1384 | if (com->intr_enable & CD1400_SRER_TXRDY) { | |
| 1385 | /* | |
| 1386 | * Here due to sloppy SRER_TXRDY | |
| 1387 | * enabling. Ignore. Come back when | |
| 1388 | * tx is empty. | |
| 1389 | */ | |
| 1390 | cd_outb(iobase, CD1400_SRER, cy_align, | |
| 1391 | com->intr_enable | |
| 1392 | = (com->intr_enable | |
| 1393 | & ~CD1400_SRER_TXRDY) | |
| 1394 | | CD1400_SRER_TXMPTY); | |
| 1395 | goto terminate_tx_service; | |
| 1396 | } | |
| 1397 | switch (com->etc) { | |
| 1398 | case CD1400_ETC_SENDBREAK: | |
| 1399 | case CD1400_ETC_STOPBREAK: | |
| 1400 | /* | |
| 1401 | * Start the command. Come back on | |
| 1402 | * next tx empty interrupt, hopefully | |
| 1403 | * after command has been executed. | |
| 1404 | */ | |
| 1405 | cd_outb(iobase, CD1400_COR2, cy_align, | |
| 1406 | com->cor[1] |= CD1400_COR2_ETC); | |
| 1407 | cd_outb(iobase, CD1400_TDR, cy_align, | |
| 1408 | CD1400_ETC_CMD); | |
| 1409 | cd_outb(iobase, CD1400_TDR, cy_align, | |
| 1410 | com->etc); | |
| 1411 | if (com->etc == CD1400_ETC_SENDBREAK) | |
| 1412 | com->etc = ETC_BREAK_STARTING; | |
| 1413 | else | |
| 1414 | com->etc = ETC_BREAK_ENDING; | |
| 1415 | goto terminate_tx_service; | |
| 1416 | case ETC_BREAK_STARTING: | |
| 1417 | /* | |
| 1418 | * BREAK is now on. Continue with | |
| 1419 | * SRER_TXMPTY processing, hopefully | |
| 1420 | * don't come back. | |
| 1421 | */ | |
| 1422 | com->etc = ETC_BREAK_STARTED; | |
| 1423 | break; | |
| 1424 | case ETC_BREAK_STARTED: | |
| 1425 | /* | |
| 1426 | * Came back due to sloppy SRER_TXMPTY | |
| 1427 | * enabling. Hope again. | |
| 1428 | */ | |
| 1429 | break; | |
| 1430 | case ETC_BREAK_ENDING: | |
| 1431 | /* | |
| 1432 | * BREAK is now off. Continue with | |
| 1433 | * SRER_TXMPTY processing and don't | |
| 1434 | * come back. The SWI handler will | |
| 1435 | * restart tx interrupts if necessary. | |
| 1436 | */ | |
| 1437 | cd_outb(iobase, CD1400_COR2, cy_align, | |
| 1438 | com->cor[1] | |
| 1439 | &= ~CD1400_COR2_ETC); | |
| 1440 | com->etc = ETC_BREAK_ENDED; | |
| 1441 | if (!(com->state & CS_ODONE)) { | |
| 1442 | com_events += LOTS_OF_EVENTS; | |
| 1443 | com->state |= CS_ODONE; | |
| 1444 | setsofttty(); | |
| 1445 | } | |
| 1446 | break; | |
| 1447 | case ETC_BREAK_ENDED: | |
| 1448 | /* | |
| 1449 | * Shouldn't get here. Hope again. | |
| 1450 | */ | |
| 1451 | break; | |
| 1452 | } | |
| 1453 | } | |
| 1454 | if (com->intr_enable & CD1400_SRER_TXMPTY) { | |
| 1455 | if (!(com->extra_state & CSE_ODONE)) { | |
| 1456 | com_events += LOTS_OF_EVENTS; | |
| 1457 | com->extra_state |= CSE_ODONE; | |
| 1458 | setsofttty(); | |
| 1459 | } | |
| 1460 | cd_outb(iobase, CD1400_SRER, cy_align, | |
| 1461 | com->intr_enable | |
| 1462 | &= ~CD1400_SRER_TXMPTY); | |
| 1463 | goto terminate_tx_service; | |
| 1464 | } | |
| 1465 | if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { | |
| 1466 | u_char *ioptr; | |
| 1467 | u_int ocount; | |
| 1468 | ||
| 1469 | ioptr = com->obufq.l_head; | |
| 1470 | ocount = com->obufq.l_tail - ioptr; | |
| 1471 | if (ocount > CD1400_TX_FIFO_SIZE) | |
| 1472 | ocount = CD1400_TX_FIFO_SIZE; | |
| 1473 | com->bytes_out += ocount; | |
| 1474 | do | |
| 1475 | cd_outb(iobase, CD1400_TDR, cy_align, | |
| 1476 | *ioptr++); | |
| 1477 | while (--ocount != 0); | |
| 1478 | com->obufq.l_head = ioptr; | |
| 1479 | if (ioptr >= com->obufq.l_tail) { | |
| 1480 | struct lbq *qp; | |
| 1481 | ||
| 1482 | qp = com->obufq.l_next; | |
| 1483 | qp->l_queued = FALSE; | |
| 1484 | qp = qp->l_next; | |
| 1485 | if (qp != NULL) { | |
| 1486 | com->obufq.l_head = qp->l_head; | |
| 1487 | com->obufq.l_tail = qp->l_tail; | |
| 1488 | com->obufq.l_next = qp; | |
| 1489 | } else { | |
| 1490 | /* output just completed */ | |
| 1491 | com->state &= ~CS_BUSY; | |
| 1492 | ||
| 1493 | /* | |
| 1494 | * The setting of CSE_ODONE may be | |
| 1495 | * stale here. We currently only | |
| 1496 | * use it when CS_BUSY is set, and | |
| 1497 | * fixing it when we clear CS_BUSY | |
| 1498 | * is easiest. | |
| 1499 | */ | |
| 1500 | if (com->extra_state & CSE_ODONE) { | |
| 1501 | com_events -= LOTS_OF_EVENTS; | |
| 1502 | com->extra_state &= ~CSE_ODONE; | |
| 1503 | } | |
| 1504 | ||
| 1505 | cd_outb(iobase, CD1400_SRER, cy_align, | |
| 1506 | com->intr_enable | |
| 1507 | = (com->intr_enable | |
| 1508 | & ~CD1400_SRER_TXRDY) | |
| 1509 | | CD1400_SRER_TXMPTY); | |
| 1510 | } | |
| 1511 | if (!(com->state & CS_ODONE)) { | |
| 1512 | com_events += LOTS_OF_EVENTS; | |
| 1513 | com->state |= CS_ODONE; | |
| 1514 | ||
| 1515 | /* handle at high level ASAP */ | |
| 1516 | setsofttty(); | |
| 1517 | } | |
| 1518 | } | |
| 1519 | } | |
| 1520 | ||
| 1521 | /* terminate service context */ | |
| 1522 | terminate_tx_service: | |
| 1523 | #ifdef PollMode | |
| 1524 | cd_outb(iobase, CD1400_TIR, cy_align, | |
| 1525 | save_tir | |
| 1526 | & ~(CD1400_TIR_RDIREQ | CD1400_TIR_RBUSY)); | |
| 1527 | #else | |
| 1528 | cd_outb(iobase, CD1400_EOSRR, cy_align, 0); | |
| 1529 | #endif | |
| 1530 | } | |
| 1531 | } | |
| 1532 | ||
| 1533 | /* ensure an edge for the next interrupt */ | |
| 1534 | cy_outb(cy_iobase, CY_CLEAR_INTR, cy_align, 0); | |
| 1535 | ||
| 1536 | schedsofttty(); | |
| 1537 | ||
| 7b95be2a | 1538 | com_unlock(); |
| 984263bc MD |
1539 | } |
| 1540 | ||
| 1541 | #if 0 | |
| 1542 | static void | |
| ce63e0fa | 1543 | siointr1(struct com_s *com) |
| 984263bc MD |
1544 | { |
| 1545 | } | |
| 1546 | #endif | |
| 1547 | ||
| 1548 | static int | |
| fef8985e | 1549 | sioioctl(struct dev_ioctl_args *ap) |
| 984263bc | 1550 | { |
| b13267a5 | 1551 | cdev_t dev = ap->a_head.a_dev; |
| fef8985e MD |
1552 | caddr_t data = ap->a_data; |
| 1553 | u_long cmd = ap->a_cmd; | |
| 984263bc MD |
1554 | struct com_s *com; |
| 1555 | int error; | |
| 1556 | int mynor; | |
| 984263bc MD |
1557 | struct tty *tp; |
| 1558 | #if defined(COMPAT_43) || defined(COMPAT_SUNOS) | |
| 1559 | int oldcmd; | |
| 1560 | struct termios term; | |
| 1561 | #endif | |
| 1562 | ||
| 1563 | mynor = minor(dev); | |
| 1564 | com = com_addr(MINOR_TO_UNIT(mynor)); | |
| 1565 | if (mynor & CONTROL_MASK) { | |
| 1566 | struct termios *ct; | |
| 1567 | ||
| 1568 | switch (mynor & CONTROL_MASK) { | |
| 1569 | case CONTROL_INIT_STATE: | |
| 1570 | ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; | |
| 1571 | break; | |
| 1572 | case CONTROL_LOCK_STATE: | |
| 1573 | ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; | |
| 1574 | break; | |
| 1575 | default: | |
| 1576 | return (ENODEV); /* /dev/nodev */ | |
| 1577 | } | |
| 1578 | switch (cmd) { | |
| 1579 | case TIOCSETA: | |
| 895c1f85 | 1580 | error = priv_check_cred(ap->a_cred, PRIV_ROOT, 0); |
| 984263bc MD |
1581 | if (error != 0) |
| 1582 | return (error); | |
| 1583 | *ct = *(struct termios *)data; | |
| 1584 | return (0); | |
| 1585 | case TIOCGETA: | |
| 1586 | *(struct termios *)data = *ct; | |
| 1587 | return (0); | |
| 1588 | case TIOCGETD: | |
| 1589 | *(int *)data = TTYDISC; | |
| 1590 | return (0); | |
| 1591 | case TIOCGWINSZ: | |
| 1592 | bzero(data, sizeof(struct winsize)); | |
| 1593 | return (0); | |
| 1594 | default: | |
| 1595 | return (ENOTTY); | |
| 1596 | } | |
| 1597 | } | |
| 1598 | tp = com->tp; | |
| 1599 | #if defined(COMPAT_43) || defined(COMPAT_SUNOS) | |
| 1600 | term = tp->t_termios; | |
| 1601 | oldcmd = cmd; | |
| 1602 | error = ttsetcompat(tp, &cmd, data, &term); | |
| 1603 | if (error != 0) | |
| 1604 | return (error); | |
| 1605 | if (cmd != oldcmd) | |
| 1606 | data = (caddr_t)&term; | |
| 1607 | #endif | |
| 1608 | if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { | |
| 1609 | int cc; | |
| 1610 | struct termios *dt = (struct termios *)data; | |
| 1611 | struct termios *lt = mynor & CALLOUT_MASK | |
| 1612 | ? &com->lt_out : &com->lt_in; | |
| 1613 | ||
| 1614 | dt->c_iflag = (tp->t_iflag & lt->c_iflag) | |
| 1615 | | (dt->c_iflag & ~lt->c_iflag); | |
| 1616 | dt->c_oflag = (tp->t_oflag & lt->c_oflag) | |
| 1617 | | (dt->c_oflag & ~lt->c_oflag); | |
| 1618 | dt->c_cflag = (tp->t_cflag & lt->c_cflag) | |
| 1619 | | (dt->c_cflag & ~lt->c_cflag); | |
| 1620 | dt->c_lflag = (tp->t_lflag & lt->c_lflag) | |
| 1621 | | (dt->c_lflag & ~lt->c_lflag); | |
| 1622 | for (cc = 0; cc < NCCS; ++cc) | |
| 1623 | if (lt->c_cc[cc] != 0) | |
| 1624 | dt->c_cc[cc] = tp->t_cc[cc]; | |
| 1625 | if (lt->c_ispeed != 0) | |
| 1626 | dt->c_ispeed = tp->t_ispeed; | |
| 1627 | if (lt->c_ospeed != 0) | |
| 1628 | dt->c_ospeed = tp->t_ospeed; | |
| 1629 | } | |
| fef8985e MD |
1630 | error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, |
| 1631 | ap->a_fflag, ap->a_cred); | |
| 984263bc MD |
1632 | if (error != ENOIOCTL) |
| 1633 | return (error); | |
| 8d77660e | 1634 | crit_enter(); |
| fef8985e | 1635 | error = ttioctl(tp, cmd, data, ap->a_fflag); |
| 984263bc MD |
1636 | disc_optim(tp, &tp->t_termios, com); |
| 1637 | if (error != ENOIOCTL) { | |
| 8d77660e | 1638 | crit_exit(); |
| 984263bc MD |
1639 | return (error); |
| 1640 | } | |
| 1641 | switch (cmd) { | |
| 1642 | case TIOCSBRK: | |
| 1643 | #if 0 | |
| 1644 | outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); | |
| 1645 | #else | |
| 1646 | cd_etc(com, CD1400_ETC_SENDBREAK); | |
| 1647 | #endif | |
| 1648 | break; | |
| 1649 | case TIOCCBRK: | |
| 1650 | #if 0 | |
| 1651 | outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); | |
| 1652 | #else | |
| 1653 | cd_etc(com, CD1400_ETC_STOPBREAK); | |
| 1654 | #endif | |
| 1655 | break; | |
| 1656 | case TIOCSDTR: | |
| 1657 | (void)commctl(com, TIOCM_DTR, DMBIS); | |
| 1658 | break; | |
| 1659 | case TIOCCDTR: | |
| 1660 | (void)commctl(com, TIOCM_DTR, DMBIC); | |
| 1661 | break; | |
| 1662 | /* | |
| 1663 | * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set. The | |
| 1664 | * changes get undone on the next call to comparam(). | |
| 1665 | */ | |
| 1666 | case TIOCMSET: | |
| 1667 | (void)commctl(com, *(int *)data, DMSET); | |
| 1668 | break; | |
| 1669 | case TIOCMBIS: | |
| 1670 | (void)commctl(com, *(int *)data, DMBIS); | |
| 1671 | break; | |
| 1672 | case TIOCMBIC: | |
| 1673 | (void)commctl(com, *(int *)data, DMBIC); | |
| 1674 | break; | |
| 1675 | case TIOCMGET: | |
| 1676 | *(int *)data = commctl(com, 0, DMGET); | |
| 1677 | break; | |
| 1678 | case TIOCMSDTRWAIT: | |
| 1679 | /* must be root since the wait applies to following logins */ | |
| 895c1f85 | 1680 | error = priv_check_cred(ap->a_cred, PRIV_ROOT, 0); |
| 984263bc | 1681 | if (error != 0) { |
| 8d77660e | 1682 | crit_exit(); |
| 984263bc MD |
1683 | return (error); |
| 1684 | } | |
| 1685 | com->dtr_wait = *(int *)data * hz / 100; | |
| 1686 | break; | |
| 1687 | case TIOCMGDTRWAIT: | |
| 1688 | *(int *)data = com->dtr_wait * 100 / hz; | |
| 1689 | break; | |
| 1690 | case TIOCTIMESTAMP: | |
| 1691 | com->do_timestamp = TRUE; | |
| 1692 | *(struct timeval *)data = com->timestamp; | |
| 1693 | break; | |
| 1694 | case TIOCDCDTIMESTAMP: | |
| 1695 | com->do_dcd_timestamp = TRUE; | |
| 1696 | *(struct timeval *)data = com->dcd_timestamp; | |
| 1697 | break; | |
| 1698 | default: | |
| 8d77660e | 1699 | crit_exit(); |
| 984263bc MD |
1700 | return (ENOTTY); |
| 1701 | } | |
| 8d77660e | 1702 | crit_exit(); |
| 984263bc MD |
1703 | return (0); |
| 1704 | } | |
| 1705 | ||
| 1706 | static void | |
| 477d3c1c | 1707 | siopoll(void *data, void *frame) |
| 984263bc MD |
1708 | { |
| 1709 | int unit; | |
| 1710 | ||
| 1711 | #ifdef CyDebug | |
| 1712 | ++cy_timeouts; | |
| 1713 | #endif | |
| 1714 | if (com_events == 0) | |
| 1715 | return; | |
| 1716 | repeat: | |
| 1717 | for (unit = 0; unit < NSIO; ++unit) { | |
| 1718 | struct com_s *com; | |
| 1719 | int incc; | |
| 1720 | struct tty *tp; | |
| 1721 | ||
| 1722 | com = com_addr(unit); | |
| 1723 | if (com == NULL) | |
| 1724 | continue; | |
| 1725 | tp = com->tp; | |
| 1726 | if (tp == NULL) { | |
| 1727 | /* | |
| 1728 | * XXX forget any events related to closed devices | |
| 1729 | * (actually never opened devices) so that we don't | |
| 1730 | * loop. | |
| 1731 | */ | |
| 1732 | disable_intr(); | |
| 1733 | incc = com->iptr - com->ibuf; | |
| 1734 | com->iptr = com->ibuf; | |
| 1735 | if (com->state & CS_CHECKMSR) { | |
| 1736 | incc += LOTS_OF_EVENTS; | |
| 1737 | com->state &= ~CS_CHECKMSR; | |
| 1738 | } | |
| 1739 | com_events -= incc; | |
| 1740 | enable_intr(); | |
| 1741 | if (incc != 0) | |
| 1742 | log(LOG_DEBUG, | |
| 1743 | "sio%d: %d events for device with no tp\n", | |
| 1744 | unit, incc); | |
| 1745 | continue; | |
| 1746 | } | |
| 1747 | if (com->iptr != com->ibuf) { | |
| 1748 | disable_intr(); | |
| 1749 | sioinput(com); | |
| 1750 | enable_intr(); | |
| 1751 | } | |
| 1752 | if (com->state & CS_CHECKMSR) { | |
| 1753 | u_char delta_modem_status; | |
| 1754 | ||
| 1755 | disable_intr(); | |
| 1756 | delta_modem_status = com->last_modem_status | |
| 1757 | ^ com->prev_modem_status; | |
| 1758 | com->prev_modem_status = com->last_modem_status; | |
| 1759 | com_events -= LOTS_OF_EVENTS; | |
| 1760 | com->state &= ~CS_CHECKMSR; | |
| 1761 | enable_intr(); | |
| 1762 | if (delta_modem_status & MSR_DCD) | |
| 1763 | (*linesw[tp->t_line].l_modem) | |
| 1764 | (tp, com->prev_modem_status & MSR_DCD); | |
| 1765 | } | |
| 1766 | if (com->extra_state & CSE_ODONE) { | |
| 1767 | disable_intr(); | |
| 1768 | com_events -= LOTS_OF_EVENTS; | |
| 1769 | com->extra_state &= ~CSE_ODONE; | |
| 1770 | enable_intr(); | |
| 1771 | if (!(com->state & CS_BUSY)) { | |
| 1772 | tp->t_state &= ~TS_BUSY; | |
| 1773 | ttwwakeup(com->tp); | |
| 1774 | } | |
| 1775 | if (com->etc != ETC_NONE) { | |
| 1776 | if (com->etc == ETC_BREAK_ENDED) | |
| 1777 | com->etc = ETC_NONE; | |
| 1778 | wakeup(&com->etc); | |
| 1779 | } | |
| 1780 | } | |
| 1781 | if (com->state & CS_ODONE) { | |
| 1782 | disable_intr(); | |
| 1783 | com_events -= LOTS_OF_EVENTS; | |
| 1784 | com->state &= ~CS_ODONE; | |
| 1785 | enable_intr(); | |
| 1786 | (*linesw[tp->t_line].l_start)(tp); | |
| 1787 | } | |
| 1788 | if (com_events == 0) | |
| 1789 | break; | |
| 1790 | } | |
| 1791 | if (com_events >= LOTS_OF_EVENTS) | |
| 1792 | goto repeat; | |
| 1793 | } | |
| 1794 | ||
| 1795 | static int | |
| ce63e0fa | 1796 | comparam(struct tty *tp, struct termios *t) |
| 984263bc MD |
1797 | { |
| 1798 | int bits; | |
| 1799 | int cflag; | |
| 1800 | struct com_s *com; | |
| 1801 | u_char cor_change; | |
| 1802 | u_long cy_clock; | |
| 1803 | int idivisor; | |
| 1804 | int iflag; | |
| 1805 | int iprescaler; | |
| 1806 | int itimeout; | |
| 1807 | int odivisor; | |
| 1808 | int oprescaler; | |
| 1809 | u_char opt; | |
| 984263bc MD |
1810 | int unit; |
| 1811 | ||
| 1812 | /* do historical conversions */ | |
| 1813 | if (t->c_ispeed == 0) | |
| 1814 | t->c_ispeed = t->c_ospeed; | |
| 1815 | ||
| 1816 | unit = DEV_TO_UNIT(tp->t_dev); | |
| 1817 | com = com_addr(unit); | |
| 1818 | ||
| 1819 | /* check requested parameters */ | |
| 1820 | cy_clock = CY_CLOCK(com->gfrcr_image); | |
| 1821 | idivisor = comspeed(t->c_ispeed, cy_clock, &iprescaler); | |
| 1822 | if (idivisor < 0) | |
| 1823 | return (EINVAL); | |
| 1824 | odivisor = comspeed(t->c_ospeed, cy_clock, &oprescaler); | |
| 1825 | if (odivisor < 0) | |
| 1826 | return (EINVAL); | |
| 1827 | ||
| 1828 | /* parameters are OK, convert them to the com struct and the device */ | |
| 8d77660e | 1829 | crit_enter(); |
| 984263bc MD |
1830 | if (odivisor == 0) |
| 1831 | (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ | |
| 1832 | else | |
| 1833 | (void)commctl(com, TIOCM_DTR, DMBIS); | |
| 1834 | ||
| 1835 | /* | |
| 1836 | * This returns with interrupts disabled so that we can complete | |
| 1837 | * the speed change atomically. | |
| 1838 | */ | |
| 1839 | (void) siosetwater(com, t->c_ispeed); | |
| 1840 | ||
| 1841 | /* XXX we don't actually change the speed atomically. */ | |
| 1842 | enable_intr(); | |
| 1843 | ||
| 1844 | if (idivisor != 0) { | |
| 1845 | cd_setreg(com, CD1400_RBPR, idivisor); | |
| 1846 | cd_setreg(com, CD1400_RCOR, iprescaler); | |
| 1847 | } | |
| 1848 | if (odivisor != 0) { | |
| 1849 | cd_setreg(com, CD1400_TBPR, odivisor); | |
| 1850 | cd_setreg(com, CD1400_TCOR, oprescaler); | |
| 1851 | } | |
| 1852 | ||
| 1853 | /* | |
| 1854 | * channel control | |
| 1855 | * receiver enable | |
| 1856 | * transmitter enable (always set) | |
| 1857 | */ | |
| 1858 | cflag = t->c_cflag; | |
| 1859 | opt = CD1400_CCR_CMDCHANCTL | CD1400_CCR_XMTEN | |
| 1860 | | (cflag & CREAD ? CD1400_CCR_RCVEN : CD1400_CCR_RCVDIS); | |
| 1861 | if (opt != com->channel_control) { | |
| 1862 | com->channel_control = opt; | |
| 1863 | cd1400_channel_cmd(com, opt); | |
| 1864 | } | |
| 1865 | ||
| 1866 | #ifdef Smarts | |
| 1867 | /* set special chars */ | |
| 1868 | /* XXX if one is _POSIX_VDISABLE, can't use some others */ | |
| 1869 | if (t->c_cc[VSTOP] != _POSIX_VDISABLE) | |
| 1870 | cd_setreg(com, CD1400_SCHR1, t->c_cc[VSTOP]); | |
| 1871 | if (t->c_cc[VSTART] != _POSIX_VDISABLE) | |
| 1872 | cd_setreg(com, CD1400_SCHR2, t->c_cc[VSTART]); | |
| 1873 | if (t->c_cc[VINTR] != _POSIX_VDISABLE) | |
| 1874 | cd_setreg(com, CD1400_SCHR3, t->c_cc[VINTR]); | |
| 1875 | if (t->c_cc[VSUSP] != _POSIX_VDISABLE) | |
| 1876 | cd_setreg(com, CD1400_SCHR4, t->c_cc[VSUSP]); | |
| 1877 | #endif | |
| 1878 | ||
| 1879 | /* | |
| 1880 | * set channel option register 1 - | |
| 1881 | * parity mode | |
| 1882 | * stop bits | |
| 1883 | * char length | |
| 1884 | */ | |
| 1885 | opt = 0; | |
| 1886 | /* parity */ | |
| 1887 | if (cflag & PARENB) { | |
| 1888 | if (cflag & PARODD) | |
| 1889 | opt |= CD1400_COR1_PARODD; | |
| 1890 | opt |= CD1400_COR1_PARNORMAL; | |
| 1891 | } | |
| 1892 | iflag = t->c_iflag; | |
| 1893 | if (!(iflag & INPCK)) | |
| 1894 | opt |= CD1400_COR1_NOINPCK; | |
| 1895 | bits = 1 + 1; | |
| 1896 | /* stop bits */ | |
| 1897 | if (cflag & CSTOPB) { | |
| 1898 | ++bits; | |
| 1899 | opt |= CD1400_COR1_STOP2; | |
| 1900 | } | |
| 1901 | /* char length */ | |
| 1902 | switch (cflag & CSIZE) { | |
| 1903 | case CS5: | |
| 1904 | bits += 5; | |
| 1905 | opt |= CD1400_COR1_CS5; | |
| 1906 | break; | |
| 1907 | case CS6: | |
| 1908 | bits += 6; | |
| 1909 | opt |= CD1400_COR1_CS6; | |
| 1910 | break; | |
| 1911 | case CS7: | |
| 1912 | bits += 7; | |
| 1913 | opt |= CD1400_COR1_CS7; | |
| 1914 | break; | |
| 1915 | default: | |
| 1916 | bits += 8; | |
| 1917 | opt |= CD1400_COR1_CS8; | |
| 1918 | break; | |
| 1919 | } | |
| 1920 | cor_change = 0; | |
| 1921 | if (opt != com->cor[0]) { | |
| 1922 | cor_change |= CD1400_CCR_COR1; | |
| 1923 | cd_setreg(com, CD1400_COR1, com->cor[0] = opt); | |
| 1924 | } | |
| 1925 | ||
| 1926 | /* | |
| 1927 | * Set receive time-out period, normally to max(one char time, 5 ms). | |
| 1928 | */ | |
| 1929 | if (t->c_ispeed == 0) | |
| 1930 | itimeout = cd_getreg(com, CD1400_RTPR); | |
| 1931 | else { | |
| 1932 | itimeout = (1000 * bits + t->c_ispeed - 1) / t->c_ispeed; | |
| 1933 | #ifdef SOFT_HOTCHAR | |
| 1934 | #define MIN_RTP 1 | |
| 1935 | #else | |
| 1936 | #define MIN_RTP 5 | |
| 1937 | #endif | |
| 1938 | if (itimeout < MIN_RTP) | |
| 1939 | itimeout = MIN_RTP; | |
| 1940 | } | |
| 1941 | if (!(t->c_lflag & ICANON) && t->c_cc[VMIN] != 0 && t->c_cc[VTIME] != 0 | |
| 1942 | && t->c_cc[VTIME] * 10 > itimeout) | |
| 1943 | itimeout = t->c_cc[VTIME] * 10; | |
| 1944 | if (itimeout > 255) | |
| 1945 | itimeout = 255; | |
| 1946 | cd_setreg(com, CD1400_RTPR, itimeout); | |
| 1947 | ||
| 1948 | /* | |
| 1949 | * set channel option register 2 - | |
| 1950 | * flow control | |
| 1951 | */ | |
| 1952 | opt = 0; | |
| 1953 | #ifdef Smarts | |
| 1954 | if (iflag & IXANY) | |
| 1955 | opt |= CD1400_COR2_IXANY; | |
| 1956 | if (iflag & IXOFF) | |
| 1957 | opt |= CD1400_COR2_IXOFF; | |
| 1958 | #endif | |
| 1959 | #ifndef SOFT_CTS_OFLOW | |
| 1960 | if (cflag & CCTS_OFLOW) | |
| 1961 | opt |= CD1400_COR2_CCTS_OFLOW; | |
| 1962 | #endif | |
| 1963 | disable_intr(); | |
| 1964 | if (opt != com->cor[1]) { | |
| 1965 | cor_change |= CD1400_CCR_COR2; | |
| 1966 | cd_setreg(com, CD1400_COR2, com->cor[1] = opt); | |
| 1967 | } | |
| 1968 | enable_intr(); | |
| 1969 | ||
| 1970 | /* | |
| 1971 | * set channel option register 3 - | |
| 1972 | * receiver FIFO interrupt threshold | |
| 1973 | * flow control | |
| 1974 | */ | |
| 1975 | opt = RxFifoThreshold; | |
| 1976 | #ifdef Smarts | |
| 1977 | if (t->c_lflag & ICANON) | |
| 1978 | opt |= CD1400_COR3_SCD34; /* detect INTR & SUSP chars */ | |
| 1979 | if (iflag & IXOFF) | |
| 1980 | /* detect and transparently handle START and STOP chars */ | |
| 1981 | opt |= CD1400_COR3_FCT | CD1400_COR3_SCD12; | |
| 1982 | #endif | |
| 1983 | if (opt != com->cor[2]) { | |
| 1984 | cor_change |= CD1400_CCR_COR3; | |
| 1985 | cd_setreg(com, CD1400_COR3, com->cor[2] = opt); | |
| 1986 | } | |
| 1987 | ||
| 1988 | /* notify the CD1400 if COR1-3 have changed */ | |
| 1989 | if (cor_change) | |
| 1990 | cd1400_channel_cmd(com, CD1400_CCR_CMDCORCHG | cor_change); | |
| 1991 | ||
| 1992 | /* | |
| 1993 | * set channel option register 4 - | |
| 1994 | * CR/NL processing | |
| 1995 | * break processing | |
| 1996 | * received exception processing | |
| 1997 | */ | |
| 1998 | opt = 0; | |
| 1999 | if (iflag & IGNCR) | |
| 2000 | opt |= CD1400_COR4_IGNCR; | |
| 2001 | #ifdef Smarts | |
| 2002 | /* | |
| 2003 | * we need a new ttyinput() for this, as we don't want to | |
| 2004 | * have ICRNL && INLCR being done in both layers, or to have | |
| 2005 | * synchronisation problems | |
| 2006 | */ | |
| 2007 | if (iflag & ICRNL) | |
| 2008 | opt |= CD1400_COR4_ICRNL; | |
| 2009 | if (iflag & INLCR) | |
| 2010 | opt |= CD1400_COR4_INLCR; | |
| 2011 | #endif | |
| 2012 | if (iflag & IGNBRK) | |
| 2013 | opt |= CD1400_COR4_IGNBRK | CD1400_COR4_NOBRKINT; | |
| 2014 | /* | |
| 2015 | * The `-ignbrk -brkint parmrk' case is not handled by the hardware, | |
| 2016 | * so only tell the hardware about -brkint if -parmrk. | |
| 2017 | */ | |
| 2018 | if (!(iflag & (BRKINT | PARMRK))) | |
| 2019 | opt |= CD1400_COR4_NOBRKINT; | |
| 2020 | #if 0 | |
| 2021 | /* XXX using this "intelligence" breaks reporting of overruns. */ | |
| 2022 | if (iflag & IGNPAR) | |
| 2023 | opt |= CD1400_COR4_PFO_DISCARD; | |
| 2024 | else { | |
| 2025 | if (iflag & PARMRK) | |
| 2026 | opt |= CD1400_COR4_PFO_ESC; | |
| 2027 | else | |
| 2028 | opt |= CD1400_COR4_PFO_NUL; | |
| 2029 | } | |
| 2030 | #else | |
| 2031 | opt |= CD1400_COR4_PFO_EXCEPTION; | |
| 2032 | #endif | |
| 2033 | cd_setreg(com, CD1400_COR4, opt); | |
| 2034 | ||
| 2035 | /* | |
| 2036 | * set channel option register 5 - | |
| 2037 | */ | |
| 2038 | opt = 0; | |
| 2039 | if (iflag & ISTRIP) | |
| 2040 | opt |= CD1400_COR5_ISTRIP; | |
| 2041 | if (t->c_iflag & IEXTEN) | |
| 2042 | /* enable LNEXT (e.g. ctrl-v quoting) handling */ | |
| 2043 | opt |= CD1400_COR5_LNEXT; | |
| 2044 | #ifdef Smarts | |
| 2045 | if (t->c_oflag & ONLCR) | |
| 2046 | opt |= CD1400_COR5_ONLCR; | |
| 2047 | if (t->c_oflag & OCRNL) | |
| 2048 | opt |= CD1400_COR5_OCRNL; | |
| 2049 | #endif | |
| 2050 | cd_setreg(com, CD1400_COR5, opt); | |
| 2051 | ||
| 2052 | /* | |
| 2053 | * We always generate modem status change interrupts for CD changes. | |
| 2054 | * Among other things, this is necessary to track TS_CARR_ON for | |
| 2055 | * pstat to print even when the driver doesn't care. CD changes | |
| 2056 | * should be rare so interrupts for them are not worth extra code to | |
| 2057 | * avoid. We avoid interrupts for other modem status changes (except | |
| 2058 | * for CTS changes when SOFT_CTS_OFLOW is configured) since this is | |
| 2059 | * simplest and best. | |
| 2060 | */ | |
| 2061 | ||
| 2062 | /* | |
| 2063 | * set modem change option register 1 | |
| 2064 | * generate modem interrupts on which 1 -> 0 input transitions | |
| 2065 | * also controls auto-DTR output flow-control, which we don't use | |
| 2066 | */ | |
| 2067 | opt = CD1400_MCOR1_CDzd; | |
| 2068 | #ifdef SOFT_CTS_OFLOW | |
| 2069 | if (cflag & CCTS_OFLOW) | |
| 2070 | opt |= CD1400_MCOR1_CTSzd; | |
| 2071 | #endif | |
| 2072 | cd_setreg(com, CD1400_MCOR1, opt); | |
| 2073 | ||
| 2074 | /* | |
| 2075 | * set modem change option register 2 | |
| 2076 | * generate modem interrupts on specific 0 -> 1 input transitions | |
| 2077 | */ | |
| 2078 | opt = CD1400_MCOR2_CDod; | |
| 2079 | #ifdef SOFT_CTS_OFLOW | |
| 2080 | if (cflag & CCTS_OFLOW) | |
| 2081 | opt |= CD1400_MCOR2_CTSod; | |
| 2082 | #endif | |
| 2083 | cd_setreg(com, CD1400_MCOR2, opt); | |
| 2084 | ||
| 2085 | /* | |
| 2086 | * XXX should have done this long ago, but there is too much state | |
| 2087 | * to change all atomically. | |
| 2088 | */ | |
| 2089 | disable_intr(); | |
| 2090 | ||
| 2091 | com->state &= ~CS_TTGO; | |
| 2092 | if (!(tp->t_state & TS_TTSTOP)) | |
| 2093 | com->state |= CS_TTGO; | |
| 2094 | if (cflag & CRTS_IFLOW) { | |
| 2095 | com->state |= CS_RTS_IFLOW; | |
| 2096 | /* | |
| 2097 | * If CS_RTS_IFLOW just changed from off to on, the change | |
| 2098 | * needs to be propagated to MCR_RTS. This isn't urgent, | |
| 2099 | * so do it later by calling comstart() instead of repeating | |
| 2100 | * a lot of code from comstart() here. | |
| 2101 | */ | |
| 2102 | } else if (com->state & CS_RTS_IFLOW) { | |
| 2103 | com->state &= ~CS_RTS_IFLOW; | |
| 2104 | /* | |
| 2105 | * CS_RTS_IFLOW just changed from on to off. Force MCR_RTS | |
| 2106 | * on here, since comstart() won't do it later. | |
| 2107 | */ | |
| 2108 | #if 0 | |
| 2109 | outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); | |
| 2110 | #else | |
| 2111 | cd_setreg(com, com->mcr_rts_reg, | |
| 2112 | com->mcr_image |= com->mcr_rts); | |
| 2113 | #endif | |
| 2114 | } | |
| 2115 | ||
| 2116 | /* | |
| 2117 | * Set up state to handle output flow control. | |
| 2118 | * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? | |
| 2119 | * Now has 10+ msec latency, while CTS flow has 50- usec latency. | |
| 2120 | */ | |
| 2121 | com->state |= CS_ODEVREADY; | |
| 2122 | #ifdef SOFT_CTS_OFLOW | |
| 2123 | com->state &= ~CS_CTS_OFLOW; | |
| 2124 | if (cflag & CCTS_OFLOW) { | |
| 2125 | com->state |= CS_CTS_OFLOW; | |
| 2126 | if (!(com->last_modem_status & MSR_CTS)) | |
| 2127 | com->state &= ~CS_ODEVREADY; | |
| 2128 | } | |
| 2129 | #endif | |
| 2130 | /* XXX shouldn't call functions while intrs are disabled. */ | |
| 2131 | disc_optim(tp, t, com); | |
| 2132 | #if 0 | |
| 2133 | /* | |
| 2134 | * Recover from fiddling with CS_TTGO. We used to call siointr1() | |
| 2135 | * unconditionally, but that defeated the careful discarding of | |
| 2136 | * stale input in sioopen(). | |
| 2137 | */ | |
| 2138 | if (com->state >= (CS_BUSY | CS_TTGO)) | |
| 2139 | siointr1(com); | |
| 2140 | #endif | |
| 2141 | if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { | |
| 2142 | if (!(com->intr_enable & CD1400_SRER_TXRDY)) | |
| 2143 | cd_setreg(com, CD1400_SRER, | |
| 2144 | com->intr_enable | |
| 2145 | = (com->intr_enable & ~CD1400_SRER_TXMPTY) | |
| 2146 | | CD1400_SRER_TXRDY); | |
| 2147 | } else { | |
| 2148 | if (com->intr_enable & CD1400_SRER_TXRDY) | |
| 2149 | cd_setreg(com, CD1400_SRER, | |
| 2150 | com->intr_enable | |
| 2151 | = (com->intr_enable & ~CD1400_SRER_TXRDY) | |
| 2152 | | CD1400_SRER_TXMPTY); | |
| 2153 | } | |
| 2154 | ||
| 2155 | enable_intr(); | |
| 8d77660e | 2156 | crit_exit(); |
| 984263bc MD |
2157 | comstart(tp); |
| 2158 | if (com->ibufold != NULL) { | |
| efda3bd0 | 2159 | kfree(com->ibufold, M_DEVBUF); |
| 984263bc MD |
2160 | com->ibufold = NULL; |
| 2161 | } | |
| 2162 | return (0); | |
| 2163 | } | |
| 2164 | ||
| 2165 | static int | |
| ce63e0fa | 2166 | siosetwater(struct com_s *com, speed_t speed) |
| 984263bc MD |
2167 | { |
| 2168 | int cp4ticks; | |
| 2169 | u_char *ibuf; | |
| 2170 | int ibufsize; | |
| 2171 | struct tty *tp; | |
| 2172 | ||
| 2173 | /* | |
| 2174 | * Make the buffer size large enough to handle a softtty interrupt | |
| 2175 | * latency of about 2 ticks without loss of throughput or data | |
| 2176 | * (about 3 ticks if input flow control is not used or not honoured, | |
| 2177 | * but a bit less for CS5-CS7 modes). | |
| 2178 | */ | |
| 2179 | cp4ticks = speed / 10 / hz * 4; | |
| 2180 | for (ibufsize = 128; ibufsize < cp4ticks;) | |
| 2181 | ibufsize <<= 1; | |
| 2182 | if (ibufsize == com->ibufsize) { | |
| 2183 | disable_intr(); | |
| 2184 | return (0); | |
| 2185 | } | |
| 2186 | ||
| 2187 | /* | |
| 2188 | * Allocate input buffer. The extra factor of 2 in the size is | |
| 2189 | * to allow for an error byte for each input byte. | |
| 2190 | */ | |
| efda3bd0 | 2191 | ibuf = kmalloc(2 * ibufsize, M_DEVBUF, M_WAITOK); |
| 984263bc MD |
2192 | |
| 2193 | /* Initialize non-critical variables. */ | |
| 2194 | com->ibufold = com->ibuf; | |
| 2195 | com->ibufsize = ibufsize; | |
| 2196 | tp = com->tp; | |
| 2197 | if (tp != NULL) { | |
| 2198 | tp->t_ififosize = 2 * ibufsize; | |
| 2199 | tp->t_ispeedwat = (speed_t)-1; | |
| 2200 | tp->t_ospeedwat = (speed_t)-1; | |
| 2201 | } | |
| 2202 | ||
| 2203 | /* | |
| 2204 | * Read current input buffer, if any. Continue with interrupts | |
| 2205 | * disabled. | |
| 2206 | */ | |
| 2207 | disable_intr(); | |
| 2208 | if (com->iptr != com->ibuf) | |
| 2209 | sioinput(com); | |
| 2210 | ||
| 2211 | /*- | |
| 2212 | * Initialize critical variables, including input buffer watermarks. | |
| 2213 | * The external device is asked to stop sending when the buffer | |
| 2214 | * exactly reaches high water, or when the high level requests it. | |
| 2215 | * The high level is notified immediately (rather than at a later | |
| 2216 | * clock tick) when this watermark is reached. | |
| 2217 | * The buffer size is chosen so the watermark should almost never | |
| 2218 | * be reached. | |
| 2219 | * The low watermark is invisibly 0 since the buffer is always | |
| 2220 | * emptied all at once. | |
| 2221 | */ | |
| 2222 | com->iptr = com->ibuf = ibuf; | |
| 2223 | com->ibufend = ibuf + ibufsize; | |
| 2224 | com->ierroff = ibufsize; | |
| 2225 | com->ihighwater = ibuf + 3 * ibufsize / 4; | |
| 2226 | return (0); | |
| 2227 | } | |
| 2228 | ||
| 2229 | static void | |
| ce63e0fa | 2230 | comstart(struct tty *tp) |
| 984263bc MD |
2231 | { |
| 2232 | struct com_s *com; | |
| 984263bc MD |
2233 | #ifdef CyDebug |
| 2234 | bool_t started; | |
| 2235 | #endif | |
| 2236 | int unit; | |
| 2237 | ||
| 2238 | unit = DEV_TO_UNIT(tp->t_dev); | |
| 2239 | com = com_addr(unit); | |
| 8d77660e | 2240 | crit_enter(); |
| 984263bc MD |
2241 | |
| 2242 | #ifdef CyDebug | |
| 2243 | ++com->start_count; | |
| 2244 | started = FALSE; | |
| 2245 | #endif | |
| 2246 | ||
| 2247 | disable_intr(); | |
| 2248 | if (tp->t_state & TS_TTSTOP) { | |
| 2249 | com->state &= ~CS_TTGO; | |
| 2250 | if (com->intr_enable & CD1400_SRER_TXRDY) | |
| 2251 | cd_setreg(com, CD1400_SRER, | |
| 2252 | com->intr_enable | |
| 2253 | = (com->intr_enable & ~CD1400_SRER_TXRDY) | |
| 2254 | | CD1400_SRER_TXMPTY); | |
| 2255 | } else { | |
| 2256 | com->state |= CS_TTGO; | |
| 2257 | if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY) | |
| 2258 | && !(com->intr_enable & CD1400_SRER_TXRDY)) | |
| 2259 | cd_setreg(com, CD1400_SRER, | |
| 2260 | com->intr_enable | |
| 2261 | = (com->intr_enable & ~CD1400_SRER_TXMPTY) | |
| 2262 | | CD1400_SRER_TXRDY); | |
| 2263 | } | |
| 2264 | if (tp->t_state & TS_TBLOCK) { | |
| 2265 | if (com->mcr_image & com->mcr_rts && com->state & CS_RTS_IFLOW) | |
| 2266 | #if 0 | |
| 2267 | outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); | |
| 2268 | #else | |
| 2269 | cd_setreg(com, com->mcr_rts_reg, | |
| 2270 | com->mcr_image &= ~com->mcr_rts); | |
| 2271 | #endif | |
| 2272 | } else { | |
| 2273 | if (!(com->mcr_image & com->mcr_rts) | |
| 2274 | && com->iptr < com->ihighwater | |
| 2275 | && com->state & CS_RTS_IFLOW) | |
| 2276 | #if 0 | |
| 2277 | outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); | |
| 2278 | #else | |
| 2279 | cd_setreg(com, com->mcr_rts_reg, | |
| 2280 | com->mcr_image |= com->mcr_rts); | |
| 2281 | #endif | |
| 2282 | } | |
| 2283 | enable_intr(); | |
| 2284 | if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { | |
| 2285 | ttwwakeup(tp); | |
| 8d77660e | 2286 | crit_exit(); |
| 984263bc MD |
2287 | return; |
| 2288 | } | |
| 2289 | if (tp->t_outq.c_cc != 0) { | |
| 2290 | struct lbq *qp; | |
| 2291 | struct lbq *next; | |
| 2292 | ||
| 2293 | if (!com->obufs[0].l_queued) { | |
| 2294 | #ifdef CyDebug | |
| 2295 | started = TRUE; | |
| 2296 | #endif | |
| 2297 | com->obufs[0].l_tail | |
| 2298 | = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, | |
| 2299 | sizeof com->obuf1); | |
| 2300 | com->obufs[0].l_next = NULL; | |
| 2301 | com->obufs[0].l_queued = TRUE; | |
| 2302 | disable_intr(); | |
| 2303 | if (com->state & CS_BUSY) { | |
| 2304 | qp = com->obufq.l_next; | |
| 2305 | while ((next = qp->l_next) != NULL) | |
| 2306 | qp = next; | |
| 2307 | qp->l_next = &com->obufs[0]; | |
| 2308 | } else { | |
| 2309 | com->obufq.l_head = com->obufs[0].l_head; | |
| 2310 | com->obufq.l_tail = com->obufs[0].l_tail; | |
| 2311 | com->obufq.l_next = &com->obufs[0]; | |
| 2312 | com->state |= CS_BUSY; | |
| 2313 | if (com->state >= (CS_BUSY | CS_TTGO | |
| 2314 | | CS_ODEVREADY)) | |
| 2315 | cd_setreg(com, CD1400_SRER, | |
| 2316 | com->intr_enable | |
| 2317 | = (com->intr_enable | |
| 2318 | & ~CD1400_SRER_TXMPTY) | |
| 2319 | | CD1400_SRER_TXRDY); | |
| 2320 | } | |
| 2321 | enable_intr(); | |
| 2322 | } | |
| 2323 | if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { | |
| 2324 | #ifdef CyDebug | |
| 2325 | started = TRUE; | |
| 2326 | #endif | |
| 2327 | com->obufs[1].l_tail | |
| 2328 | = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, | |
| 2329 | sizeof com->obuf2); | |
| 2330 | com->obufs[1].l_next = NULL; | |
| 2331 | com->obufs[1].l_queued = TRUE; | |
| 2332 | disable_intr(); | |
| 2333 | if (com->state & CS_BUSY) { | |
| 2334 | qp = com->obufq.l_next; | |
| 2335 | while ((next = qp->l_next) != NULL) | |
| 2336 | qp = next; | |
| 2337 | qp->l_next = &com->obufs[1]; | |
| 2338 | } else { | |
| 2339 | com->obufq.l_head = com->obufs[1].l_head; | |
| 2340 | com->obufq.l_tail = com->obufs[1].l_tail; | |
| 2341 | com->obufq.l_next = &com->obufs[1]; | |
| 2342 | com->state |= CS_BUSY; | |
| 2343 | if (com->state >= (CS_BUSY | CS_TTGO | |
| 2344 | | CS_ODEVREADY)) | |
| 2345 | cd_setreg(com, CD1400_SRER, | |
| 2346 | com->intr_enable | |
| 2347 | = (com->intr_enable | |
| 2348 | & ~CD1400_SRER_TXMPTY) | |
| 2349 | | CD1400_SRER_TXRDY); | |
| 2350 | } | |
| 2351 | enable_intr(); | |
| 2352 | } | |
| 2353 | tp->t_state |= TS_BUSY; | |
| 2354 | } | |
| 2355 | #ifdef CyDebug | |
| 2356 | if (started) | |
| 2357 | ++com->start_real; | |
| 2358 | #endif | |
| 2359 | #if 0 | |
| 2360 | disable_intr(); | |
| 2361 | if (com->state >= (CS_BUSY | CS_TTGO)) | |
| 2362 | siointr1(com); /* fake interrupt to start output */ | |
| 2363 | enable_intr(); | |
| 2364 | #endif | |
| 2365 | ttwwakeup(tp); | |
| 8d77660e | 2366 | crit_exit(); |
| 984263bc MD |
2367 | } |
| 2368 | ||
| 2369 | static void | |
| ce63e0fa | 2370 | comstop(struct tty *tp, int rw) |
| 984263bc MD |
2371 | { |
| 2372 | struct com_s *com; | |
| 2373 | bool_t wakeup_etc; | |
| 2374 | ||
| 2375 | com = com_addr(DEV_TO_UNIT(tp->t_dev)); | |
| 2376 | wakeup_etc = FALSE; | |
| 2377 | disable_intr(); | |
| 2378 | if (rw & FWRITE) { | |
| 2379 | com->obufs[0].l_queued = FALSE; | |
| 2380 | com->obufs[1].l_queued = FALSE; | |
| 2381 | if (com->extra_state & CSE_ODONE) { | |
| 2382 | com_events -= LOTS_OF_EVENTS; | |
| 2383 | com->extra_state &= ~CSE_ODONE; | |
| 2384 | if (com->etc != ETC_NONE) { | |
| 2385 | if (com->etc == ETC_BREAK_ENDED) | |
| 2386 | com->etc = ETC_NONE; | |
| 2387 | wakeup_etc = TRUE; | |
| 2388 | } | |
| 2389 | } | |
| 2390 | com->tp->t_state &= ~TS_BUSY; | |
| 2391 | if (com->state & CS_ODONE) | |
| 2392 | com_events -= LOTS_OF_EVENTS; | |
| 2393 | com->state &= ~(CS_ODONE | CS_BUSY); | |
| 2394 | } | |
| 2395 | if (rw & FREAD) { | |
| 2396 | /* XXX no way to reset only input fifo. */ | |
| 2397 | com_events -= (com->iptr - com->ibuf); | |
| 2398 | com->iptr = com->ibuf; | |
| 2399 | } | |
| 2400 | enable_intr(); | |
| 2401 | if (wakeup_etc) | |
| 2402 | wakeup(&com->etc); | |
| 2403 | if (rw & FWRITE && com->etc == ETC_NONE) | |
| 2404 | cd1400_channel_cmd(com, CD1400_CCR_CMDRESET | CD1400_CCR_FTF); | |
| 2405 | comstart(tp); | |
| 2406 | } | |
| 2407 | ||
| 2408 | static int | |
| ce63e0fa | 2409 | commctl(struct com_s *com, int bits, int how) |
| 984263bc MD |
2410 | { |
| 2411 | int mcr; | |
| 2412 | int msr; | |
| 2413 | ||
| 2414 | if (how == DMGET) { | |
| 2415 | if (com->channel_control & CD1400_CCR_RCVEN) | |
| 2416 | bits |= TIOCM_LE; | |
| 2417 | mcr = com->mcr_image; | |
| 2418 | if (mcr & com->mcr_dtr) | |
| 2419 | bits |= TIOCM_DTR; | |
| 2420 | if (mcr & com->mcr_rts) | |
| 2421 | /* XXX wired on for Cyclom-8Ys */ | |
| 2422 | bits |= TIOCM_RTS; | |
| 2423 | ||
| 2424 | /* | |
| 2425 | * We must read the modem status from the hardware because | |
| 2426 | * we don't generate modem status change interrupts for all | |
| 2427 | * changes, so com->prev_modem_status is not guaranteed to | |
| 2428 | * be up to date. This is safe, unlike for sio, because | |
| 2429 | * reading the status register doesn't clear pending modem | |
| 2430 | * status change interrupts. | |
| 2431 | */ | |
| 2432 | msr = cd_getreg(com, CD1400_MSVR2); | |
| 2433 | ||
| 2434 | if (msr & MSR_CTS) | |
| 2435 | bits |= TIOCM_CTS; | |
| 2436 | if (msr & MSR_DCD) | |
| 2437 | bits |= TIOCM_CD; | |
| 2438 | if (msr & MSR_DSR) | |
| 2439 | bits |= TIOCM_DSR; | |
| 2440 | if (msr & MSR_RI) | |
| 2441 | /* XXX not connected except for Cyclom-16Y? */ | |
| 2442 | bits |= TIOCM_RI; | |
| 2443 | return (bits); | |
| 2444 | } | |
| 2445 | mcr = 0; | |
| 2446 | if (bits & TIOCM_DTR) | |
| 2447 | mcr |= com->mcr_dtr; | |
| 2448 | if (bits & TIOCM_RTS) | |
| 2449 | mcr |= com->mcr_rts; | |
| 2450 | disable_intr(); | |
| 2451 | switch (how) { | |
| 2452 | case DMSET: | |
| 2453 | com->mcr_image = mcr; | |
| 2454 | cd_setreg(com, CD1400_MSVR1, mcr); | |
| 2455 | cd_setreg(com, CD1400_MSVR2, mcr); | |
| 2456 | break; | |
| 2457 | case DMBIS: | |
| 2458 | com->mcr_image = mcr = com->mcr_image | mcr; | |
| 2459 | cd_setreg(com, CD1400_MSVR1, mcr); | |
| 2460 | cd_setreg(com, CD1400_MSVR2, mcr); | |
| 2461 | break; | |
| 2462 | case DMBIC: | |
| 2463 | com->mcr_image = mcr = com->mcr_image & ~mcr; | |
| 2464 | cd_setreg(com, CD1400_MSVR1, mcr); | |
| 2465 | cd_setreg(com, CD1400_MSVR2, mcr); | |
| 2466 | break; | |
| 2467 | } | |
| 2468 | enable_intr(); | |
| 2469 | return (0); | |
| 2470 | } | |
| 2471 | ||
| 2472 | static void | |
| ce63e0fa | 2473 | siosettimeout(void) |
| 984263bc MD |
2474 | { |
| 2475 | struct com_s *com; | |
| 2476 | bool_t someopen; | |
| 2477 | int unit; | |
| 2478 | ||
| 2479 | /* | |
| 2480 | * Set our timeout period to 1 second if no polled devices are open. | |
| 2481 | * Otherwise set it to max(1/200, 1/hz). | |
| 2482 | * Enable timeouts iff some device is open. | |
| 2483 | */ | |
| e8b273ff | 2484 | callout_stop(&sio_timeout_handle); |
| 984263bc MD |
2485 | sio_timeout = hz; |
| 2486 | someopen = FALSE; | |
| 2487 | for (unit = 0; unit < NSIO; ++unit) { | |
| 2488 | com = com_addr(unit); | |
| 2489 | if (com != NULL && com->tp != NULL | |
| 2490 | && com->tp->t_state & TS_ISOPEN) { | |
| 2491 | someopen = TRUE; | |
| 2492 | #if 0 | |
| 2493 | if (com->poll || com->poll_output) { | |
| 2494 | sio_timeout = hz > 200 ? hz / 200 : 1; | |
| 2495 | break; | |
| 2496 | } | |
| 2497 | #endif | |
| 2498 | } | |
| 2499 | } | |
| 2500 | if (someopen) { | |
| 2501 | sio_timeouts_until_log = hz / sio_timeout; | |
| e8b273ff MD |
2502 | callout_reset(&sio_timeout_handle, sio_timeout, |
| 2503 | comwakeup, NULL); | |
| 984263bc MD |
2504 | } else { |
| 2505 | /* Flush error messages, if any. */ | |
| 2506 | sio_timeouts_until_log = 1; | |
| 3641b7ca | 2507 | comwakeup(NULL); |
| e8b273ff | 2508 | callout_stop(&sio_timeout_handle); |
| 984263bc MD |
2509 | } |
| 2510 | } | |
| 2511 | ||
| 2512 | static void | |
| ce63e0fa | 2513 | comwakeup(void *chan) |
| 984263bc MD |
2514 | { |
| 2515 | struct com_s *com; | |
| 2516 | int unit; | |
| 2517 | ||
| e8b273ff | 2518 | callout_reset(&sio_timeout_handle, sio_timeout, comwakeup, NULL); |
| 984263bc MD |
2519 | |
| 2520 | #if 0 | |
| 2521 | /* | |
| 2522 | * Recover from lost output interrupts. | |
| 2523 | * Poll any lines that don't use interrupts. | |
| 2524 | */ | |
| 2525 | for (unit = 0; unit < NSIO; ++unit) { | |
| 2526 | com = com_addr(unit); | |
| 2527 | if (com != NULL | |
| 2528 | && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { | |
| 2529 | disable_intr(); | |
| 2530 | siointr1(com); | |
| 2531 | enable_intr(); | |
| 2532 | } | |
| 2533 | } | |
| 2534 | #endif | |
| 2535 | ||
| 2536 | /* | |
| 2537 | * Check for and log errors, but not too often. | |
| 2538 | */ | |
| 2539 | if (--sio_timeouts_until_log > 0) | |
| 2540 | return; | |
| 2541 | sio_timeouts_until_log = hz / sio_timeout; | |
| 2542 | for (unit = 0; unit < NSIO; ++unit) { | |
| 2543 | int errnum; | |
| 2544 | ||
| 2545 | com = com_addr(unit); | |
| 2546 | if (com == NULL) | |
| 2547 | continue; | |
| 2548 | for (errnum = 0; errnum < CE_NTYPES; ++errnum) { | |
| 2549 | u_int delta; | |
| 2550 | u_long total; | |
| 2551 | ||
| 2552 | disable_intr(); | |
| 2553 | delta = com->delta_error_counts[errnum]; | |
| 2554 | com->delta_error_counts[errnum] = 0; | |
| 2555 | enable_intr(); | |
| 2556 | if (delta == 0) | |
| 2557 | continue; | |
| 2558 | total = com->error_counts[errnum] += delta; | |
| 2559 | log(LOG_ERR, "cy%d: %u more %s%s (total %lu)\n", | |
| 2560 | unit, delta, error_desc[errnum], | |
| 2561 | delta == 1 ? "" : "s", total); | |
| 2562 | } | |
| 2563 | } | |
| 2564 | } | |
| 2565 | ||
| 2566 | static void | |
| ce63e0fa | 2567 | disc_optim(struct tty *tp, struct termios *t, struct com_s *com) |
| 984263bc MD |
2568 | { |
| 2569 | #ifndef SOFT_HOTCHAR | |
| 2570 | u_char opt; | |
| 2571 | #endif | |
| 2572 | ||
| 2573 | /* | |
| 2574 | * XXX can skip a lot more cases if Smarts. Maybe | |
| 2575 | * (IGNCR | ISTRIP | IXON) in c_iflag. But perhaps we | |
| 2576 | * shouldn't skip if (TS_CNTTB | TS_LNCH) is set in t_state. | |
| 2577 | */ | |
| 2578 | if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) | |
| 2579 | && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) | |
| 2580 | && (!(t->c_iflag & PARMRK) | |
| 2581 | || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) | |
| 2582 | && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) | |
| 2583 | && linesw[tp->t_line].l_rint == ttyinput) | |
| 2584 | tp->t_state |= TS_CAN_BYPASS_L_RINT; | |
| 2585 | else | |
| 2586 | tp->t_state &= ~TS_CAN_BYPASS_L_RINT; | |
| 2587 | com->hotchar = linesw[tp->t_line].l_hotchar; | |
| 2588 | #ifndef SOFT_HOTCHAR | |
| 2589 | opt = com->cor[2] & ~CD1400_COR3_SCD34; | |
| 2590 | if (com->hotchar != 0) { | |
| 2591 | cd_setreg(com, CD1400_SCHR3, com->hotchar); | |
| 2592 | cd_setreg(com, CD1400_SCHR4, com->hotchar); | |
| 2593 | opt |= CD1400_COR3_SCD34; | |
| 2594 | } | |
| 2595 | if (opt != com->cor[2]) { | |
| 2596 | cd_setreg(com, CD1400_COR3, com->cor[2] = opt); | |
| 2597 | cd1400_channel_cmd(com, CD1400_CCR_CMDCORCHG | CD1400_CCR_COR3); | |
| 2598 | } | |
| 2599 | #endif | |
| 2600 | } | |
| 2601 | ||
| 2602 | #ifdef Smarts | |
| 2603 | /* standard line discipline input routine */ | |
| 2604 | int | |
| ce63e0fa | 2605 | cyinput(int c, struct tty *tp) |
| 984263bc MD |
2606 | { |
| 2607 | /* XXX duplicate ttyinput(), but without the IXOFF/IXON/ISTRIP/IPARMRK | |
| 2608 | * bits, as they are done by the CD1400. Hardly worth the effort, | |
| 2609 | * given that high-throughput sessions are raw anyhow. | |
| 2610 | */ | |
| 2611 | } | |
| 2612 | #endif /* Smarts */ | |
| 2613 | ||
| 2614 | static int | |
| ce63e0fa | 2615 | comspeed(speed_t speed, u_long cy_clock, int *prescaler_io) |
| 984263bc MD |
2616 | { |
| 2617 | int actual; | |
| 2618 | int error; | |
| 2619 | int divider; | |
| 2620 | int prescaler; | |
| 2621 | int prescaler_unit; | |
| 2622 | ||
| 2623 | if (speed == 0) | |
| 2624 | return (0); | |
| 2625 | if (speed < 0 || speed > 150000) | |
| 2626 | return (-1); | |
| 2627 | ||
| 2628 | /* determine which prescaler to use */ | |
| 2629 | for (prescaler_unit = 4, prescaler = 2048; prescaler_unit; | |
| 2630 | prescaler_unit--, prescaler >>= 2) { | |
| 2631 | if (cy_clock / prescaler / speed > 63) | |
| 2632 | break; | |
| 2633 | } | |
| 2634 | ||
| 2635 | divider = (cy_clock / prescaler * 2 / speed + 1) / 2; /* round off */ | |
| 2636 | if (divider > 255) | |
| 2637 | divider = 255; | |
| 2638 | actual = cy_clock/prescaler/divider; | |
| 2639 | ||
| 2640 | /* 10 times error in percent: */ | |
| 2641 | error = ((actual - (long)speed) * 2000 / (long)speed + 1) / 2; | |
| 2642 | ||
| 2643 | /* 3.0% max error tolerance */ | |
| 2644 | if (error < -30 || error > 30) | |
| 2645 | return (-1); | |
| 2646 | ||
| 2647 | #if 0 | |
| e3869ec7 SW |
2648 | kprintf("prescaler = %d (%d)\n", prescaler, prescaler_unit); |
| 2649 | kprintf("divider = %d (%x)\n", divider, divider); | |
| 2650 | kprintf("actual = %d\n", actual); | |
| 2651 | kprintf("error = %d\n", error); | |
| 984263bc MD |
2652 | #endif |
| 2653 | ||
| 2654 | *prescaler_io = prescaler_unit; | |
| 2655 | return (divider); | |
| 2656 | } | |
| 2657 | ||
| 2658 | static void | |
| ce63e0fa | 2659 | cd1400_channel_cmd(struct com_s *com, int cmd) |
| 984263bc MD |
2660 | { |
| 2661 | cd1400_channel_cmd_wait(com); | |
| 2662 | cd_setreg(com, CD1400_CCR, cmd); | |
| 2663 | cd1400_channel_cmd_wait(com); | |
| 2664 | } | |
| 2665 | ||
| 2666 | static void | |
| ce63e0fa | 2667 | cd1400_channel_cmd_wait(struct com_s *com) |
| 984263bc MD |
2668 | { |
| 2669 | struct timeval start; | |
| 2670 | struct timeval tv; | |
| 2671 | long usec; | |
| 2672 | ||
| 2673 | if (cd_getreg(com, CD1400_CCR) == 0) | |
| 2674 | return; | |
| 2675 | microtime(&start); | |
| 2676 | for (;;) { | |
| 2677 | if (cd_getreg(com, CD1400_CCR) == 0) | |
| 2678 | return; | |
| 2679 | microtime(&tv); | |
| 2680 | usec = 1000000 * (tv.tv_sec - start.tv_sec) + | |
| 2681 | tv.tv_usec - start.tv_usec; | |
| 2682 | if (usec >= 5000) { | |
| 2683 | log(LOG_ERR, | |
| 2684 | "cy%d: channel command timeout (%ld usec)\n", | |
| 2685 | com->unit, usec); | |
| 2686 | return; | |
| 2687 | } | |
| 2688 | } | |
| 2689 | } | |
| 2690 | ||
| 2691 | static void | |
| ce63e0fa | 2692 | cd_etc(struct com_s *com, int etc) |
| 984263bc MD |
2693 | { |
| 2694 | /* | |
| 2695 | * We can't change the hardware's ETC state while there are any | |
| 2696 | * characters in the tx fifo, since those characters would be | |
| 2697 | * interpreted as commands! Unputting characters from the fifo | |
| 2698 | * is difficult, so we wait up to 12 character times for the fifo | |
| 2699 | * to drain. The command will be delayed for up to 2 character | |
| 2700 | * times for the tx to become empty. Unputting characters from | |
| 2701 | * the tx holding and shift registers is impossible, so we wait | |
| 2702 | * for the tx to become empty so that the command is sure to be | |
| 2703 | * executed soon after we issue it. | |
| 2704 | */ | |
| 2705 | disable_intr(); | |
| 2706 | if (com->etc == etc) { | |
| 2707 | enable_intr(); | |
| 2708 | goto wait; | |
| 2709 | } | |
| 2710 | if ((etc == CD1400_ETC_SENDBREAK | |
| 2711 | && (com->etc == ETC_BREAK_STARTING | |
| 2712 | || com->etc == ETC_BREAK_STARTED)) | |
| 2713 | || (etc == CD1400_ETC_STOPBREAK | |
| 2714 | && (com->etc == ETC_BREAK_ENDING || com->etc == ETC_BREAK_ENDED | |
| 2715 | || com->etc == ETC_NONE))) { | |
| 2716 | enable_intr(); | |
| 2717 | return; | |
| 2718 | } | |
| 2719 | com->etc = etc; | |
| 2720 | cd_setreg(com, CD1400_SRER, | |
| 2721 | com->intr_enable | |
| 2722 | = (com->intr_enable & ~CD1400_SRER_TXRDY) | CD1400_SRER_TXMPTY); | |
| 2723 | enable_intr(); | |
| 2724 | wait: | |
| 2725 | while (com->etc == etc | |
| 377d4740 | 2726 | && tsleep(&com->etc, PCATCH, "cyetc", 0) == 0) |
| 984263bc MD |
2727 | continue; |
| 2728 | } | |
| 2729 | ||
| 2730 | static int | |
| ce63e0fa | 2731 | cd_getreg(struct com_s *com, int reg) |
| 984263bc MD |
2732 | { |
| 2733 | struct com_s *basecom; | |
| 2734 | u_char car; | |
| 2735 | int cy_align; | |
| 2736 | u_long ef; | |
| 2737 | cy_addr iobase; | |
| 2738 | int val; | |
| 2739 | ||
| 2740 | basecom = com_addr(com->unit & ~(CD1400_NO_OF_CHANNELS - 1)); | |
| 2741 | car = com->unit & CD1400_CAR_CHAN; | |
| 2742 | cy_align = com->cy_align; | |
| 2743 | iobase = com->iobase; | |
| 2744 | ef = read_eflags(); | |
| 2745 | if (ef & PSL_I) | |
| 2746 | disable_intr(); | |
| 2747 | if (basecom->car != car) | |
| 2748 | cd_outb(iobase, CD1400_CAR, cy_align, basecom->car = car); | |
| 2749 | val = cd_inb(iobase, reg, cy_align); | |
| 2750 | if (ef & PSL_I) | |
| 2751 | enable_intr(); | |
| 2752 | return (val); | |
| 2753 | } | |
| 2754 | ||
| 2755 | static void | |
| ce63e0fa | 2756 | cd_setreg(struct com_s *com, int reg, int val) |
| 984263bc MD |
2757 | { |
| 2758 | struct com_s *basecom; | |
| 2759 | u_char car; | |
| 2760 | int cy_align; | |
| 2761 | u_long ef; | |
| 2762 | cy_addr iobase; | |
| 2763 | ||
| 2764 | basecom = com_addr(com->unit & ~(CD1400_NO_OF_CHANNELS - 1)); | |
| 2765 | car = com->unit & CD1400_CAR_CHAN; | |
| 2766 | cy_align = com->cy_align; | |
| 2767 | iobase = com->iobase; | |
| 2768 | ef = read_eflags(); | |
| 2769 | if (ef & PSL_I) | |
| 2770 | disable_intr(); | |
| 2771 | if (basecom->car != car) | |
| 2772 | cd_outb(iobase, CD1400_CAR, cy_align, basecom->car = car); | |
| 2773 | cd_outb(iobase, reg, cy_align, val); | |
| 2774 | if (ef & PSL_I) | |
| 2775 | enable_intr(); | |
| 2776 | } | |
| 2777 | ||
| 2778 | #ifdef CyDebug | |
| 2779 | /* useful in ddb */ | |
| 2780 | void | |
| ce63e0fa | 2781 | cystatus(int unit) |
| 984263bc MD |
2782 | { |
| 2783 | struct com_s *com; | |
| 2784 | cy_addr iobase; | |
| 2785 | u_int ocount; | |
| 2786 | struct tty *tp; | |
| 2787 | ||
| 2788 | com = com_addr(unit); | |
| e3869ec7 SW |
2789 | kprintf("info for channel %d\n", unit); |
| 2790 | kprintf("------------------\n"); | |
| 2791 | kprintf("total cyclom service probes:\t%d\n", cy_svrr_probes); | |
| 2792 | kprintf("calls to upper layer:\t\t%d\n", cy_timeouts); | |
| 984263bc MD |
2793 | if (com == NULL) |
| 2794 | return; | |
| 2795 | iobase = com->iobase; | |
| e3869ec7 SW |
2796 | kprintf("\n"); |
| 2797 | kprintf("cd1400 base address:\\tt%p\n", iobase); | |
| 2798 | kprintf("saved channel_control:\t\t0x%02x\n", com->channel_control); | |
| 2799 | kprintf("saved cor1-3:\t\t\t0x%02x 0x%02x 0x%02x\n", | |
| 984263bc | 2800 | com->cor[0], com->cor[1], com->cor[2]); |
| e3869ec7 | 2801 | kprintf("service request enable reg:\t0x%02x (0x%02x cached)\n", |
| 984263bc | 2802 | cd_getreg(com, CD1400_SRER), com->intr_enable); |
| e3869ec7 | 2803 | kprintf("service request register:\t0x%02x\n", |
| 984263bc | 2804 | cd_inb(iobase, CD1400_SVRR, com->cy_align)); |
| e3869ec7 | 2805 | kprintf("modem status:\t\t\t0x%02x (0x%02x cached)\n", |
| 984263bc | 2806 | cd_getreg(com, CD1400_MSVR2), com->prev_modem_status); |
| e3869ec7 | 2807 | kprintf("rx/tx/mdm interrupt registers:\t0x%02x 0x%02x 0x%02x\n", |
| 984263bc MD |
2808 | cd_inb(iobase, CD1400_RIR, com->cy_align), |
| 2809 | cd_inb(iobase, CD1400_TIR, com->cy_align), | |
| 2810 | cd_inb(iobase, CD1400_MIR, com->cy_align)); | |
| e3869ec7 SW |
2811 | kprintf("\n"); |
| 2812 | kprintf("com state:\t\t\t0x%02x\n", com->state); | |
| 2813 | kprintf("calls to comstart():\t\t%d (%d useful)\n", | |
| 984263bc | 2814 | com->start_count, com->start_real); |
| e3869ec7 | 2815 | kprintf("rx buffer chars free:\t\t%d\n", com->iptr - com->ibuf); |
| 984263bc MD |
2816 | ocount = 0; |
| 2817 | if (com->obufs[0].l_queued) | |
| 2818 | ocount += com->obufs[0].l_tail - com->obufs[0].l_head; | |
| 2819 | if (com->obufs[1].l_queued) | |
| 2820 | ocount += com->obufs[1].l_tail - com->obufs[1].l_head; | |
| e3869ec7 SW |
2821 | kprintf("tx buffer chars:\t\t%u\n", ocount); |
| 2822 | kprintf("received chars:\t\t\t%d\n", com->bytes_in); | |
| 2823 | kprintf("received exceptions:\t\t%d\n", com->recv_exception); | |
| 2824 | kprintf("modem signal deltas:\t\t%d\n", com->mdm); | |
| 2825 | kprintf("transmitted chars:\t\t%d\n", com->bytes_out); | |
| 2826 | kprintf("\n"); | |
| 984263bc MD |
2827 | tp = com->tp; |
| 2828 | if (tp != NULL) { | |
| e3869ec7 SW |
2829 | kprintf("tty state:\t\t\t0x%08x\n", tp->t_state); |
| 2830 | kprintf( | |
| 984263bc MD |
2831 | "upper layer queue lengths:\t%d raw, %d canon, %d output\n", |
| 2832 | tp->t_rawq.c_cc, tp->t_canq.c_cc, tp->t_outq.c_cc); | |
| 2833 | } else | |
| e3869ec7 | 2834 | kprintf("tty state:\t\t\tclosed\n"); |
| 984263bc MD |
2835 | } |
| 2836 | #endif /* CyDebug */ |