4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
23 /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */
24 /* All Rights Reserved */
27 * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
28 * Copyright 2012 Milan Jurik. All rights reserved.
29 * Copyright (c) 2016 by Delphix. All rights reserved.
34 * Serial I/O driver for 8250/16450/16550A/16650/16750 chips.
37 #include <sys/param.h>
38 #include <sys/types.h>
39 #include <sys/signal.h>
40 #include <sys/stream.h>
41 #include <sys/termio.h>
42 #include <sys/errno.h>
44 #include <sys/cmn_err.h>
45 #include <sys/stropts.h>
46 #include <sys/strsubr.h>
47 #include <sys/strtty.h>
48 #include <sys/debug.h>
52 #include <sys/consdev.h>
53 #include <sys/mkdev.h>
56 #include <sys/strsun.h>
58 #include <sys/promif.h>
60 #include <sys/modctl.h>
62 #include <sys/sunddi.h>
65 #include <sys/policy.h>
68 * set the RX FIFO trigger_level to half the RX FIFO size for now
69 * we may want to make this configurable later.
71 static int asy_trig_level
= FIFO_TRIG_8
;
73 int asy_drain_check
= 15000000; /* tunable: exit drain check time */
74 int asy_min_dtr_low
= 500000; /* tunable: minimum DTR down time */
75 int asy_min_utbrk
= 100000; /* tunable: minumum untimed brk time */
77 int asymaxchip
= ASY16750
; /* tunable: limit chip support we look for */
80 * Just in case someone has a chip with broken loopback mode, we provide a
81 * means to disable the loopback test. By default, we only loopback test
82 * UARTs which look like they have FIFOs bigger than 16 bytes.
83 * Set to 0 to suppress test, or to 2 to enable test on any size FIFO.
85 int asy_fifo_test
= 1; /* tunable: set to 0, 1, or 2 */
88 * Allow ability to switch off testing of the scratch register.
89 * Some UART emulators might not have it. This will also disable the test
90 * for Exar/Startech ST16C650, as that requires use of the SCR register.
92 int asy_scr_test
= 1; /* tunable: set to 0 to disable SCR reg test */
95 * As we don't yet support on-chip flow control, it's a bad idea to put a
96 * large number of characters in the TX FIFO, since if other end tells us
97 * to stop transmitting, we can only stop filling the TX FIFO, but it will
98 * still carry on draining by itself, so remote end still gets what's left
101 int asy_max_tx_fifo
= 16; /* tunable: max fill of TX FIFO */
103 #define async_stopc async_ttycommon.t_stopc
104 #define async_startc async_ttycommon.t_startc
109 /* enum value for sw and hw flow control action */
114 } async_flowc_action
;
117 #define ASY_DEBUG_INIT 0x0001 /* Output msgs during driver initialization. */
118 #define ASY_DEBUG_INPUT 0x0002 /* Report characters received during int. */
119 #define ASY_DEBUG_EOT 0x0004 /* Output msgs when wait for xmit to finish. */
120 #define ASY_DEBUG_CLOSE 0x0008 /* Output msgs when driver open/close called */
121 #define ASY_DEBUG_HFLOW 0x0010 /* Output msgs when H/W flowcontrol is active */
122 #define ASY_DEBUG_PROCS 0x0020 /* Output each proc name as it is entered. */
123 #define ASY_DEBUG_STATE 0x0040 /* Output value of Interrupt Service Reg. */
124 #define ASY_DEBUG_INTR 0x0080 /* Output value of Interrupt Service Reg. */
125 #define ASY_DEBUG_OUT 0x0100 /* Output msgs about output events. */
126 #define ASY_DEBUG_BUSY 0x0200 /* Output msgs when xmit is enabled/disabled */
127 #define ASY_DEBUG_MODEM 0x0400 /* Output msgs about modem status & control. */
128 #define ASY_DEBUG_MODM2 0x0800 /* Output msgs about modem status & control. */
129 #define ASY_DEBUG_IOCTL 0x1000 /* Output msgs about ioctl messages. */
130 #define ASY_DEBUG_CHIP 0x2000 /* Output msgs about chip identification. */
131 #define ASY_DEBUG_SFLOW 0x4000 /* Output msgs when S/W flowcontrol is active */
132 #define ASY_DEBUG(x) (debug & (x))
133 static int debug
= 0;
135 #define ASY_DEBUG(x) B_FALSE
138 /* pnpISA compressed device ids */
139 #define pnpMTS0219 0xb6930219 /* Multitech MT5634ZTX modem */
142 * PPS (Pulse Per Second) support.
144 void ddi_hardpps(struct timeval
*, int);
146 * This is protected by the asy_excl_hi of the port on which PPS event
147 * handling is enabled. Note that only one port should have this enabled at
148 * any one time. Enabling PPS handling on multiple ports will result in
149 * unpredictable (but benign) results.
151 static struct ppsclockev asy_ppsev
;
154 /* XXX Use these to observe PPS latencies and jitter on a scope */
162 static int max_asy_instance
= -1;
164 static uint_t
asysoftintr(caddr_t intarg
);
165 static uint_t
asyintr(caddr_t argasy
);
167 static boolean_t
abort_charseq_recognize(uchar_t ch
);
169 /* The async interrupt entry points */
170 static void async_txint(struct asycom
*asy
);
171 static void async_rxint(struct asycom
*asy
, uchar_t lsr
);
172 static void async_msint(struct asycom
*asy
);
173 static void async_softint(struct asycom
*asy
);
175 static void async_ioctl(struct asyncline
*async
, queue_t
*q
, mblk_t
*mp
);
176 static void async_reioctl(void *unit
);
177 static void async_iocdata(queue_t
*q
, mblk_t
*mp
);
178 static void async_restart(void *arg
);
179 static void async_start(struct asyncline
*async
);
180 static void async_nstart(struct asyncline
*async
, int mode
);
181 static void async_resume(struct asyncline
*async
);
182 static void asy_program(struct asycom
*asy
, int mode
);
183 static void asyinit(struct asycom
*asy
);
184 static void asy_waiteot(struct asycom
*asy
);
185 static void asyputchar(cons_polledio_arg_t
, uchar_t c
);
186 static int asygetchar(cons_polledio_arg_t
);
187 static boolean_t
asyischar(cons_polledio_arg_t
);
189 static int asymctl(struct asycom
*, int, int);
190 static int asytodm(int, int);
191 static int dmtoasy(int);
193 static void asyerror(int level
, const char *fmt
, ...) __KPRINTFLIKE(2);
194 static void asy_parse_mode(dev_info_t
*devi
, struct asycom
*asy
);
195 static void asy_soft_state_free(struct asycom
*);
196 static char *asy_hw_name(struct asycom
*asy
);
197 static void async_hold_utbrk(void *arg
);
198 static void async_resume_utbrk(struct asyncline
*async
);
199 static void async_dtr_free(struct asyncline
*async
);
200 static int asy_identify_chip(dev_info_t
*devi
, struct asycom
*asy
);
201 static void asy_reset_fifo(struct asycom
*asy
, uchar_t flags
);
202 static int asy_getproperty(dev_info_t
*devi
, struct asycom
*asy
,
203 const char *property
);
204 static boolean_t
async_flowcontrol_sw_input(struct asycom
*asy
,
205 async_flowc_action onoff
, int type
);
206 static void async_flowcontrol_sw_output(struct asycom
*asy
,
207 async_flowc_action onoff
);
208 static void async_flowcontrol_hw_input(struct asycom
*asy
,
209 async_flowc_action onoff
, int type
);
210 static void async_flowcontrol_hw_output(struct asycom
*asy
,
211 async_flowc_action onoff
);
213 #define GET_PROP(devi, pname, pflag, pval, plen) \
214 (ddi_prop_op(DDI_DEV_T_ANY, (devi), PROP_LEN_AND_VAL_BUF, \
215 (pflag), (pname), (caddr_t)(pval), (plen)))
217 kmutex_t asy_glob_lock
; /* lock protecting global data manipulation */
218 void *asy_soft_state
;
220 /* Standard COM port I/O addresses */
221 static const int standard_com_ports
[] = {
222 COM1_IOADDR
, COM2_IOADDR
, COM3_IOADDR
, COM4_IOADDR
225 static int *com_ports
;
226 static uint_t num_com_ports
;
230 * Set this to true to make the driver pretend to do a suspend. Useful
231 * for debugging suspend/resume code with a serial debugger.
233 boolean_t asy_nosuspend
= B_FALSE
;
238 * Baud rate table. Indexed by #defines found in sys/termios.h
240 ushort_t asyspdtab
[] = {
242 0x900, /* 50 baud rate */
243 0x600, /* 75 baud rate */
244 0x417, /* 110 baud rate (%0.026) */
245 0x359, /* 134 baud rate (%0.058) */
246 0x300, /* 150 baud rate */
247 0x240, /* 200 baud rate */
248 0x180, /* 300 baud rate */
249 0x0c0, /* 600 baud rate */
250 0x060, /* 1200 baud rate */
251 0x040, /* 1800 baud rate */
252 0x030, /* 2400 baud rate */
253 0x018, /* 4800 baud rate */
254 0x00c, /* 9600 baud rate */
255 0x006, /* 19200 baud rate */
256 0x003, /* 38400 baud rate */
258 0x002, /* 57600 baud rate */
259 0x0, /* 76800 baud rate not supported */
260 0x001, /* 115200 baud rate */
261 0x0, /* 153600 baud rate not supported */
262 0x0, /* 0x8002 (SMC chip) 230400 baud rate not supported */
263 0x0, /* 307200 baud rate not supported */
264 0x0, /* 0x8001 (SMC chip) 460800 baud rate not supported */
276 static int asyrsrv(queue_t
*q
);
277 static int asyopen(queue_t
*rq
, dev_t
*dev
, int flag
, int sflag
, cred_t
*cr
);
278 static int asyclose(queue_t
*q
, int flag
, cred_t
*credp
);
279 static int asywputdo(queue_t
*q
, mblk_t
*mp
, boolean_t
);
280 static int asywput(queue_t
*q
, mblk_t
*mp
);
282 struct module_info asy_info
= {
291 static struct qinit asy_rint
= {
301 static struct qinit asy_wint
= {
311 struct streamtab asy_str_info
= {
318 static int asyinfo(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
,
320 static int asyprobe(dev_info_t
*);
321 static int asyattach(dev_info_t
*, ddi_attach_cmd_t
);
322 static int asydetach(dev_info_t
*, ddi_detach_cmd_t
);
323 static int asyquiesce(dev_info_t
*);
325 static struct cb_ops cb_asy_ops
= {
327 nodev
, /* cb_close */
328 nodev
, /* cb_strategy */
329 nodev
, /* cb_print */
332 nodev
, /* cb_write */
333 nodev
, /* cb_ioctl */
334 nodev
, /* cb_devmap */
336 nodev
, /* cb_segmap */
337 nochpoll
, /* cb_chpoll */
338 ddi_prop_op
, /* cb_prop_op */
339 &asy_str_info
, /* cb_stream */
343 struct dev_ops asy_ops
= {
344 DEVO_REV
, /* devo_rev */
346 asyinfo
, /* devo_getinfo */
347 nulldev
, /* devo_identify */
348 asyprobe
, /* devo_probe */
349 asyattach
, /* devo_attach */
350 asydetach
, /* devo_detach */
351 nodev
, /* devo_reset */
352 &cb_asy_ops
, /* devo_cb_ops */
353 NULL
, /* devo_bus_ops */
355 asyquiesce
, /* quiesce */
358 static struct modldrv modldrv
= {
359 &mod_driverops
, /* Type of module. This one is a driver */
361 &asy_ops
, /* driver ops */
364 static struct modlinkage modlinkage
= {
375 i
= ddi_soft_state_init(&asy_soft_state
, sizeof (struct asycom
), 2);
377 mutex_init(&asy_glob_lock
, NULL
, MUTEX_DRIVER
, NULL
);
378 if ((i
= mod_install(&modlinkage
)) != 0) {
379 mutex_destroy(&asy_glob_lock
);
380 ddi_soft_state_fini(&asy_soft_state
);
382 DEBUGCONT2(ASY_DEBUG_INIT
, "%s, debug = %x\n",
383 modldrv
.drv_linkinfo
, debug
);
394 if ((i
= mod_remove(&modlinkage
)) == 0) {
395 DEBUGCONT1(ASY_DEBUG_INIT
, "%s unloading\n",
396 modldrv
.drv_linkinfo
);
397 ASSERT(max_asy_instance
== -1);
398 mutex_destroy(&asy_glob_lock
);
399 /* free "motherboard-serial-ports" property if allocated */
400 if (com_ports
!= NULL
&& com_ports
!= (int *)standard_com_ports
)
401 ddi_prop_free(com_ports
);
403 ddi_soft_state_fini(&asy_soft_state
);
409 _info(struct modinfo
*modinfop
)
411 return (mod_info(&modlinkage
, modinfop
));
415 async_put_suspq(struct asycom
*asy
, mblk_t
*mp
)
417 struct asyncline
*async
= asy
->asy_priv
;
419 ASSERT(mutex_owned(&asy
->asy_excl
));
421 if (async
->async_suspqf
== NULL
)
422 async
->async_suspqf
= mp
;
424 async
->async_suspqb
->b_next
= mp
;
426 async
->async_suspqb
= mp
;
430 async_get_suspq(struct asycom
*asy
)
432 struct asyncline
*async
= asy
->asy_priv
;
435 ASSERT(mutex_owned(&asy
->asy_excl
));
437 if ((mp
= async
->async_suspqf
) != NULL
) {
438 async
->async_suspqf
= mp
->b_next
;
441 async
->async_suspqb
= NULL
;
447 async_process_suspq(struct asycom
*asy
)
449 struct asyncline
*async
= asy
->asy_priv
;
452 ASSERT(mutex_owned(&asy
->asy_excl
));
454 while ((mp
= async_get_suspq(asy
)) != NULL
) {
457 q
= async
->async_ttycommon
.t_writeq
;
459 mutex_exit(&asy
->asy_excl
);
460 (void) asywputdo(q
, mp
, B_FALSE
);
461 mutex_enter(&asy
->asy_excl
);
463 async
->async_flags
&= ~ASYNC_DDI_SUSPENDED
;
464 cv_broadcast(&async
->async_flags_cv
);
468 asy_get_bus_type(dev_info_t
*devinfo
)
470 char parent_type
[16];
473 parentlen
= sizeof (parent_type
);
475 if (ddi_prop_op(DDI_DEV_T_ANY
, devinfo
, PROP_LEN_AND_VAL_BUF
, 0,
476 "device_type", (caddr_t
)parent_type
, &parentlen
)
477 != DDI_PROP_SUCCESS
&& ddi_prop_op(DDI_DEV_T_ANY
, devinfo
,
478 PROP_LEN_AND_VAL_BUF
, 0, "bus-type", (caddr_t
)parent_type
,
479 &parentlen
) != DDI_PROP_SUCCESS
) {
481 "asy: can't figure out device type for"
483 ddi_get_name(ddi_get_parent(devinfo
)));
484 return (ASY_BUS_UNKNOWN
);
486 if (strcmp(parent_type
, "isa") == 0)
487 return (ASY_BUS_ISA
);
488 else if (strcmp(parent_type
, "pci") == 0)
489 return (ASY_BUS_PCI
);
491 return (ASY_BUS_UNKNOWN
);
495 asy_get_io_regnum_pci(dev_info_t
*devi
, struct asycom
*asy
)
500 struct pci_phys_spec
*reglist
;
502 if (ddi_getlongprop(DDI_DEV_T_ANY
, devi
, DDI_PROP_DONTPASS
,
503 "reg", (caddr_t
)®list
, ®len
) != DDI_PROP_SUCCESS
) {
504 cmn_err(CE_WARN
, "asy_get_io_regnum_pci: reg property"
505 " not found in devices property list");
510 * PCI devices are assumed to not have broken FIFOs;
511 * Agere/Lucent Venus PCI modem chipsets are an example
514 asy
->asy_flags2
|= ASY2_NO_LOOPBACK
;
517 nregs
= reglen
/ sizeof (*reglist
);
518 for (i
= 0; i
< nregs
; i
++) {
519 switch (reglist
[i
].pci_phys_hi
& PCI_ADDR_MASK
) {
520 case PCI_ADDR_IO
: /* I/O bus reg property */
521 if (regnum
== -1) /* use only the first one */
530 /* check for valid count of registers */
532 size
= ((uint64_t)reglist
[regnum
].pci_size_low
) |
533 ((uint64_t)reglist
[regnum
].pci_size_hi
) << 32;
537 kmem_free(reglist
, reglen
);
542 asy_get_io_regnum_isa(dev_info_t
*devi
, struct asycom
*asy
)
552 if (ddi_getlongprop(DDI_DEV_T_ANY
, devi
, DDI_PROP_DONTPASS
,
553 "reg", (caddr_t
)®list
, ®len
) != DDI_PROP_SUCCESS
) {
554 cmn_err(CE_WARN
, "asy_get_io_regnum: reg property not found "
555 "in devices property list");
560 nregs
= reglen
/ sizeof (*reglist
);
561 for (i
= 0; i
< nregs
; i
++) {
562 switch (reglist
[i
].bustype
) {
563 case 1: /* I/O bus reg property */
564 if (regnum
== -1) /* only use the first one */
568 case pnpMTS0219
: /* Multitech MT5634ZTX modem */
569 /* Venus chipset can't do loopback test */
571 asy
->asy_flags2
|= ASY2_NO_LOOPBACK
;
579 /* check for valid count of registers */
580 if ((regnum
< 0) || (reglist
[regnum
].size
< 8))
582 kmem_free(reglist
, reglen
);
587 asy_get_io_regnum(dev_info_t
*devinfo
, struct asycom
*asy
)
589 switch (asy_get_bus_type(devinfo
)) {
591 return (asy_get_io_regnum_isa(devinfo
, asy
));
593 return (asy_get_io_regnum_pci(devinfo
, asy
));
600 asydetach(dev_info_t
*devi
, ddi_detach_cmd_t cmd
)
604 struct asyncline
*async
;
606 instance
= ddi_get_instance(devi
); /* find out which unit */
608 asy
= ddi_get_soft_state(asy_soft_state
, instance
);
610 return (DDI_FAILURE
);
611 async
= asy
->asy_priv
;
615 DEBUGNOTE2(ASY_DEBUG_INIT
, "asy%d: %s shutdown.",
616 instance
, asy_hw_name(asy
));
618 /* cancel DTR hold timeout */
619 if (async
->async_dtrtid
!= 0) {
620 (void) untimeout(async
->async_dtrtid
);
621 async
->async_dtrtid
= 0;
624 /* remove all minor device node(s) for this device */
625 ddi_remove_minor_node(devi
, NULL
);
627 mutex_destroy(&asy
->asy_excl
);
628 mutex_destroy(&asy
->asy_excl_hi
);
629 cv_destroy(&async
->async_flags_cv
);
630 ddi_remove_intr(devi
, 0, asy
->asy_iblock
);
631 ddi_regs_map_free(&asy
->asy_iohandle
);
632 ddi_remove_softintr(asy
->asy_softintr_id
);
633 mutex_destroy(&asy
->asy_soft_lock
);
634 asy_soft_state_free(asy
);
635 DEBUGNOTE1(ASY_DEBUG_INIT
, "asy%d: shutdown complete",
645 return (DDI_SUCCESS
);
647 mutex_enter(&asy
->asy_excl
);
649 ASSERT(async
->async_ops
>= 0);
650 while (async
->async_ops
> 0)
651 cv_wait(&async
->async_ops_cv
, &asy
->asy_excl
);
653 async
->async_flags
|= ASYNC_DDI_SUSPENDED
;
655 /* Wait for timed break and delay to complete */
656 while ((async
->async_flags
& (ASYNC_BREAK
|ASYNC_DELAY
))) {
657 if (cv_wait_sig(&async
->async_flags_cv
, &asy
->asy_excl
)
659 async_process_suspq(asy
);
660 mutex_exit(&asy
->asy_excl
);
661 return (DDI_FAILURE
);
665 /* Clear untimed break */
666 if (async
->async_flags
& ASYNC_OUT_SUSPEND
)
667 async_resume_utbrk(async
);
669 mutex_exit(&asy
->asy_excl
);
671 mutex_enter(&asy
->asy_soft_sr
);
672 mutex_enter(&asy
->asy_excl
);
673 if (async
->async_wbufcid
!= 0) {
674 bufcall_id_t bcid
= async
->async_wbufcid
;
675 async
->async_wbufcid
= 0;
676 async
->async_flags
|= ASYNC_RESUME_BUFCALL
;
677 mutex_exit(&asy
->asy_excl
);
679 mutex_enter(&asy
->asy_excl
);
681 mutex_enter(&asy
->asy_excl_hi
);
683 /* Disable interrupts from chip */
684 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ ICR
, 0);
685 asy
->asy_flags
|= ASY_DDI_SUSPENDED
;
688 * Hardware interrupts are disabled we can drop our high level
691 mutex_exit(&asy
->asy_excl_hi
);
693 /* Process remaining RX characters and RX errors, if any */
694 lsr
= ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LSR
);
695 async_rxint(asy
, lsr
);
697 /* Wait for TX to drain */
698 for (i
= 1000; i
> 0; i
--) {
699 lsr
= ddi_get8(asy
->asy_iohandle
,
700 asy
->asy_ioaddr
+ LSR
);
701 if ((lsr
& (XSRE
| XHRE
)) == (XSRE
| XHRE
))
703 delay(drv_usectohz(10000));
707 "asy: transmitter wasn't drained before "
708 "driver was suspended");
710 mutex_exit(&asy
->asy_excl
);
711 mutex_exit(&asy
->asy_soft_sr
);
715 return (DDI_FAILURE
);
718 return (DDI_SUCCESS
);
723 * We don't bother probing for the hardware, as since Solaris 2.6, device
724 * nodes are only created for auto-detected hardware or nodes explicitly
725 * created by the user, e.g. via the DCA. However, we should check the
726 * device node is at least vaguely usable, i.e. we have a block of 8 i/o
727 * ports. This prevents attempting to attach to bogus serial ports which
728 * some BIOSs still partially report when they are disabled in the BIOS.
731 asyprobe(dev_info_t
*devi
)
733 return ((asy_get_io_regnum(devi
, NULL
) < 0) ?
734 DDI_PROBE_FAILURE
: DDI_PROBE_DONTCARE
);
738 asyattach(dev_info_t
*devi
, ddi_attach_cmd_t cmd
)
746 char name
[ASY_MINOR_LEN
];
748 static ddi_device_acc_attr_t ioattr
= {
754 instance
= ddi_get_instance(devi
); /* find out which unit */
761 struct asyncline
*async
;
765 return (DDI_SUCCESS
);
767 asy
= ddi_get_soft_state(asy_soft_state
, instance
);
769 return (DDI_FAILURE
);
771 mutex_enter(&asy
->asy_soft_sr
);
772 mutex_enter(&asy
->asy_excl
);
773 mutex_enter(&asy
->asy_excl_hi
);
775 async
= asy
->asy_priv
;
776 /* Disable interrupts */
777 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ ICR
, 0);
778 if (asy_identify_chip(devi
, asy
) != DDI_SUCCESS
) {
779 mutex_exit(&asy
->asy_excl_hi
);
780 mutex_exit(&asy
->asy_excl
);
781 mutex_exit(&asy
->asy_soft_sr
);
782 cmn_err(CE_WARN
, "!Cannot identify UART chip at %p\n",
783 (void *)asy
->asy_ioaddr
);
784 return (DDI_FAILURE
);
786 asy
->asy_flags
&= ~ASY_DDI_SUSPENDED
;
787 if (async
->async_flags
& ASYNC_ISOPEN
) {
788 asy_program(asy
, ASY_INIT
);
789 /* Kick off output */
790 if (async
->async_ocnt
> 0) {
793 mutex_exit(&asy
->asy_excl_hi
);
794 if (async
->async_xmitblk
)
795 freeb(async
->async_xmitblk
);
796 async
->async_xmitblk
= NULL
;
798 mutex_enter(&asy
->asy_excl_hi
);
802 mutex_exit(&asy
->asy_excl_hi
);
803 mutex_exit(&asy
->asy_excl
);
804 mutex_exit(&asy
->asy_soft_sr
);
806 mutex_enter(&asy
->asy_excl
);
807 if (async
->async_flags
& ASYNC_RESUME_BUFCALL
) {
808 async
->async_wbufcid
= bufcall(async
->async_wbufcds
,
809 BPRI_HI
, (void (*)(void *)) async_reioctl
,
810 (void *)(intptr_t)async
->async_common
->asy_unit
);
811 async
->async_flags
&= ~ASYNC_RESUME_BUFCALL
;
813 async_process_suspq(asy
);
814 mutex_exit(&asy
->asy_excl
);
815 return (DDI_SUCCESS
);
818 return (DDI_FAILURE
);
821 ret
= ddi_soft_state_zalloc(asy_soft_state
, instance
);
822 if (ret
!= DDI_SUCCESS
)
823 return (DDI_FAILURE
);
824 asy
= ddi_get_soft_state(asy_soft_state
, instance
);
825 ASSERT(asy
!= NULL
); /* can't fail - we only just allocated it */
826 asy
->asy_unit
= instance
;
827 mutex_enter(&asy_glob_lock
);
828 if (instance
> max_asy_instance
)
829 max_asy_instance
= instance
;
830 mutex_exit(&asy_glob_lock
);
832 regnum
= asy_get_io_regnum(devi
, asy
);
835 ddi_regs_map_setup(devi
, regnum
, (caddr_t
*)&asy
->asy_ioaddr
,
836 0, 0, &ioattr
, &asy
->asy_iohandle
)
838 cmn_err(CE_WARN
, "asy%d: could not map UART registers @ %p",
839 instance
, (void *)asy
->asy_ioaddr
);
841 asy_soft_state_free(asy
);
842 return (DDI_FAILURE
);
845 DEBUGCONT2(ASY_DEBUG_INIT
, "asy%dattach: UART @ %p\n",
846 instance
, (void *)asy
->asy_ioaddr
);
848 mutex_enter(&asy_glob_lock
);
849 if (com_ports
== NULL
) { /* need to initialize com_ports */
850 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY
, devi
, 0,
851 "motherboard-serial-ports", &com_ports
, &num_com_ports
) !=
853 /* Use our built-in COM[1234] values */
854 com_ports
= (int *)standard_com_ports
;
855 num_com_ports
= sizeof (standard_com_ports
) /
856 sizeof (standard_com_ports
[0]);
858 if (num_com_ports
> 10) {
859 /* We run out of single digits for device properties */
862 "More than %d motherboard-serial-ports",
866 mutex_exit(&asy_glob_lock
);
869 * Lookup the i/o address to see if this is a standard COM port
870 * in which case we assign it the correct tty[a-d] to match the
871 * COM port number, or some other i/o address in which case it
872 * will be assigned /dev/term/[0123...] in some rather arbitrary
876 for (i
= 0; i
< num_com_ports
; i
++) {
877 if (asy
->asy_ioaddr
== (uint8_t *)(uintptr_t)com_ports
[i
]) {
878 asy
->asy_com_port
= i
+ 1;
884 * It appears that there was async hardware that on reset
885 * did not clear ICR. Hence when we get to
886 * ddi_get_iblock_cookie below, this hardware would cause
887 * the system to hang if there was input available.
890 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ ICR
, 0x00);
892 /* establish default usage */
893 asy
->asy_mcr
|= RTS
|DTR
; /* do use RTS/DTR after open */
894 asy
->asy_lcr
= STOP1
|BITS8
; /* default to 1 stop 8 bits */
895 asy
->asy_bidx
= B9600
; /* default to 9600 */
897 asy
->asy_msint_cnt
= 0; /* # of times in async_msint */
899 mcr
= 0; /* don't enable until open */
901 if (asy
->asy_com_port
!= 0) {
903 * For motherboard ports, emulate tty eeprom properties.
904 * Actually, we can't tell if a port is motherboard or not,
905 * so for "motherboard ports", read standard DOS COM ports.
907 switch (asy_getproperty(devi
, asy
, "ignore-cd")) {
908 case 0: /* *-ignore-cd=False */
909 DEBUGCONT1(ASY_DEBUG_MODEM
,
910 "asy%dattach: clear ASY_IGNORE_CD\n", instance
);
911 asy
->asy_flags
&= ~ASY_IGNORE_CD
; /* wait for cd */
913 case 1: /* *-ignore-cd=True */
915 default: /* *-ignore-cd not defined */
917 * We set rather silly defaults of soft carrier on
918 * and DTR/RTS raised here because it might be that
919 * one of the motherboard ports is the system console.
921 DEBUGCONT1(ASY_DEBUG_MODEM
,
922 "asy%dattach: set ASY_IGNORE_CD, set RTS & DTR\n",
924 mcr
= asy
->asy_mcr
; /* rts/dtr on */
925 asy
->asy_flags
|= ASY_IGNORE_CD
; /* ignore cd */
929 /* Property for not raising DTR/RTS */
930 switch (asy_getproperty(devi
, asy
, "rts-dtr-off")) {
931 case 0: /* *-rts-dtr-off=False */
932 asy
->asy_flags
|= ASY_RTS_DTR_OFF
; /* OFF */
933 mcr
= asy
->asy_mcr
; /* rts/dtr on */
934 DEBUGCONT1(ASY_DEBUG_MODEM
, "asy%dattach: "
935 "ASY_RTS_DTR_OFF set and DTR & RTS set\n",
938 case 1: /* *-rts-dtr-off=True */
940 default: /* *-rts-dtr-off undefined */
944 /* Parse property for tty modes */
945 asy_parse_mode(devi
, asy
);
947 DEBUGCONT1(ASY_DEBUG_MODEM
,
948 "asy%dattach: clear ASY_IGNORE_CD, clear RTS & DTR\n",
950 asy
->asy_flags
&= ~ASY_IGNORE_CD
; /* wait for cd */
954 * Initialize the port with default settings.
957 asy
->asy_fifo_buf
= 1;
958 asy
->asy_use_fifo
= FIFO_OFF
;
961 * Get icookie for mutexes initialization
963 if ((ddi_get_iblock_cookie(devi
, 0, &asy
->asy_iblock
) !=
965 (ddi_get_soft_iblock_cookie(devi
, DDI_SOFTINT_MED
,
966 &asy
->asy_soft_iblock
) != DDI_SUCCESS
)) {
967 ddi_regs_map_free(&asy
->asy_iohandle
);
969 "asy%d: could not hook interrupt for UART @ %p\n",
970 instance
, (void *)asy
->asy_ioaddr
);
971 asy_soft_state_free(asy
);
972 return (DDI_FAILURE
);
976 * Initialize mutexes before accessing the hardware
978 mutex_init(&asy
->asy_soft_lock
, NULL
, MUTEX_DRIVER
,
979 (void *)asy
->asy_soft_iblock
);
980 mutex_init(&asy
->asy_excl
, NULL
, MUTEX_DRIVER
, NULL
);
981 mutex_init(&asy
->asy_excl_hi
, NULL
, MUTEX_DRIVER
,
982 (void *)asy
->asy_iblock
);
983 mutex_init(&asy
->asy_soft_sr
, NULL
, MUTEX_DRIVER
,
984 (void *)asy
->asy_soft_iblock
);
985 mutex_enter(&asy
->asy_excl
);
986 mutex_enter(&asy
->asy_excl_hi
);
988 if (asy_identify_chip(devi
, asy
) != DDI_SUCCESS
) {
989 mutex_exit(&asy
->asy_excl_hi
);
990 mutex_exit(&asy
->asy_excl
);
991 mutex_destroy(&asy
->asy_soft_lock
);
992 mutex_destroy(&asy
->asy_excl
);
993 mutex_destroy(&asy
->asy_excl_hi
);
994 mutex_destroy(&asy
->asy_soft_sr
);
995 ddi_regs_map_free(&asy
->asy_iohandle
);
996 cmn_err(CE_CONT
, "!Cannot identify UART chip at %p\n",
997 (void *)asy
->asy_ioaddr
);
998 asy_soft_state_free(asy
);
999 return (DDI_FAILURE
);
1002 /* disable all interrupts */
1003 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ ICR
, 0);
1004 /* select baud rate generator */
1005 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
, DLAB
);
1006 /* Set the baud rate to 9600 */
1007 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ (DAT
+DLL
),
1008 asyspdtab
[asy
->asy_bidx
] & 0xff);
1009 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ (DAT
+DLH
),
1010 (asyspdtab
[asy
->asy_bidx
] >> 8) & 0xff);
1011 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
, asy
->asy_lcr
);
1012 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ MCR
, mcr
);
1014 mutex_exit(&asy
->asy_excl_hi
);
1015 mutex_exit(&asy
->asy_excl
);
1018 * Set up the other components of the asycom structure for this port.
1020 asy
->asy_dip
= devi
;
1023 * Install per instance software interrupt handler.
1025 if (ddi_add_softintr(devi
, DDI_SOFTINT_MED
,
1026 &(asy
->asy_softintr_id
), NULL
, 0, asysoftintr
,
1027 (caddr_t
)asy
) != DDI_SUCCESS
) {
1028 mutex_destroy(&asy
->asy_soft_lock
);
1029 mutex_destroy(&asy
->asy_excl
);
1030 mutex_destroy(&asy
->asy_excl_hi
);
1031 ddi_regs_map_free(&asy
->asy_iohandle
);
1033 "Can not set soft interrupt for ASY driver\n");
1034 asy_soft_state_free(asy
);
1035 return (DDI_FAILURE
);
1038 mutex_enter(&asy
->asy_excl
);
1039 mutex_enter(&asy
->asy_excl_hi
);
1042 * Install interrupt handler for this device.
1044 if (ddi_add_intr(devi
, 0, NULL
, 0, asyintr
,
1045 (caddr_t
)asy
) != DDI_SUCCESS
) {
1046 mutex_exit(&asy
->asy_excl_hi
);
1047 mutex_exit(&asy
->asy_excl
);
1048 ddi_remove_softintr(asy
->asy_softintr_id
);
1049 mutex_destroy(&asy
->asy_soft_lock
);
1050 mutex_destroy(&asy
->asy_excl
);
1051 mutex_destroy(&asy
->asy_excl_hi
);
1052 ddi_regs_map_free(&asy
->asy_iohandle
);
1054 "Can not set device interrupt for ASY driver\n");
1055 asy_soft_state_free(asy
);
1056 return (DDI_FAILURE
);
1059 mutex_exit(&asy
->asy_excl_hi
);
1060 mutex_exit(&asy
->asy_excl
);
1062 asyinit(asy
); /* initialize the asyncline structure */
1064 /* create minor device nodes for this device */
1065 if (asy
->asy_com_port
!= 0) {
1067 * For DOS COM ports, add letter suffix so
1068 * devfsadm can create correct link names.
1070 name
[0] = asy
->asy_com_port
+ 'a' - 1;
1074 * asy port which isn't a standard DOS COM
1075 * port gets a numeric name based on instance
1077 (void) snprintf(name
, ASY_MINOR_LEN
, "%d", instance
);
1079 status
= ddi_create_minor_node(devi
, name
, S_IFCHR
, instance
,
1080 asy
->asy_com_port
!= 0 ? DDI_NT_SERIAL_MB
: DDI_NT_SERIAL
, 0);
1081 if (status
== DDI_SUCCESS
) {
1082 (void) strcat(name
, ",cu");
1083 status
= ddi_create_minor_node(devi
, name
, S_IFCHR
,
1085 asy
->asy_com_port
!= 0 ? DDI_NT_SERIAL_MB_DO
:
1086 DDI_NT_SERIAL_DO
, 0);
1089 if (status
!= DDI_SUCCESS
) {
1090 struct asyncline
*async
= asy
->asy_priv
;
1092 ddi_remove_minor_node(devi
, NULL
);
1093 ddi_remove_intr(devi
, 0, asy
->asy_iblock
);
1094 ddi_remove_softintr(asy
->asy_softintr_id
);
1095 mutex_destroy(&asy
->asy_soft_lock
);
1096 mutex_destroy(&asy
->asy_excl
);
1097 mutex_destroy(&asy
->asy_excl_hi
);
1098 cv_destroy(&async
->async_flags_cv
);
1099 ddi_regs_map_free(&asy
->asy_iohandle
);
1100 asy_soft_state_free(asy
);
1101 return (DDI_FAILURE
);
1105 * Fill in the polled I/O structure.
1107 asy
->polledio
.cons_polledio_version
= CONSPOLLEDIO_V0
;
1108 asy
->polledio
.cons_polledio_argument
= (cons_polledio_arg_t
)asy
;
1109 asy
->polledio
.cons_polledio_putchar
= asyputchar
;
1110 asy
->polledio
.cons_polledio_getchar
= asygetchar
;
1111 asy
->polledio
.cons_polledio_ischar
= asyischar
;
1112 asy
->polledio
.cons_polledio_enter
= NULL
;
1113 asy
->polledio
.cons_polledio_exit
= NULL
;
1115 ddi_report_dev(devi
);
1116 DEBUGCONT1(ASY_DEBUG_INIT
, "asy%dattach: done\n", instance
);
1117 return (DDI_SUCCESS
);
1122 asyinfo(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
,
1125 dev_t dev
= (dev_t
)arg
;
1126 int instance
, error
;
1129 instance
= UNIT(dev
);
1132 case DDI_INFO_DEVT2DEVINFO
:
1133 asy
= ddi_get_soft_state(asy_soft_state
, instance
);
1134 if ((asy
== NULL
) || (asy
->asy_dip
== NULL
))
1135 error
= DDI_FAILURE
;
1137 *result
= (void *) asy
->asy_dip
;
1138 error
= DDI_SUCCESS
;
1141 case DDI_INFO_DEVT2INSTANCE
:
1142 *result
= (void *)(intptr_t)instance
;
1143 error
= DDI_SUCCESS
;
1146 error
= DDI_FAILURE
;
1151 /* asy_getproperty -- walk through all name variants until we find a match */
1154 asy_getproperty(dev_info_t
*devi
, struct asycom
*asy
, const char *property
)
1158 char letter
= asy
->asy_com_port
+ 'a' - 1; /* for ttya */
1159 char number
= asy
->asy_com_port
+ '0'; /* for COM1 */
1163 /* Property for ignoring DCD */
1164 (void) sprintf(name
, "tty%c-%s", letter
, property
);
1166 ret
= GET_PROP(devi
, name
, DDI_PROP_CANSLEEP
, val
, &len
);
1167 if (ret
!= DDI_PROP_SUCCESS
) {
1168 (void) sprintf(name
, "com%c-%s", number
, property
);
1170 ret
= GET_PROP(devi
, name
, DDI_PROP_CANSLEEP
, val
, &len
);
1172 if (ret
!= DDI_PROP_SUCCESS
) {
1173 (void) sprintf(name
, "tty0%c-%s", number
, property
);
1175 ret
= GET_PROP(devi
, name
, DDI_PROP_CANSLEEP
, val
, &len
);
1177 if (ret
!= DDI_PROP_SUCCESS
) {
1178 (void) sprintf(name
, "port-%c-%s", letter
, property
);
1180 ret
= GET_PROP(devi
, name
, DDI_PROP_CANSLEEP
, val
, &len
);
1182 if (ret
!= DDI_PROP_SUCCESS
)
1183 return (-1); /* property non-existant */
1184 if (val
[0] == 'f' || val
[0] == 'F' || val
[0] == '0')
1185 return (0); /* property false/0 */
1186 return (1); /* property true/!0 */
1189 /* asy_soft_state_free - local wrapper for ddi_soft_state_free(9F) */
1192 asy_soft_state_free(struct asycom
*asy
)
1194 mutex_enter(&asy_glob_lock
);
1195 /* If we were the max_asy_instance, work out new value */
1196 if (asy
->asy_unit
== max_asy_instance
) {
1197 while (--max_asy_instance
>= 0) {
1198 if (ddi_get_soft_state(asy_soft_state
,
1199 max_asy_instance
) != NULL
)
1203 mutex_exit(&asy_glob_lock
);
1205 if (asy
->asy_priv
!= NULL
) {
1206 kmem_free(asy
->asy_priv
, sizeof (struct asyncline
));
1207 asy
->asy_priv
= NULL
;
1209 ddi_soft_state_free(asy_soft_state
, asy
->asy_unit
);
1213 asy_hw_name(struct asycom
*asy
)
1215 switch (asy
->asy_hwtype
) {
1217 return ("8250A/16450");
1227 DEBUGNOTE2(ASY_DEBUG_INIT
,
1228 "asy%d: asy_hw_name: unknown asy_hwtype: %d",
1229 asy
->asy_unit
, asy
->asy_hwtype
);
1235 asy_identify_chip(dev_info_t
*devi
, struct asycom
*asy
)
1243 /* Check scratch register works. */
1245 /* write to scratch register */
1246 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ SCR
, SCRTEST
);
1247 /* make sure that pattern doesn't just linger on the bus */
1248 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ FIFOR
, 0x00);
1249 /* read data back from scratch register */
1250 ret
= ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ SCR
);
1251 if (ret
!= SCRTEST
) {
1253 * Scratch register not working.
1254 * Probably not an async chip.
1255 * 8250 and 8250B don't have scratch registers,
1256 * but only worked in ancient PC XT's anyway.
1258 cmn_err(CE_CONT
, "!asy%d: UART @ %p "
1259 "scratch register: expected 0x5a, got 0x%02x\n",
1260 asy
->asy_unit
, (void *)asy
->asy_ioaddr
, ret
);
1261 return (DDI_FAILURE
);
1265 * Use 16550 fifo reset sequence specified in NS application
1266 * note. Disable fifos until chip is initialized.
1268 ddi_put8(asy
->asy_iohandle
,
1269 asy
->asy_ioaddr
+ FIFOR
, 0x00); /* clear */
1270 ddi_put8(asy
->asy_iohandle
,
1271 asy
->asy_ioaddr
+ FIFOR
, FIFO_ON
); /* enable */
1272 ddi_put8(asy
->asy_iohandle
,
1273 asy
->asy_ioaddr
+ FIFOR
, FIFO_ON
| FIFORXFLSH
);
1275 if (asymaxchip
>= ASY16650
&& asy_scr_test
) {
1277 * Reset 16650 enhanced regs also, in case we have one of these
1279 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
,
1281 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ EFR
,
1283 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
,
1288 * See what sort of FIFO we have.
1289 * Try enabling it and see what chip makes of this.
1293 asy
->asy_hwtype
= asymaxchip
; /* just for asy_reset_fifo() */
1294 if (asymaxchip
>= ASY16550A
)
1296 FIFO_ON
| FIFODMA
| (asy_trig_level
& 0xff);
1297 if (asymaxchip
>= ASY16650
)
1298 asy
->asy_fifor
|= FIFOEXTRA1
| FIFOEXTRA2
;
1300 asy_reset_fifo(asy
, FIFOTXFLSH
| FIFORXFLSH
);
1302 mcr
= ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ MCR
);
1303 ret
= ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ ISR
);
1304 DEBUGCONT4(ASY_DEBUG_CHIP
,
1305 "asy%d: probe fifo FIFOR=0x%02x ISR=0x%02x MCR=0x%02x\n",
1306 asy
->asy_unit
, asy
->asy_fifor
| FIFOTXFLSH
| FIFORXFLSH
,
1308 switch (ret
& 0xf0) {
1310 hwtype
= ASY16550
; /* 16550 with broken FIFO */
1315 asy
->asy_fifo_buf
= 16;
1316 asy
->asy_use_fifo
= FIFO_ON
;
1317 asy
->asy_fifor
&= ~(FIFOEXTRA1
| FIFOEXTRA2
);
1321 asy
->asy_fifo_buf
= 32;
1322 asy
->asy_use_fifo
= FIFO_ON
;
1323 asy
->asy_fifor
&= ~(FIFOEXTRA1
);
1327 * Note we get 0xff if chip didn't return us anything,
1328 * e.g. if there's no chip there.
1331 cmn_err(CE_CONT
, "asy%d: UART @ %p "
1332 "interrupt register: got 0xff\n",
1333 asy
->asy_unit
, (void *)asy
->asy_ioaddr
);
1334 return (DDI_FAILURE
);
1339 asy
->asy_fifo_buf
= 64;
1340 asy
->asy_use_fifo
= FIFO_ON
;
1343 hwtype
= ASY8250A
; /* No FIFO */
1347 if (hwtype
> asymaxchip
) {
1348 cmn_err(CE_CONT
, "asy%d: UART @ %p "
1349 "unexpected probe result: "
1350 "FIFOR=0x%02x ISR=0x%02x MCR=0x%02x\n",
1351 asy
->asy_unit
, (void *)asy
->asy_ioaddr
,
1352 asy
->asy_fifor
| FIFOTXFLSH
| FIFORXFLSH
, ret
, mcr
);
1353 return (DDI_FAILURE
);
1357 * Now reset the FIFO operation appropriate for the chip type.
1358 * Note we must call asy_reset_fifo() before any possible
1359 * downgrade of the asy->asy_hwtype, or it may not disable
1360 * the more advanced features we specifically want downgraded.
1362 asy_reset_fifo(asy
, 0);
1363 asy
->asy_hwtype
= hwtype
;
1366 * Check for Exar/Startech ST16C650, which will still look like a
1367 * 16550A until we enable its enhanced mode.
1369 if (asy
->asy_hwtype
== ASY16550A
&& asymaxchip
>= ASY16650
&&
1371 /* Enable enhanced mode register access */
1372 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
,
1374 /* zero scratch register (not scratch register if enhanced) */
1375 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ SCR
, 0);
1376 /* Disable enhanced mode register access */
1377 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
,
1379 /* read back scratch register */
1380 ret
= ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ SCR
);
1381 if (ret
== SCRTEST
) {
1382 /* looks like we have an ST16650 -- enable it */
1383 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
,
1385 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ EFR
,
1387 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
,
1389 asy
->asy_hwtype
= ASY16650
;
1390 asy
->asy_fifo_buf
= 32;
1391 asy
->asy_fifor
|= 0x10; /* 24 byte txfifo trigger */
1392 asy_reset_fifo(asy
, 0);
1397 * If we think we might have a FIFO larger than 16 characters,
1398 * measure FIFO size and check it against expected.
1400 if (asy_fifo_test
> 0 &&
1401 !(asy
->asy_flags2
& ASY2_NO_LOOPBACK
) &&
1402 (asy
->asy_fifo_buf
> 16 ||
1403 (asy_fifo_test
> 1 && asy
->asy_use_fifo
== FIFO_ON
) ||
1404 ASY_DEBUG(ASY_DEBUG_CHIP
))) {
1407 /* Set baud rate to 57600 (fairly arbitrary choice) */
1408 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
,
1410 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ DAT
,
1411 asyspdtab
[B57600
] & 0xff);
1412 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ ICR
,
1413 (asyspdtab
[B57600
] >> 8) & 0xff);
1414 /* Set 8 bits, 1 stop bit */
1415 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
,
1417 /* Set loopback mode */
1418 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ MCR
,
1419 DTR
| RTS
| ASY_LOOP
| OUT1
| OUT2
);
1422 for (i
= 0; i
< asy
->asy_fifo_buf
* 2; i
++) {
1423 ddi_put8(asy
->asy_iohandle
,
1424 asy
->asy_ioaddr
+ DAT
, i
);
1427 * Now there's an interesting question here about which
1428 * FIFO we're testing the size of, RX or TX. We just
1429 * filled the TX FIFO much faster than it can empty,
1430 * although it is possible one or two characters may
1431 * have gone from it to the TX shift register.
1432 * We wait for enough time for all the characters to
1433 * move into the RX FIFO and any excess characters to
1434 * have been lost, and then read all the RX FIFO. So
1435 * the answer we finally get will be the size which is
1436 * the MIN(RX FIFO,(TX FIFO + 1 or 2)). The critical
1437 * one is actually the TX FIFO, because if we overfill
1438 * it in normal operation, the excess characters are
1439 * lost with no warning.
1442 * Wait for characters to move into RX FIFO.
1443 * In theory, 200 * asy->asy_fifo_buf * 2 should be
1444 * enough. However, in practice it isn't always, so we
1445 * increase to 400 so some slow 16550A's finish, and we
1446 * increase to 3 so we spot more characters coming back
1447 * than we sent, in case that should ever happen.
1449 delay(drv_usectohz(400 * asy
->asy_fifo_buf
* 3));
1451 /* Now see how many characters we can read back */
1452 for (i
= 0; i
< asy
->asy_fifo_buf
* 3; i
++) {
1453 ret
= ddi_get8(asy
->asy_iohandle
,
1454 asy
->asy_ioaddr
+ LSR
);
1456 break; /* FIFO emptied */
1457 (void) ddi_get8(asy
->asy_iohandle
,
1458 asy
->asy_ioaddr
+ DAT
); /* lose another */
1461 DEBUGCONT3(ASY_DEBUG_CHIP
,
1462 "asy%d FIFO size: expected=%d, measured=%d\n",
1463 asy
->asy_unit
, asy
->asy_fifo_buf
, i
);
1465 hwtype
= asy
->asy_hwtype
;
1466 if (i
< asy
->asy_fifo_buf
) {
1468 * FIFO is somewhat smaller than we anticipated.
1469 * If we have 16 characters usable, then this
1470 * UART will probably work well enough in
1471 * 16550A mode. If less than 16 characters,
1472 * then we'd better not use it at all.
1473 * UARTs with busted FIFOs do crop up.
1475 if (i
>= 16 && asy
->asy_fifo_buf
>= 16) {
1476 /* fall back to a 16550A */
1478 asy
->asy_fifo_buf
= 16;
1479 asy
->asy_fifor
&= ~(FIFOEXTRA1
| FIFOEXTRA2
);
1481 /* fall back to no FIFO at all */
1483 asy
->asy_fifo_buf
= 1;
1484 asy
->asy_use_fifo
= FIFO_OFF
;
1486 ~(FIFO_ON
| FIFOEXTRA1
| FIFOEXTRA2
);
1490 * We will need to reprogram the FIFO if we changed
1491 * our mind about how to drive it above, and in any
1492 * case, it would be a good idea to flush any garbage
1493 * out incase the loopback test left anything behind.
1494 * Again as earlier above, we must call asy_reset_fifo()
1495 * before any possible downgrade of asy->asy_hwtype.
1497 if (asy
->asy_hwtype
>= ASY16650
&& hwtype
< ASY16650
) {
1498 /* Disable 16650 enhanced mode */
1499 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
,
1501 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ EFR
,
1503 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
,
1506 asy_reset_fifo(asy
, FIFOTXFLSH
| FIFORXFLSH
);
1507 asy
->asy_hwtype
= hwtype
;
1509 /* Clear loopback mode and restore DTR/RTS */
1510 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ MCR
, mcr
);
1513 DEBUGNOTE3(ASY_DEBUG_CHIP
, "asy%d %s @ %p",
1514 asy
->asy_unit
, asy_hw_name(asy
), (void *)asy
->asy_ioaddr
);
1516 /* Make UART type visible in device tree for prtconf, etc */
1517 dev
= makedevice(DDI_MAJOR_T_UNKNOWN
, asy
->asy_unit
);
1518 (void) ddi_prop_update_string(dev
, devi
, "uart", asy_hw_name(asy
));
1520 if (asy
->asy_hwtype
== ASY16550
) /* for broken 16550's, */
1521 asy
->asy_hwtype
= ASY8250A
; /* drive them as 8250A */
1523 return (DDI_SUCCESS
);
1527 * asyinit() initializes the TTY protocol-private data for this channel
1528 * before enabling the interrupts.
1531 asyinit(struct asycom
*asy
)
1533 struct asyncline
*async
;
1535 asy
->asy_priv
= kmem_zalloc(sizeof (struct asyncline
), KM_SLEEP
);
1536 async
= asy
->asy_priv
;
1537 mutex_enter(&asy
->asy_excl
);
1538 async
->async_common
= asy
;
1539 cv_init(&async
->async_flags_cv
, NULL
, CV_DRIVER
, NULL
);
1540 mutex_exit(&asy
->asy_excl
);
1545 asyopen(queue_t
*rq
, dev_t
*dev
, int flag
, int sflag
, cred_t
*cr
)
1548 struct asyncline
*async
;
1552 struct termios
*termiosp
;
1555 DEBUGCONT1(ASY_DEBUG_CLOSE
, "asy%dopen\n", unit
);
1556 asy
= ddi_get_soft_state(asy_soft_state
, unit
);
1558 return (ENXIO
); /* unit not configured */
1559 async
= asy
->asy_priv
;
1560 mutex_enter(&asy
->asy_excl
);
1563 mutex_enter(&asy
->asy_excl_hi
);
1566 * Block waiting for carrier to come up, unless this is a no-delay open.
1568 if (!(async
->async_flags
& ASYNC_ISOPEN
)) {
1570 * Set the default termios settings (cflag).
1571 * Others are set in ldterm.
1573 mutex_exit(&asy
->asy_excl_hi
);
1575 if (ddi_getlongprop(DDI_DEV_T_ANY
, ddi_root_node(),
1577 (caddr_t
)&termiosp
, &len
) == DDI_PROP_SUCCESS
&&
1578 len
== sizeof (struct termios
)) {
1579 async
->async_ttycommon
.t_cflag
= termiosp
->c_cflag
;
1580 kmem_free(termiosp
, len
);
1583 "asy: couldn't get ttymodes property!");
1584 mutex_enter(&asy
->asy_excl_hi
);
1586 /* eeprom mode support - respect properties */
1588 async
->async_ttycommon
.t_cflag
= asy
->asy_cflag
;
1590 async
->async_ttycommon
.t_iflag
= 0;
1591 async
->async_ttycommon
.t_iocpending
= NULL
;
1592 async
->async_ttycommon
.t_size
.ws_row
= 0;
1593 async
->async_ttycommon
.t_size
.ws_col
= 0;
1594 async
->async_ttycommon
.t_size
.ws_xpixel
= 0;
1595 async
->async_ttycommon
.t_size
.ws_ypixel
= 0;
1596 async
->async_dev
= *dev
;
1597 async
->async_wbufcid
= 0;
1599 async
->async_startc
= CSTART
;
1600 async
->async_stopc
= CSTOP
;
1601 asy_program(asy
, ASY_INIT
);
1603 if ((async
->async_ttycommon
.t_flags
& TS_XCLUDE
) &&
1604 secpolicy_excl_open(cr
) != 0) {
1605 mutex_exit(&asy
->asy_excl_hi
);
1606 mutex_exit(&asy
->asy_excl
);
1608 } else if ((*dev
& OUTLINE
) && !(async
->async_flags
& ASYNC_OUT
)) {
1609 mutex_exit(&asy
->asy_excl_hi
);
1610 mutex_exit(&asy
->asy_excl
);
1615 async
->async_flags
|= ASYNC_OUT
;
1617 /* Raise DTR on every open, but delay if it was just lowered. */
1618 while (async
->async_flags
& ASYNC_DTR_DELAY
) {
1619 DEBUGCONT1(ASY_DEBUG_MODEM
,
1620 "asy%dopen: waiting for the ASYNC_DTR_DELAY to be clear\n",
1622 mutex_exit(&asy
->asy_excl_hi
);
1623 if (cv_wait_sig(&async
->async_flags_cv
,
1624 &asy
->asy_excl
) == 0) {
1625 DEBUGCONT1(ASY_DEBUG_MODEM
,
1626 "asy%dopen: interrupted by signal, exiting\n",
1628 mutex_exit(&asy
->asy_excl
);
1631 mutex_enter(&asy
->asy_excl_hi
);
1634 mcr
= ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ MCR
);
1635 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ MCR
,
1636 mcr
|(asy
->asy_mcr
&DTR
));
1638 DEBUGCONT3(ASY_DEBUG_INIT
,
1639 "asy%dopen: \"Raise DTR on every open\": make mcr = %x, "
1640 "make TS_SOFTCAR = %s\n",
1641 unit
, mcr
|(asy
->asy_mcr
&DTR
),
1642 (asy
->asy_flags
& ASY_IGNORE_CD
) ? "ON" : "OFF");
1644 if (asy
->asy_flags
& ASY_IGNORE_CD
) {
1645 DEBUGCONT1(ASY_DEBUG_MODEM
,
1646 "asy%dopen: ASY_IGNORE_CD set, set TS_SOFTCAR\n",
1648 async
->async_ttycommon
.t_flags
|= TS_SOFTCAR
;
1651 async
->async_ttycommon
.t_flags
&= ~TS_SOFTCAR
;
1656 asy
->asy_msr
= ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ MSR
);
1657 DEBUGCONT3(ASY_DEBUG_INIT
, "asy%dopen: TS_SOFTCAR is %s, "
1658 "MSR & DCD is %s\n",
1660 (async
->async_ttycommon
.t_flags
& TS_SOFTCAR
) ? "set" : "clear",
1661 (asy
->asy_msr
& DCD
) ? "set" : "clear");
1663 if (asy
->asy_msr
& DCD
)
1664 async
->async_flags
|= ASYNC_CARR_ON
;
1666 async
->async_flags
&= ~ASYNC_CARR_ON
;
1667 mutex_exit(&asy
->asy_excl_hi
);
1670 * If FNDELAY and FNONBLOCK are clear, block until carrier up.
1671 * Quit on interrupt.
1673 if (!(flag
& (FNDELAY
|FNONBLOCK
)) &&
1674 !(async
->async_ttycommon
.t_cflag
& CLOCAL
)) {
1675 if ((!(async
->async_flags
& (ASYNC_CARR_ON
|ASYNC_OUT
)) &&
1676 !(async
->async_ttycommon
.t_flags
& TS_SOFTCAR
)) ||
1677 ((async
->async_flags
& ASYNC_OUT
) &&
1678 !(*dev
& OUTLINE
))) {
1679 async
->async_flags
|= ASYNC_WOPEN
;
1680 if (cv_wait_sig(&async
->async_flags_cv
,
1681 &asy
->asy_excl
) == B_FALSE
) {
1682 async
->async_flags
&= ~ASYNC_WOPEN
;
1683 mutex_exit(&asy
->asy_excl
);
1686 async
->async_flags
&= ~ASYNC_WOPEN
;
1689 } else if ((async
->async_flags
& ASYNC_OUT
) && !(*dev
& OUTLINE
)) {
1690 mutex_exit(&asy
->asy_excl
);
1694 async
->async_ttycommon
.t_readq
= rq
;
1695 async
->async_ttycommon
.t_writeq
= WR(rq
);
1696 rq
->q_ptr
= WR(rq
)->q_ptr
= (caddr_t
)async
;
1697 mutex_exit(&asy
->asy_excl
);
1699 * Caution here -- qprocson sets the pointers that are used by canput
1700 * called by async_softint. ASYNC_ISOPEN must *not* be set until those
1701 * pointers are valid.
1704 async
->async_flags
|= ASYNC_ISOPEN
;
1705 async
->async_polltid
= 0;
1706 DEBUGCONT1(ASY_DEBUG_INIT
, "asy%dopen: done\n", unit
);
1711 async_progress_check(void *arg
)
1713 struct asyncline
*async
= arg
;
1714 struct asycom
*asy
= async
->async_common
;
1718 * We define "progress" as either waiting on a timed break or delay, or
1719 * having had at least one transmitter interrupt. If none of these are
1720 * true, then just terminate the output and wake up that close thread.
1722 mutex_enter(&asy
->asy_excl
);
1723 mutex_enter(&asy
->asy_excl_hi
);
1724 if (!(async
->async_flags
& (ASYNC_BREAK
|ASYNC_DELAY
|ASYNC_PROGRESS
))) {
1725 async
->async_ocnt
= 0;
1726 async
->async_flags
&= ~ASYNC_BUSY
;
1727 async
->async_timer
= 0;
1728 bp
= async
->async_xmitblk
;
1729 async
->async_xmitblk
= NULL
;
1730 mutex_exit(&asy
->asy_excl_hi
);
1734 * Since this timer is running, we know that we're in exit(2).
1735 * That means that the user can't possibly be waiting on any
1736 * valid ioctl(2) completion anymore, and we should just flush
1739 flushq(async
->async_ttycommon
.t_writeq
, FLUSHALL
);
1740 cv_broadcast(&async
->async_flags_cv
);
1742 async
->async_flags
&= ~ASYNC_PROGRESS
;
1743 async
->async_timer
= timeout(async_progress_check
, async
,
1744 drv_usectohz(asy_drain_check
));
1745 mutex_exit(&asy
->asy_excl_hi
);
1747 mutex_exit(&asy
->asy_excl
);
1751 * Release DTR so that asyopen() can raise it.
1754 async_dtr_free(struct asyncline
*async
)
1756 struct asycom
*asy
= async
->async_common
;
1758 DEBUGCONT0(ASY_DEBUG_MODEM
,
1759 "async_dtr_free, clearing ASYNC_DTR_DELAY\n");
1760 mutex_enter(&asy
->asy_excl
);
1761 async
->async_flags
&= ~ASYNC_DTR_DELAY
;
1762 async
->async_dtrtid
= 0;
1763 cv_broadcast(&async
->async_flags_cv
);
1764 mutex_exit(&asy
->asy_excl
);
1772 asyclose(queue_t
*q
, int flag
, cred_t
*credp
)
1774 struct asyncline
*async
;
1781 async
= (struct asyncline
*)q
->q_ptr
;
1782 ASSERT(async
!= NULL
);
1784 instance
= UNIT(async
->async_dev
);
1785 DEBUGCONT1(ASY_DEBUG_CLOSE
, "asy%dclose\n", instance
);
1787 asy
= async
->async_common
;
1789 mutex_enter(&asy
->asy_excl
);
1790 async
->async_flags
|= ASYNC_CLOSING
;
1793 * Turn off PPS handling early to avoid events occuring during
1794 * close. Also reset the DCD edge monitoring bit.
1796 mutex_enter(&asy
->asy_excl_hi
);
1797 asy
->asy_flags
&= ~(ASY_PPS
| ASY_PPS_EDGE
);
1798 mutex_exit(&asy
->asy_excl_hi
);
1801 * There are two flavors of break -- timed (M_BREAK or TCSBRK) and
1802 * untimed (TIOCSBRK). For the timed case, these are enqueued on our
1803 * write queue and there's a timer running, so we don't have to worry
1804 * about them. For the untimed case, though, the user obviously made a
1805 * mistake, because these are handled immediately. We'll terminate the
1806 * break now and honor their implicit request by discarding the rest of
1809 if (async
->async_flags
& ASYNC_OUT_SUSPEND
) {
1810 if (async
->async_utbrktid
!= 0) {
1811 (void) untimeout(async
->async_utbrktid
);
1812 async
->async_utbrktid
= 0;
1814 mutex_enter(&asy
->asy_excl_hi
);
1815 lcr
= ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
);
1816 ddi_put8(asy
->asy_iohandle
,
1817 asy
->asy_ioaddr
+ LCR
, (lcr
& ~SETBREAK
));
1818 mutex_exit(&asy
->asy_excl_hi
);
1819 async
->async_flags
&= ~ASYNC_OUT_SUSPEND
;
1824 * If the user told us not to delay the close ("non-blocking"), then
1825 * don't bother trying to drain.
1827 * If the user did M_STOP (ASYNC_STOPPED), there's no hope of ever
1828 * getting an M_START (since these messages aren't enqueued), and the
1829 * only other way to clear the stop condition is by loss of DCD, which
1830 * would discard the queue data. Thus, we drop the output data if
1831 * ASYNC_STOPPED is set.
1833 if ((flag
& (FNDELAY
|FNONBLOCK
)) ||
1834 (async
->async_flags
& ASYNC_STOPPED
)) {
1839 * If there's any pending output, then we have to try to drain it.
1840 * There are two main cases to be handled:
1841 * - called by close(2): need to drain until done or until
1842 * a signal is received. No timeout.
1843 * - called by exit(2): need to drain while making progress
1844 * or until a timeout occurs. No signals.
1846 * If we can't rely on receiving a signal to get us out of a hung
1847 * session, then we have to use a timer. In this case, we set a timer
1848 * to check for progress in sending the output data -- all that we ask
1849 * (at each interval) is that there's been some progress made. Since
1850 * the interrupt routine grabs buffers from the write queue, we can't
1851 * trust changes in async_ocnt. Instead, we use a progress flag.
1853 * Note that loss of carrier will cause the output queue to be flushed,
1854 * and we'll wake up again and finish normally.
1856 if (!ddi_can_receive_sig() && asy_drain_check
!= 0) {
1857 async
->async_flags
&= ~ASYNC_PROGRESS
;
1858 async
->async_timer
= timeout(async_progress_check
, async
,
1859 drv_usectohz(asy_drain_check
));
1861 while (async
->async_ocnt
> 0 ||
1862 async
->async_ttycommon
.t_writeq
->q_first
!= NULL
||
1863 (async
->async_flags
& (ASYNC_BUSY
|ASYNC_BREAK
|ASYNC_DELAY
))) {
1864 if (cv_wait_sig(&async
->async_flags_cv
, &asy
->asy_excl
) == 0)
1867 if (async
->async_timer
!= 0) {
1868 (void) untimeout(async
->async_timer
);
1869 async
->async_timer
= 0;
1873 async
->async_ocnt
= 0;
1874 if (async
->async_xmitblk
!= NULL
)
1875 freeb(async
->async_xmitblk
);
1876 async
->async_xmitblk
= NULL
;
1879 * If line has HUPCL set or is incompletely opened fix up the modem
1882 DEBUGCONT1(ASY_DEBUG_MODEM
, "asy%dclose: next check HUPCL flag\n",
1884 mutex_enter(&asy
->asy_excl_hi
);
1885 if ((async
->async_ttycommon
.t_cflag
& HUPCL
) ||
1886 (async
->async_flags
& ASYNC_WOPEN
)) {
1887 DEBUGCONT3(ASY_DEBUG_MODEM
,
1888 "asy%dclose: HUPCL flag = %x, ASYNC_WOPEN flag = %x\n",
1890 async
->async_ttycommon
.t_cflag
& HUPCL
,
1891 async
->async_ttycommon
.t_cflag
& ASYNC_WOPEN
);
1892 async
->async_flags
|= ASYNC_DTR_DELAY
;
1894 /* turn off DTR, RTS but NOT interrupt to 386 */
1895 if (asy
->asy_flags
& (ASY_IGNORE_CD
|ASY_RTS_DTR_OFF
)) {
1896 DEBUGCONT3(ASY_DEBUG_MODEM
,
1897 "asy%dclose: ASY_IGNORE_CD flag = %x, "
1898 "ASY_RTS_DTR_OFF flag = %x\n",
1900 asy
->asy_flags
& ASY_IGNORE_CD
,
1901 asy
->asy_flags
& ASY_RTS_DTR_OFF
);
1903 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ MCR
,
1906 DEBUGCONT1(ASY_DEBUG_MODEM
,
1907 "asy%dclose: Dropping DTR and RTS\n", instance
);
1908 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ MCR
,
1911 async
->async_dtrtid
=
1912 timeout((void (*)())async_dtr_free
,
1913 (caddr_t
)async
, drv_usectohz(asy_min_dtr_low
));
1916 * If nobody's using it now, turn off receiver interrupts.
1918 if ((async
->async_flags
& (ASYNC_WOPEN
|ASYNC_ISOPEN
)) == 0) {
1919 icr
= ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ ICR
);
1920 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ ICR
,
1923 mutex_exit(&asy
->asy_excl_hi
);
1925 ttycommon_close(&async
->async_ttycommon
);
1928 * Cancel outstanding "bufcall" request.
1930 if (async
->async_wbufcid
!= 0) {
1931 unbufcall(async
->async_wbufcid
);
1932 async
->async_wbufcid
= 0;
1935 /* Note that qprocsoff can't be done until after interrupts are off */
1937 q
->q_ptr
= WR(q
)->q_ptr
= NULL
;
1938 async
->async_ttycommon
.t_readq
= NULL
;
1939 async
->async_ttycommon
.t_writeq
= NULL
;
1942 * Clear out device state, except persistant device property flags.
1944 async
->async_flags
&= (ASYNC_DTR_DELAY
|ASY_RTS_DTR_OFF
);
1945 cv_broadcast(&async
->async_flags_cv
);
1946 mutex_exit(&asy
->asy_excl
);
1948 DEBUGCONT1(ASY_DEBUG_CLOSE
, "asy%dclose: done\n", instance
);
1953 asy_isbusy(struct asycom
*asy
)
1955 struct asyncline
*async
;
1957 DEBUGCONT0(ASY_DEBUG_EOT
, "asy_isbusy\n");
1958 async
= asy
->asy_priv
;
1959 ASSERT(mutex_owned(&asy
->asy_excl
));
1960 ASSERT(mutex_owned(&asy
->asy_excl_hi
));
1962 * XXXX this should be recoded
1964 return ((async
->async_ocnt
> 0) ||
1965 ((ddi_get8(asy
->asy_iohandle
,
1966 asy
->asy_ioaddr
+ LSR
) & (XSRE
|XHRE
)) == 0));
1970 asy_waiteot(struct asycom
*asy
)
1973 * Wait for the current transmission block and the
1974 * current fifo data to transmit. Once this is done
1977 DEBUGCONT0(ASY_DEBUG_EOT
, "asy_waiteot\n");
1978 ASSERT(mutex_owned(&asy
->asy_excl
));
1979 ASSERT(mutex_owned(&asy
->asy_excl_hi
));
1980 while (asy_isbusy(asy
)) {
1981 mutex_exit(&asy
->asy_excl_hi
);
1982 mutex_exit(&asy
->asy_excl
);
1983 drv_usecwait(10000); /* wait .01 */
1984 mutex_enter(&asy
->asy_excl
);
1985 mutex_enter(&asy
->asy_excl_hi
);
1989 /* asy_reset_fifo -- flush fifos and [re]program fifo control register */
1991 asy_reset_fifo(struct asycom
*asy
, uchar_t flush
)
1995 /* On a 16750, we have to set DLAB in order to set FIFOEXTRA. */
1997 if (asy
->asy_hwtype
>= ASY16750
) {
1998 lcr
= ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
);
1999 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
,
2003 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ FIFOR
,
2004 asy
->asy_fifor
| flush
);
2008 if (asy
->asy_hwtype
>= ASY16750
) {
2009 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
, lcr
);
2014 * Program the ASY port. Most of the async operation is based on the values
2015 * of 'c_iflag' and 'c_cflag'.
2018 #define BAUDINDEX(cflg) (((cflg) & CBAUDEXT) ? \
2019 (((cflg) & CBAUD) + CBAUD + 1) : ((cflg) & CBAUD))
2022 asy_program(struct asycom
*asy
, int mode
)
2024 struct asyncline
*async
;
2025 int baudrate
, c_flag
;
2033 ASSERT(mutex_owned(&asy
->asy_excl
));
2034 ASSERT(mutex_owned(&asy
->asy_excl_hi
));
2036 async
= asy
->asy_priv
;
2038 instance
= UNIT(async
->async_dev
);
2039 DEBUGCONT2(ASY_DEBUG_PROCS
,
2040 "asy%d_program: mode = 0x%08X, enter\n", instance
, mode
);
2043 baudrate
= BAUDINDEX(async
->async_ttycommon
.t_cflag
);
2045 async
->async_ttycommon
.t_cflag
&= ~(CIBAUD
);
2047 if (baudrate
> CBAUD
) {
2048 async
->async_ttycommon
.t_cflag
|= CIBAUDEXT
;
2049 async
->async_ttycommon
.t_cflag
|=
2050 (((baudrate
- CBAUD
- 1) << IBSHIFT
) & CIBAUD
);
2052 async
->async_ttycommon
.t_cflag
&= ~CIBAUDEXT
;
2053 async
->async_ttycommon
.t_cflag
|=
2054 ((baudrate
<< IBSHIFT
) & CIBAUD
);
2057 c_flag
= async
->async_ttycommon
.t_cflag
&
2058 (CLOCAL
|CREAD
|CSTOPB
|CSIZE
|PARENB
|PARODD
|CBAUD
|CBAUDEXT
);
2060 /* disable interrupts */
2061 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ ICR
, 0);
2063 ocflags
= asy
->asy_ocflag
;
2065 /* flush/reset the status registers */
2066 (void) ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ ISR
);
2067 (void) ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LSR
);
2068 asy
->asy_msr
= flush_reg
= ddi_get8(asy
->asy_iohandle
,
2069 asy
->asy_ioaddr
+ MSR
);
2071 * The device is programmed in the open sequence, if we
2072 * have to hardware handshake, then this is a good time
2073 * to check if the device can receive any data.
2076 if ((CRTSCTS
& async
->async_ttycommon
.t_cflag
) && !(flush_reg
& CTS
)) {
2077 async_flowcontrol_hw_output(asy
, FLOW_STOP
);
2080 * We can not use async_flowcontrol_hw_output(asy, FLOW_START)
2081 * here, because if CRTSCTS is clear, we need clear
2082 * ASYNC_HW_OUT_FLW bit.
2084 async
->async_flags
&= ~ASYNC_HW_OUT_FLW
;
2088 * If IXON is not set, clear ASYNC_SW_OUT_FLW;
2089 * If IXON is set, no matter what IXON flag is before this
2090 * function call to asy_program,
2091 * we will use the old ASYNC_SW_OUT_FLW status.
2092 * Because of handling IXON in the driver, we also should re-calculate
2093 * the value of ASYNC_OUT_FLW_RESUME bit, but in fact,
2094 * the TCSET* commands which call asy_program
2095 * are put into the write queue, so there is no output needed to
2096 * be resumed at this point.
2098 if (!(IXON
& async
->async_ttycommon
.t_iflag
))
2099 async
->async_flags
&= ~ASYNC_SW_OUT_FLW
;
2101 /* manually flush receive buffer or fifo (workaround for buggy fifos) */
2102 if (mode
== ASY_INIT
)
2103 if (asy
->asy_use_fifo
== FIFO_ON
) {
2104 for (flush_reg
= asy
->asy_fifo_buf
; flush_reg
-- > 0; ) {
2105 (void) ddi_get8(asy
->asy_iohandle
,
2106 asy
->asy_ioaddr
+ DAT
);
2109 flush_reg
= ddi_get8(asy
->asy_iohandle
,
2110 asy
->asy_ioaddr
+ DAT
);
2113 if (ocflags
!= (c_flag
& ~CLOCAL
) || mode
== ASY_INIT
) {
2114 /* Set line control */
2115 lcr
= ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
);
2116 lcr
&= ~(WLS0
|WLS1
|STB
|PEN
|EPS
);
2118 if (c_flag
& CSTOPB
)
2119 lcr
|= STB
; /* 2 stop bits */
2121 if (c_flag
& PARENB
)
2124 if ((c_flag
& PARODD
) == 0)
2127 switch (c_flag
& CSIZE
) {
2142 /* set the baud rate, unless it is "0" */
2143 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
, DLAB
);
2145 if (baudrate
!= 0) {
2146 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ DAT
,
2147 asyspdtab
[baudrate
] & 0xff);
2148 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ ICR
,
2149 (asyspdtab
[baudrate
] >> 8) & 0xff);
2151 /* set the line control modes */
2152 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
, lcr
);
2155 * If we have a FIFO buffer, enable/flush
2156 * at intialize time, flush if transitioning from
2157 * CREAD off to CREAD on.
2159 if ((ocflags
& CREAD
) == 0 && (c_flag
& CREAD
) ||
2161 if (asy
->asy_use_fifo
== FIFO_ON
)
2162 asy_reset_fifo(asy
, FIFORXFLSH
);
2164 /* remember the new cflags */
2165 asy
->asy_ocflag
= c_flag
& ~CLOCAL
;
2169 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ MCR
,
2170 (asy
->asy_mcr
& RTS
) | OUT2
);
2172 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ MCR
,
2173 asy
->asy_mcr
| OUT2
);
2176 * Call the modem status interrupt handler to check for the carrier
2177 * in case CLOCAL was turned off after the carrier came on.
2178 * (Note: Modem status interrupt is not enabled if CLOCAL is ON.)
2182 /* Set interrupt control */
2183 DEBUGCONT3(ASY_DEBUG_MODM2
,
2184 "asy%d_program: c_flag & CLOCAL = %x t_cflag & CRTSCTS = %x\n",
2185 instance
, c_flag
& CLOCAL
,
2186 async
->async_ttycommon
.t_cflag
& CRTSCTS
);
2188 if ((c_flag
& CLOCAL
) && !(async
->async_ttycommon
.t_cflag
& CRTSCTS
))
2190 * direct-wired line ignores DCD, so we don't enable modem
2191 * status interrupts.
2193 icr
= (TIEN
| SIEN
);
2195 icr
= (TIEN
| SIEN
| MIEN
);
2200 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ ICR
, icr
);
2201 DEBUGCONT1(ASY_DEBUG_PROCS
, "asy%d_program: done\n", instance
);
2205 asy_baudok(struct asycom
*asy
)
2207 struct asyncline
*async
= asy
->asy_priv
;
2211 baudrate
= BAUDINDEX(async
->async_ttycommon
.t_cflag
);
2213 if (baudrate
>= sizeof (asyspdtab
)/sizeof (*asyspdtab
))
2216 return (baudrate
== 0 || asyspdtab
[baudrate
]);
2220 * asyintr() is the High Level Interrupt Handler.
2222 * There are four different interrupt types indexed by ISR register values:
2224 * 1: Tx holding register is empty, ready for next char
2225 * 2: Rx register now holds a char to be picked up
2226 * 3: error or break on line
2227 * This routine checks the Bit 0 (interrupt-not-pending) to determine if
2228 * the interrupt is from this port.
2231 asyintr(caddr_t argasy
)
2233 struct asycom
*asy
= (struct asycom
*)argasy
;
2234 struct asyncline
*async
;
2235 int ret_status
= DDI_INTR_UNCLAIMED
;
2236 uchar_t interrupt_id
, lsr
;
2238 interrupt_id
= ddi_get8(asy
->asy_iohandle
,
2239 asy
->asy_ioaddr
+ ISR
) & 0x0F;
2240 async
= asy
->asy_priv
;
2242 if ((async
== NULL
) ||
2243 !(async
->async_flags
& (ASYNC_ISOPEN
|ASYNC_WOPEN
))) {
2244 if (interrupt_id
& NOINTERRUPT
)
2245 return (DDI_INTR_UNCLAIMED
);
2248 * reset the device by:
2249 * reading line status
2250 * reading any data from data status register
2251 * reading modem status
2253 (void) ddi_get8(asy
->asy_iohandle
,
2254 asy
->asy_ioaddr
+ LSR
);
2255 (void) ddi_get8(asy
->asy_iohandle
,
2256 asy
->asy_ioaddr
+ DAT
);
2257 asy
->asy_msr
= ddi_get8(asy
->asy_iohandle
,
2258 asy
->asy_ioaddr
+ MSR
);
2259 return (DDI_INTR_CLAIMED
);
2263 mutex_enter(&asy
->asy_excl_hi
);
2265 if (asy
->asy_flags
& ASY_DDI_SUSPENDED
) {
2266 mutex_exit(&asy
->asy_excl_hi
);
2267 return (DDI_INTR_CLAIMED
);
2271 * We will loop until the interrupt line is pulled low. asy
2272 * interrupt is edge triggered.
2275 for (;; interrupt_id
=
2276 (ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ ISR
) & 0x0F)) {
2278 if (interrupt_id
& NOINTERRUPT
)
2280 ret_status
= DDI_INTR_CLAIMED
;
2282 DEBUGCONT1(ASY_DEBUG_INTR
, "asyintr: interrupt_id = 0x%d\n",
2284 lsr
= ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LSR
);
2285 switch (interrupt_id
) {
2289 /* receiver interrupt or receiver errors */
2290 async_rxint(asy
, lsr
);
2293 /* transmit interrupt */
2297 /* modem status interrupt */
2301 if ((lsr
& XHRE
) && (async
->async_flags
& ASYNC_BUSY
) &&
2302 (async
->async_ocnt
> 0))
2305 mutex_exit(&asy
->asy_excl_hi
);
2306 return (ret_status
);
2310 * Transmitter interrupt service routine.
2311 * If there is more data to transmit in the current pseudo-DMA block,
2312 * send the next character if output is not stopped or draining.
2313 * Otherwise, queue up a soft interrupt.
2315 * XXX - Needs review for HW FIFOs.
2318 async_txint(struct asycom
*asy
)
2320 struct asyncline
*async
= asy
->asy_priv
;
2324 * If ASYNC_BREAK or ASYNC_OUT_SUSPEND has been set, return to
2325 * asyintr()'s context to claim the interrupt without performing
2326 * any action. No character will be loaded into FIFO/THR until
2327 * timed or untimed break is removed
2329 if (async
->async_flags
& (ASYNC_BREAK
|ASYNC_OUT_SUSPEND
))
2332 fifo_len
= asy
->asy_fifo_buf
; /* with FIFO buffers */
2333 if (fifo_len
> asy_max_tx_fifo
)
2334 fifo_len
= asy_max_tx_fifo
;
2336 if (async_flowcontrol_sw_input(asy
, FLOW_CHECK
, IN_FLOW_NULL
))
2339 if (async
->async_ocnt
> 0 && fifo_len
> 0 &&
2340 !(async
->async_flags
&
2341 (ASYNC_HW_OUT_FLW
|ASYNC_SW_OUT_FLW
|ASYNC_STOPPED
))) {
2342 while (fifo_len
-- > 0 && async
->async_ocnt
-- > 0) {
2343 ddi_put8(asy
->asy_iohandle
,
2344 asy
->asy_ioaddr
+ DAT
, *async
->async_optr
++);
2346 async
->async_flags
|= ASYNC_PROGRESS
;
2356 * Interrupt on port: handle PPS event. This function is only called
2357 * for a port on which PPS event handling has been enabled.
2360 asy_ppsevent(struct asycom
*asy
, int msr
)
2362 if (asy
->asy_flags
& ASY_PPS_EDGE
) {
2363 /* Have seen leading edge, now look for and record drop */
2364 if ((msr
& DCD
) == 0)
2365 asy
->asy_flags
&= ~ASY_PPS_EDGE
;
2367 * Waiting for leading edge, look for rise; stamp event and
2368 * calibrate kernel clock.
2370 } else if (msr
& DCD
) {
2372 * This code captures a timestamp at the designated
2373 * transition of the PPS signal (DCD asserted). The
2374 * code provides a pointer to the timestamp, as well
2375 * as the hardware counter value at the capture.
2377 * Note: the kernel has nano based time values while
2378 * NTP requires micro based, an in-line fast algorithm
2379 * to convert nsec to usec is used here -- see hrt2ts()
2380 * in kernel/os/timers.c for a full description.
2382 struct timeval
*tvp
= &asy_ppsev
.tv
;
2386 asy
->asy_flags
|= ASY_PPS_EDGE
;
2391 usec
= nsec
+ (nsec
>> 2);
2392 usec
= nsec
+ (usec
>> 1);
2393 usec
= nsec
+ (usec
>> 2);
2394 usec
= nsec
+ (usec
>> 4);
2395 usec
= nsec
- (usec
>> 3);
2396 usec
= nsec
+ (usec
>> 2);
2397 usec
= nsec
+ (usec
>> 3);
2398 usec
= nsec
+ (usec
>> 4);
2399 usec
= nsec
+ (usec
>> 1);
2400 usec
= nsec
+ (usec
>> 6);
2401 tvp
->tv_usec
= usec
>> 10;
2402 tvp
->tv_sec
= ts
.tv_sec
;
2407 * Because the kernel keeps a high-resolution time,
2408 * pass the current highres timestamp in tvp and zero
2411 ddi_hardpps(tvp
, 0);
2416 * Receiver interrupt: RxRDY interrupt, FIFO timeout interrupt or receive
2418 * Try to put the character into the circular buffer for this line; if it
2419 * overflows, indicate a circular buffer overrun. If this port is always
2420 * to be serviced immediately, or the character is a STOP character, or
2421 * more than 15 characters have arrived, queue up a soft interrupt to
2422 * drain the circular buffer.
2423 * XXX - needs review for hw FIFOs support.
2427 async_rxint(struct asycom
*asy
, uchar_t lsr
)
2429 struct asyncline
*async
= asy
->asy_priv
;
2431 uint_t s
, needsoft
= 0;
2433 int looplim
= asy
->asy_fifo_buf
* 2;
2435 tp
= &async
->async_ttycommon
;
2436 if (!(tp
->t_cflag
& CREAD
)) {
2437 while (lsr
& (RCA
|PARERR
|FRMERR
|BRKDET
|OVRRUN
)) {
2438 (void) (ddi_get8(asy
->asy_iohandle
,
2439 asy
->asy_ioaddr
+ DAT
) & 0xff);
2440 lsr
= ddi_get8(asy
->asy_iohandle
,
2441 asy
->asy_ioaddr
+ LSR
);
2442 if (looplim
-- < 0) /* limit loop */
2445 return; /* line is not open for read? */
2448 while (lsr
& (RCA
|PARERR
|FRMERR
|BRKDET
|OVRRUN
)) {
2450 s
= 0; /* reset error status */
2452 c
= ddi_get8(asy
->asy_iohandle
,
2453 asy
->asy_ioaddr
+ DAT
) & 0xff;
2456 * We handle XON/XOFF char if IXON is set,
2457 * but if received char is _POSIX_VDISABLE,
2458 * we left it to the up level module.
2460 if (tp
->t_iflag
& IXON
) {
2461 if ((c
== async
->async_stopc
) &&
2462 (c
!= _POSIX_VDISABLE
)) {
2463 async_flowcontrol_sw_output(asy
,
2466 } else if ((c
== async
->async_startc
) &&
2467 (c
!= _POSIX_VDISABLE
)) {
2468 async_flowcontrol_sw_output(asy
,
2473 if ((tp
->t_iflag
& IXANY
) &&
2474 (async
->async_flags
& ASYNC_SW_OUT_FLW
)) {
2475 async_flowcontrol_sw_output(asy
,
2483 * Check for character break sequence
2485 if ((abort_enable
== KIOCABORTALTERNATE
) &&
2486 (asy
->asy_flags
& ASY_CONSOLE
)) {
2487 if (abort_charseq_recognize(c
))
2488 abort_sequence_enter(NULL
);
2491 /* Handle framing errors */
2492 if (lsr
& (PARERR
|FRMERR
|BRKDET
|OVRRUN
)) {
2494 if (tp
->t_iflag
& INPCK
) /* parity enabled */
2498 if (lsr
& (FRMERR
|BRKDET
))
2501 async
->async_hw_overrun
= 1;
2507 if ((tp
->t_iflag
& PARMRK
) &&
2508 !(tp
->t_iflag
& (IGNPAR
|ISTRIP
)) &&
2510 if (RING_POK(async
, 2)) {
2511 RING_PUT(async
, 0377);
2514 async
->async_sw_overrun
= 1;
2516 if (RING_POK(async
, 1))
2519 async
->async_sw_overrun
= 1;
2521 if (s
& FRERROR
) /* Handle framing errors */
2523 if ((asy
->asy_flags
& ASY_CONSOLE
) &&
2525 KIOCABORTALTERNATE
))
2526 abort_sequence_enter((char *)0);
2528 async
->async_break
++;
2530 if (RING_POK(async
, 1))
2531 RING_MARK(async
, c
, s
);
2533 async
->async_sw_overrun
= 1;
2534 else /* Parity errors are handled by ldterm */
2535 if (RING_POK(async
, 1))
2536 RING_MARK(async
, c
, s
);
2538 async
->async_sw_overrun
= 1;
2540 lsr
= ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LSR
);
2541 if (looplim
-- < 0) /* limit loop */
2544 if ((RING_CNT(async
) > (RINGSIZE
* 3)/4) &&
2545 !(async
->async_inflow_source
& IN_FLOW_RINGBUFF
)) {
2546 async_flowcontrol_hw_input(asy
, FLOW_STOP
, IN_FLOW_RINGBUFF
);
2547 (void) async_flowcontrol_sw_input(asy
, FLOW_STOP
,
2551 if ((async
->async_flags
& ASYNC_SERVICEIMM
) || needsoft
||
2552 (RING_FRAC(async
)) || (async
->async_polltid
== 0))
2553 ASYSETSOFT(asy
); /* need a soft interrupt */
2557 * Modem status interrupt.
2559 * (Note: It is assumed that the MSR hasn't been read by asyintr().)
2563 async_msint(struct asycom
*asy
)
2565 struct asyncline
*async
= asy
->asy_priv
;
2566 int msr
, t_cflag
= async
->async_ttycommon
.t_cflag
;
2568 int instance
= UNIT(async
->async_dev
);
2572 /* this resets the interrupt */
2573 msr
= ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ MSR
);
2574 DEBUGCONT10(ASY_DEBUG_STATE
,
2575 "async%d_msint call #%d:\n"
2576 " transition: %3s %3s %3s %3s\n"
2577 "current state: %3s %3s %3s %3s\n",
2579 ++(asy
->asy_msint_cnt
),
2580 (msr
& DCTS
) ? "DCTS" : " ",
2581 (msr
& DDSR
) ? "DDSR" : " ",
2582 (msr
& DRI
) ? "DRI " : " ",
2583 (msr
& DDCD
) ? "DDCD" : " ",
2584 (msr
& CTS
) ? "CTS " : " ",
2585 (msr
& DSR
) ? "DSR " : " ",
2586 (msr
& RI
) ? "RI " : " ",
2587 (msr
& DCD
) ? "DCD " : " ");
2589 /* If CTS status is changed, do H/W output flow control */
2590 if ((t_cflag
& CRTSCTS
) && (((asy
->asy_msr
^ msr
) & CTS
) != 0))
2591 async_flowcontrol_hw_output(asy
,
2592 msr
& CTS
? FLOW_START
: FLOW_STOP
);
2594 * Reading MSR resets the interrupt, we save the
2595 * value of msr so that other functions could examine MSR by
2596 * looking at asy_msr.
2598 asy
->asy_msr
= (uchar_t
)msr
;
2600 /* Handle PPS event */
2601 if (asy
->asy_flags
& ASY_PPS
)
2602 asy_ppsevent(asy
, msr
);
2607 * We will make sure that the modem status presented to us
2608 * during the previous read has not changed. If the chip samples
2609 * the modem status on the falling edge of the interrupt line,
2610 * and uses this state as the base for detecting change of modem
2611 * status, we would miss a change of modem status event that occured
2612 * after we initiated a read MSR operation.
2614 msr
= ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ MSR
);
2615 if (STATES(msr
) != STATES(asy
->asy_msr
))
2616 goto async_msint_retry
;
2620 * Handle a second-stage interrupt.
2624 asysoftintr(caddr_t intarg
)
2626 struct asycom
*asy
= (struct asycom
*)intarg
;
2627 struct asyncline
*async
;
2632 * Test and clear soft interrupt.
2634 mutex_enter(&asy
->asy_soft_lock
);
2635 DEBUGCONT0(ASY_DEBUG_PROCS
, "asysoftintr: enter\n");
2636 rv
= asy
->asysoftpend
;
2638 asy
->asysoftpend
= 0;
2639 mutex_exit(&asy
->asy_soft_lock
);
2642 if (asy
->asy_priv
== NULL
)
2643 return (rv
? DDI_INTR_CLAIMED
: DDI_INTR_UNCLAIMED
);
2644 async
= (struct asyncline
*)asy
->asy_priv
;
2645 mutex_enter(&asy
->asy_excl_hi
);
2646 if (asy
->asy_flags
& ASY_NEEDSOFT
) {
2647 asy
->asy_flags
&= ~ASY_NEEDSOFT
;
2648 mutex_exit(&asy
->asy_excl_hi
);
2650 mutex_enter(&asy
->asy_excl_hi
);
2654 * There are some instances where the softintr is not
2655 * scheduled and hence not called. It so happens that
2656 * causes the last few characters to be stuck in the
2657 * ringbuffer. Hence, call the handler once again so
2658 * the last few characters are cleared.
2660 cc
= RING_CNT(async
);
2661 mutex_exit(&asy
->asy_excl_hi
);
2663 (void) async_softint(asy
);
2665 return (rv
? DDI_INTR_CLAIMED
: DDI_INTR_UNCLAIMED
);
2669 * Handle a software interrupt.
2672 async_softint(struct asycom
*asy
)
2674 struct asyncline
*async
= asy
->asy_priv
;
2682 int instance
= UNIT(async
->async_dev
);
2684 DEBUGCONT1(ASY_DEBUG_PROCS
, "async%d_softint\n", instance
);
2685 mutex_enter(&asy
->asy_excl_hi
);
2686 if (asy
->asy_flags
& ASY_DOINGSOFT
) {
2687 asy
->asy_flags
|= ASY_DOINGSOFT_RETRY
;
2688 mutex_exit(&asy
->asy_excl_hi
);
2691 asy
->asy_flags
|= ASY_DOINGSOFT
;
2693 asy
->asy_flags
&= ~ASY_DOINGSOFT_RETRY
;
2694 mutex_exit(&asy
->asy_excl_hi
);
2695 mutex_enter(&asy
->asy_excl
);
2696 tp
= &async
->async_ttycommon
;
2698 if (async
->async_flags
& ASYNC_OUT_FLW_RESUME
) {
2699 if (async
->async_ocnt
> 0) {
2700 mutex_enter(&asy
->asy_excl_hi
);
2701 async_resume(async
);
2702 mutex_exit(&asy
->asy_excl_hi
);
2704 if (async
->async_xmitblk
)
2705 freeb(async
->async_xmitblk
);
2706 async
->async_xmitblk
= NULL
;
2709 async
->async_flags
&= ~ASYNC_OUT_FLW_RESUME
;
2711 mutex_enter(&asy
->asy_excl_hi
);
2712 if (async
->async_ext
) {
2713 async
->async_ext
= 0;
2714 /* check for carrier up */
2715 DEBUGCONT3(ASY_DEBUG_MODM2
,
2716 "async%d_softint: asy_msr & DCD = %x, "
2717 "tp->t_flags & TS_SOFTCAR = %x\n",
2718 instance
, asy
->asy_msr
& DCD
, tp
->t_flags
& TS_SOFTCAR
);
2720 if (asy
->asy_msr
& DCD
) {
2721 /* carrier present */
2722 if ((async
->async_flags
& ASYNC_CARR_ON
) == 0) {
2723 DEBUGCONT1(ASY_DEBUG_MODM2
,
2724 "async%d_softint: set ASYNC_CARR_ON\n",
2726 async
->async_flags
|= ASYNC_CARR_ON
;
2727 if (async
->async_flags
& ASYNC_ISOPEN
) {
2728 mutex_exit(&asy
->asy_excl_hi
);
2729 mutex_exit(&asy
->asy_excl
);
2730 (void) putctl(q
, M_UNHANGUP
);
2731 mutex_enter(&asy
->asy_excl
);
2732 mutex_enter(&asy
->asy_excl_hi
);
2734 cv_broadcast(&async
->async_flags_cv
);
2737 if ((async
->async_flags
& ASYNC_CARR_ON
) &&
2738 !(tp
->t_cflag
& CLOCAL
) &&
2739 !(tp
->t_flags
& TS_SOFTCAR
)) {
2742 DEBUGCONT1(ASY_DEBUG_MODEM
,
2743 "async%d_softint: carrier dropped, "
2747 * Carrier went away.
2748 * Drop DTR, abort any output in
2749 * progress, indicate that output is
2750 * not stopped, and send a hangup
2751 * notification upstream.
2753 val
= ddi_get8(asy
->asy_iohandle
,
2754 asy
->asy_ioaddr
+ MCR
);
2755 ddi_put8(asy
->asy_iohandle
,
2756 asy
->asy_ioaddr
+ MCR
, (val
& ~DTR
));
2758 if (async
->async_flags
& ASYNC_BUSY
) {
2759 DEBUGCONT0(ASY_DEBUG_BUSY
,
2762 "Clearing async_ocnt\n");
2763 async
->async_ocnt
= 0;
2766 async
->async_flags
&= ~ASYNC_STOPPED
;
2767 if (async
->async_flags
& ASYNC_ISOPEN
) {
2768 mutex_exit(&asy
->asy_excl_hi
);
2769 mutex_exit(&asy
->asy_excl
);
2770 (void) putctl(q
, M_HANGUP
);
2771 mutex_enter(&asy
->asy_excl
);
2772 DEBUGCONT1(ASY_DEBUG_MODEM
,
2774 "putctl(q, M_HANGUP)\n",
2777 * Flush FIFO buffers
2778 * Any data left in there is invalid now
2780 if (asy
->asy_use_fifo
== FIFO_ON
)
2781 asy_reset_fifo(asy
, FIFOTXFLSH
);
2783 * Flush our write queue if we have one.
2784 * If we're in the midst of close, then
2785 * flush everything. Don't leave stale
2786 * ioctls lying about.
2788 flushflag
= (async
->async_flags
&
2789 ASYNC_CLOSING
) ? FLUSHALL
:
2791 flushq(tp
->t_writeq
, flushflag
);
2794 bp
= async
->async_xmitblk
;
2797 async
->async_xmitblk
= NULL
;
2800 mutex_enter(&asy
->asy_excl_hi
);
2801 async
->async_flags
&= ~ASYNC_BUSY
;
2803 * This message warns of Carrier loss
2804 * with data left to transmit can hang
2807 DEBUGCONT0(ASY_DEBUG_MODEM
,
2808 "async_softint: Flushing to "
2809 "prevent HUPCL hanging\n");
2810 } /* if (ASYNC_ISOPEN) */
2811 } /* if (ASYNC_CARR_ON && CLOCAL) */
2812 async
->async_flags
&= ~ASYNC_CARR_ON
;
2813 cv_broadcast(&async
->async_flags_cv
);
2815 } /* if (async->async_ext) */
2817 mutex_exit(&asy
->asy_excl_hi
);
2820 * If data has been added to the circular buffer, remove
2821 * it from the buffer, and send it up the stream if there's
2822 * somebody listening. Try to do it 16 bytes at a time. If we
2823 * have more than 16 bytes to move, move 16 byte chunks and
2824 * leave the rest for next time around (maybe it will grow).
2826 mutex_enter(&asy
->asy_excl_hi
);
2827 if (!(async
->async_flags
& ASYNC_ISOPEN
)) {
2831 if ((cc
= RING_CNT(async
)) == 0)
2833 mutex_exit(&asy
->asy_excl_hi
);
2836 mutex_enter(&asy
->asy_excl_hi
);
2837 if (!(async
->async_inflow_source
& IN_FLOW_STREAMS
)) {
2838 async_flowcontrol_hw_input(asy
, FLOW_STOP
,
2840 (void) async_flowcontrol_sw_input(asy
, FLOW_STOP
,
2845 if (async
->async_inflow_source
& IN_FLOW_STREAMS
) {
2846 mutex_enter(&asy
->asy_excl_hi
);
2847 async_flowcontrol_hw_input(asy
, FLOW_START
,
2849 (void) async_flowcontrol_sw_input(asy
, FLOW_START
,
2851 mutex_exit(&asy
->asy_excl_hi
);
2854 DEBUGCONT2(ASY_DEBUG_INPUT
, "async%d_softint: %d char(s) in queue.\n",
2857 if (!(bp
= allocb(cc
, BPRI_MED
))) {
2858 mutex_exit(&asy
->asy_excl
);
2859 ttycommon_qfull(&async
->async_ttycommon
, q
);
2860 mutex_enter(&asy
->asy_excl
);
2861 mutex_enter(&asy
->asy_excl_hi
);
2864 mutex_enter(&asy
->asy_excl_hi
);
2866 if (RING_ERR(async
, S_ERRORS
)) {
2868 c
= RING_GET(async
);
2871 *bp
->b_wptr
++ = RING_GET(async
);
2873 mutex_exit(&asy
->asy_excl_hi
);
2874 mutex_exit(&asy
->asy_excl
);
2875 if (bp
->b_wptr
> bp
->b_rptr
) {
2877 asyerror(CE_NOTE
, "asy%d: local queue full",
2885 * If we have a parity error, then send
2886 * up an M_BREAK with the "bad"
2887 * character as an argument. Let ldterm
2888 * figure out what to do with the error.
2891 (void) putctl1(q
, M_BREAK
, c
);
2892 ASYSETSOFT(async
->async_common
); /* finish cc chars */
2894 mutex_enter(&asy
->asy_excl
);
2895 mutex_enter(&asy
->asy_excl_hi
);
2897 if ((RING_CNT(async
) < (RINGSIZE
/4)) &&
2898 (async
->async_inflow_source
& IN_FLOW_RINGBUFF
)) {
2899 async_flowcontrol_hw_input(asy
, FLOW_START
, IN_FLOW_RINGBUFF
);
2900 (void) async_flowcontrol_sw_input(asy
, FLOW_START
,
2905 * If a transmission has finished, indicate that it's finished,
2906 * and start that line up again.
2908 if (async
->async_break
> 0) {
2909 nb
= async
->async_break
;
2910 async
->async_break
= 0;
2911 if (async
->async_flags
& ASYNC_ISOPEN
) {
2912 mutex_exit(&asy
->asy_excl_hi
);
2913 mutex_exit(&asy
->asy_excl
);
2914 for (; nb
> 0; nb
--)
2915 (void) putctl(q
, M_BREAK
);
2916 mutex_enter(&asy
->asy_excl
);
2917 mutex_enter(&asy
->asy_excl_hi
);
2920 if (async
->async_ocnt
<= 0 && (async
->async_flags
& ASYNC_BUSY
)) {
2921 DEBUGCONT2(ASY_DEBUG_BUSY
,
2922 "async%d_softint: Clearing ASYNC_BUSY. async_ocnt=%d\n",
2925 async
->async_flags
&= ~ASYNC_BUSY
;
2926 mutex_exit(&asy
->asy_excl_hi
);
2927 if (async
->async_xmitblk
)
2928 freeb(async
->async_xmitblk
);
2929 async
->async_xmitblk
= NULL
;
2932 * If the flag isn't set after doing the async_start above, we
2933 * may have finished all the queued output. Signal any thread
2936 if (!(async
->async_flags
& ASYNC_BUSY
))
2937 cv_broadcast(&async
->async_flags_cv
);
2938 mutex_enter(&asy
->asy_excl_hi
);
2941 * A note about these overrun bits: all they do is *tell* someone
2942 * about an error- They do not track multiple errors. In fact,
2943 * you could consider them latched register bits if you like.
2944 * We are only interested in printing the error message once for
2945 * any cluster of overrun errors.
2947 if (async
->async_hw_overrun
) {
2948 if (async
->async_flags
& ASYNC_ISOPEN
) {
2949 mutex_exit(&asy
->asy_excl_hi
);
2950 mutex_exit(&asy
->asy_excl
);
2951 asyerror(CE_NOTE
, "asy%d: silo overflow", instance
);
2952 mutex_enter(&asy
->asy_excl
);
2953 mutex_enter(&asy
->asy_excl_hi
);
2955 async
->async_hw_overrun
= 0;
2957 if (async
->async_sw_overrun
) {
2958 if (async
->async_flags
& ASYNC_ISOPEN
) {
2959 mutex_exit(&asy
->asy_excl_hi
);
2960 mutex_exit(&asy
->asy_excl
);
2961 asyerror(CE_NOTE
, "asy%d: ring buffer overflow",
2963 mutex_enter(&asy
->asy_excl
);
2964 mutex_enter(&asy
->asy_excl_hi
);
2966 async
->async_sw_overrun
= 0;
2968 if (asy
->asy_flags
& ASY_DOINGSOFT_RETRY
) {
2969 mutex_exit(&asy
->asy_excl
);
2972 asy
->asy_flags
&= ~ASY_DOINGSOFT
;
2973 mutex_exit(&asy
->asy_excl_hi
);
2974 mutex_exit(&asy
->asy_excl
);
2975 DEBUGCONT1(ASY_DEBUG_PROCS
, "async%d_softint: done\n", instance
);
2979 * Restart output on a line after a delay or break timer expired.
2982 async_restart(void *arg
)
2984 struct asyncline
*async
= (struct asyncline
*)arg
;
2985 struct asycom
*asy
= async
->async_common
;
2989 * If break timer expired, turn off the break bit.
2992 int instance
= UNIT(async
->async_dev
);
2994 DEBUGCONT1(ASY_DEBUG_PROCS
, "async%d_restart\n", instance
);
2996 mutex_enter(&asy
->asy_excl
);
2998 * If ASYNC_OUT_SUSPEND is also set, we don't really
2999 * clean the HW break, TIOCCBRK is responsible for this.
3001 if ((async
->async_flags
& ASYNC_BREAK
) &&
3002 !(async
->async_flags
& ASYNC_OUT_SUSPEND
)) {
3003 mutex_enter(&asy
->asy_excl_hi
);
3004 lcr
= ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
);
3005 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
,
3007 mutex_exit(&asy
->asy_excl_hi
);
3009 async
->async_flags
&= ~(ASYNC_DELAY
|ASYNC_BREAK
);
3010 cv_broadcast(&async
->async_flags_cv
);
3013 mutex_exit(&asy
->asy_excl
);
3017 async_start(struct asyncline
*async
)
3019 async_nstart(async
, 0);
3023 * Start output on a line, unless it's busy, frozen, or otherwise.
3027 async_nstart(struct asyncline
*async
, int mode
)
3029 struct asycom
*asy
= async
->async_common
;
3040 int instance
= UNIT(async
->async_dev
);
3042 DEBUGCONT1(ASY_DEBUG_PROCS
, "async%d_nstart\n", instance
);
3044 if (asy
->asy_use_fifo
== FIFO_ON
) {
3045 fifo_len
= asy
->asy_fifo_buf
; /* with FIFO buffers */
3046 if (fifo_len
> asy_max_tx_fifo
)
3047 fifo_len
= asy_max_tx_fifo
;
3050 ASSERT(mutex_owned(&asy
->asy_excl
));
3053 * If the chip is busy (i.e., we're waiting for a break timeout
3054 * to expire, or for the current transmission to finish, or for
3055 * output to finish draining from chip), don't grab anything new.
3057 if (async
->async_flags
& (ASYNC_BREAK
|ASYNC_BUSY
)) {
3058 DEBUGCONT2((mode
? ASY_DEBUG_OUT
: 0),
3059 "async%d_nstart: start %s.\n",
3061 async
->async_flags
& ASYNC_BREAK
? "break" : "busy");
3066 * Check only pended sw input flow control.
3068 mutex_enter(&asy
->asy_excl_hi
);
3069 if (async_flowcontrol_sw_input(asy
, FLOW_CHECK
, IN_FLOW_NULL
))
3071 mutex_exit(&asy
->asy_excl_hi
);
3074 * If we're waiting for a delay timeout to expire, don't grab
3077 if (async
->async_flags
& ASYNC_DELAY
) {
3078 DEBUGCONT1((mode
? ASY_DEBUG_OUT
: 0),
3079 "async%d_nstart: start ASYNC_DELAY.\n", instance
);
3083 if ((q
= async
->async_ttycommon
.t_writeq
) == NULL
) {
3084 DEBUGCONT1((mode
? ASY_DEBUG_OUT
: 0),
3085 "async%d_nstart: start writeq is null.\n", instance
);
3086 return; /* not attached to a stream */
3090 if ((bp
= getq(q
)) == NULL
)
3091 return; /* no data to transmit */
3094 * We have a message block to work on.
3095 * Check whether it's a break, a delay, or an ioctl (the latter
3096 * occurs if the ioctl in question was waiting for the output
3097 * to drain). If it's one of those, process it immediately.
3099 switch (bp
->b_datap
->db_type
) {
3103 * Set the break bit, and arrange for "async_restart"
3104 * to be called in 1/4 second; it will turn the
3105 * break bit off, and call "async_start" to grab
3108 mutex_enter(&asy
->asy_excl_hi
);
3109 val
= ddi_get8(asy
->asy_iohandle
,
3110 asy
->asy_ioaddr
+ LCR
);
3111 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
,
3113 mutex_exit(&asy
->asy_excl_hi
);
3114 async
->async_flags
|= ASYNC_BREAK
;
3115 (void) timeout(async_restart
, (caddr_t
)async
,
3116 drv_usectohz(1000000)/4);
3118 return; /* wait for this to finish */
3122 * Arrange for "async_restart" to be called when the
3123 * delay expires; it will turn ASYNC_DELAY off,
3124 * and call "async_start" to grab the next message.
3126 (void) timeout(async_restart
, (caddr_t
)async
,
3127 (int)(*(unsigned char *)bp
->b_rptr
+ 6));
3128 async
->async_flags
|= ASYNC_DELAY
;
3130 return; /* wait for this to finish */
3134 * This ioctl was waiting for the output ahead of
3135 * it to drain; obviously, it has. Do it, and
3136 * then grab the next message after it.
3138 mutex_exit(&asy
->asy_excl
);
3139 async_ioctl(async
, q
, bp
);
3140 mutex_enter(&asy
->asy_excl
);
3144 while (bp
!= NULL
&& ((cc
= MBLKL(bp
)) == 0)) {
3154 * We have data to transmit. If output is stopped, put
3155 * it back and try again later.
3157 if (async
->async_flags
& (ASYNC_HW_OUT_FLW
| ASYNC_SW_OUT_FLW
|
3158 ASYNC_STOPPED
| ASYNC_OUT_SUSPEND
)) {
3159 (void) putbq(q
, bp
);
3163 async
->async_xmitblk
= bp
;
3164 xmit_addr
= bp
->b_rptr
;
3167 (void) putbq(q
, bp
); /* not done with this message yet */
3170 * In 5-bit mode, the high order bits are used
3171 * to indicate character sizes less than five,
3172 * so we need to explicitly mask before transmitting
3174 if ((async
->async_ttycommon
.t_cflag
& CSIZE
) == CS5
) {
3175 unsigned char *p
= xmit_addr
;
3179 *p
++ &= (unsigned char) 0x1f;
3183 * Set up this block for pseudo-DMA.
3185 mutex_enter(&asy
->asy_excl_hi
);
3187 * If the transmitter is ready, shove the first
3191 while (--fifo_len
>= 0 && cc
> 0) {
3192 if (!(ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LSR
) &
3195 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ DAT
,
3200 async
->async_optr
= xmit_addr
;
3201 async
->async_ocnt
= cc
;
3203 async
->async_flags
|= ASYNC_PROGRESS
;
3204 DEBUGCONT2(ASY_DEBUG_BUSY
,
3205 "async%d_nstart: Set ASYNC_BUSY. async_ocnt=%d\n",
3206 instance
, async
->async_ocnt
);
3207 async
->async_flags
|= ASYNC_BUSY
;
3208 mutex_exit(&asy
->asy_excl_hi
);
3212 * Resume output by poking the transmitter.
3215 async_resume(struct asyncline
*async
)
3217 struct asycom
*asy
= async
->async_common
;
3222 ASSERT(mutex_owned(&asy
->asy_excl_hi
));
3224 instance
= UNIT(async
->async_dev
);
3225 DEBUGCONT1(ASY_DEBUG_PROCS
, "async%d_resume\n", instance
);
3228 if (ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LSR
) & XHRE
) {
3229 if (async_flowcontrol_sw_input(asy
, FLOW_CHECK
, IN_FLOW_NULL
))
3231 if (async
->async_ocnt
> 0 &&
3232 !(async
->async_flags
&
3233 (ASYNC_HW_OUT_FLW
|ASYNC_SW_OUT_FLW
|ASYNC_OUT_SUSPEND
))) {
3234 ddi_put8(asy
->asy_iohandle
,
3235 asy
->asy_ioaddr
+ DAT
, *async
->async_optr
++);
3236 async
->async_ocnt
--;
3237 async
->async_flags
|= ASYNC_PROGRESS
;
3243 * Hold the untimed break to last the minimum time.
3246 async_hold_utbrk(void *arg
)
3248 struct asyncline
*async
= arg
;
3249 struct asycom
*asy
= async
->async_common
;
3251 mutex_enter(&asy
->asy_excl
);
3252 async
->async_flags
&= ~ASYNC_HOLD_UTBRK
;
3253 cv_broadcast(&async
->async_flags_cv
);
3254 async
->async_utbrktid
= 0;
3255 mutex_exit(&asy
->asy_excl
);
3259 * Resume the untimed break.
3262 async_resume_utbrk(struct asyncline
*async
)
3265 struct asycom
*asy
= async
->async_common
;
3266 ASSERT(mutex_owned(&asy
->asy_excl
));
3269 * Because the wait time is very short,
3270 * so we use uninterruptably wait.
3272 while (async
->async_flags
& ASYNC_HOLD_UTBRK
) {
3273 cv_wait(&async
->async_flags_cv
, &asy
->asy_excl
);
3275 mutex_enter(&asy
->asy_excl_hi
);
3277 * Timed break and untimed break can exist simultaneously,
3278 * if ASYNC_BREAK is also set at here, we don't
3279 * really clean the HW break.
3281 if (!(async
->async_flags
& ASYNC_BREAK
)) {
3282 val
= ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
);
3283 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LCR
,
3286 async
->async_flags
&= ~ASYNC_OUT_SUSPEND
;
3287 cv_broadcast(&async
->async_flags_cv
);
3288 if (async
->async_ocnt
> 0) {
3289 async_resume(async
);
3290 mutex_exit(&asy
->asy_excl_hi
);
3292 async
->async_flags
&= ~ASYNC_BUSY
;
3293 mutex_exit(&asy
->asy_excl_hi
);
3294 if (async
->async_xmitblk
!= NULL
) {
3295 freeb(async
->async_xmitblk
);
3296 async
->async_xmitblk
= NULL
;
3303 * Process an "ioctl" message sent down to us.
3304 * Note that we don't need to get any locks until we are ready to access
3305 * the hardware. Nothing we access until then is going to be altered
3306 * outside of the STREAMS framework, so we should be safe.
3308 int asydelay
= 10000;
3310 async_ioctl(struct asyncline
*async
, queue_t
*wq
, mblk_t
*mp
)
3312 struct asycom
*asy
= async
->async_common
;
3313 tty_common_t
*tp
= &async
->async_ttycommon
;
3314 struct iocblk
*iocp
;
3322 int instance
= UNIT(async
->async_dev
);
3324 DEBUGCONT1(ASY_DEBUG_PROCS
, "async%d_ioctl\n", instance
);
3327 if (tp
->t_iocpending
!= NULL
) {
3329 * We were holding an "ioctl" response pending the
3330 * availability of an "mblk" to hold data to be passed up;
3331 * another "ioctl" came through, which means that "ioctl"
3332 * must have timed out or been aborted.
3334 freemsg(async
->async_ttycommon
.t_iocpending
);
3335 async
->async_ttycommon
.t_iocpending
= NULL
;
3338 iocp
= (struct iocblk
*)mp
->b_rptr
;
3341 * For TIOCMGET and the PPS ioctls, do NOT call ttycommon_ioctl()
3342 * because this function frees up the message block (mp->b_cont) that
3343 * contains the user location where we pass back the results.
3345 * Similarly, CONSOPENPOLLEDIO needs ioc_count, which ttycommon_ioctl
3346 * zaps. We know that ttycommon_ioctl doesn't know any CONS*
3347 * ioctls, so keep the others safe too.
3349 DEBUGCONT2(ASY_DEBUG_IOCTL
, "async%d_ioctl: %s\n",
3351 iocp
->ioc_cmd
== TIOCMGET
? "TIOCMGET" :
3352 iocp
->ioc_cmd
== TIOCMSET
? "TIOCMSET" :
3353 iocp
->ioc_cmd
== TIOCMBIS
? "TIOCMBIS" :
3354 iocp
->ioc_cmd
== TIOCMBIC
? "TIOCMBIC" :
3357 switch (iocp
->ioc_cmd
) {
3362 case CONSOPENPOLLEDIO
:
3363 case CONSCLOSEPOLLEDIO
:
3364 case CONSSETABORTENABLE
:
3365 case CONSGETABORTENABLE
:
3366 error
= -1; /* Do Nothing */
3371 * The only way in which "ttycommon_ioctl" can fail is if the
3372 * "ioctl" requires a response containing data to be returned
3373 * to the user, and no mblk could be allocated for the data.
3374 * No such "ioctl" alters our state. Thus, we always go ahead
3375 * and do any state-changes the "ioctl" calls for. If we
3376 * couldn't allocate the data, "ttycommon_ioctl" has stashed
3377 * the "ioctl" away safely, so we just call "bufcall" to
3378 * request that we be called back when we stand a better
3379 * chance of allocating the data.
3381 if ((datasize
= ttycommon_ioctl(tp
, wq
, mp
, &error
)) != 0) {
3382 if (async
->async_wbufcid
)
3383 unbufcall(async
->async_wbufcid
);
3384 async
->async_wbufcid
= bufcall(datasize
, BPRI_HI
,
3385 (void (*)(void *)) async_reioctl
,
3386 (void *)(intptr_t)async
->async_common
->asy_unit
);
3391 mutex_enter(&asy
->asy_excl
);
3395 * "ttycommon_ioctl" did most of the work; we just use the
3398 switch (iocp
->ioc_cmd
) {
3401 mutex_enter(&asy
->asy_excl_hi
);
3402 if (asy_baudok(asy
))
3403 asy_program(asy
, ASY_NOINIT
);
3406 mutex_exit(&asy
->asy_excl_hi
);
3413 mutex_enter(&asy
->asy_excl_hi
);
3414 if (!asy_baudok(asy
))
3417 if (asy_isbusy(asy
))
3419 asy_program(asy
, ASY_NOINIT
);
3421 mutex_exit(&asy
->asy_excl_hi
);
3424 } else if (error
< 0) {
3426 * "ttycommon_ioctl" didn't do anything; we process it here.
3429 switch (iocp
->ioc_cmd
) {
3435 if (mp
->b_cont
!= NULL
)
3436 freemsg(mp
->b_cont
);
3438 mp
->b_cont
= allocb(sizeof (int), BPRI_HI
);
3439 if (mp
->b_cont
== NULL
) {
3443 if (asy
->asy_flags
& ASY_PPS
)
3444 *(int *)mp
->b_cont
->b_wptr
= 1;
3446 *(int *)mp
->b_cont
->b_wptr
= 0;
3447 mp
->b_cont
->b_wptr
+= sizeof (int);
3448 mp
->b_datap
->db_type
= M_IOCACK
;
3449 iocp
->ioc_count
= sizeof (int);
3456 error
= miocpullup(mp
, sizeof (int));
3460 mutex_enter(&asy
->asy_excl_hi
);
3461 if (*(int *)mp
->b_cont
->b_rptr
)
3462 asy
->asy_flags
|= ASY_PPS
;
3464 asy
->asy_flags
&= ~ASY_PPS
;
3465 /* Reset edge sense */
3466 asy
->asy_flags
&= ~ASY_PPS_EDGE
;
3467 mutex_exit(&asy
->asy_excl_hi
);
3468 mp
->b_datap
->db_type
= M_IOCACK
;
3474 * Get PPS event data.
3478 #ifdef _SYSCALL32_IMPL
3479 struct ppsclockev32 p32
;
3481 struct ppsclockev ppsclockev
;
3483 if (mp
->b_cont
!= NULL
) {
3484 freemsg(mp
->b_cont
);
3488 if ((asy
->asy_flags
& ASY_PPS
) == 0) {
3493 /* Protect from incomplete asy_ppsev */
3494 mutex_enter(&asy
->asy_excl_hi
);
3495 ppsclockev
= asy_ppsev
;
3496 mutex_exit(&asy
->asy_excl_hi
);
3498 #ifdef _SYSCALL32_IMPL
3499 if ((iocp
->ioc_flag
& IOC_MODELS
) != IOC_NATIVE
) {
3500 TIMEVAL_TO_TIMEVAL32(&p32
.tv
, &ppsclockev
.tv
);
3501 p32
.serial
= ppsclockev
.serial
;
3503 iocp
->ioc_count
= sizeof (struct ppsclockev32
);
3508 iocp
->ioc_count
= sizeof (struct ppsclockev
);
3511 if ((bp
= allocb(iocp
->ioc_count
, BPRI_HI
)) == NULL
) {
3517 bcopy(buf
, bp
->b_wptr
, iocp
->ioc_count
);
3518 bp
->b_wptr
+= iocp
->ioc_count
;
3519 mp
->b_datap
->db_type
= M_IOCACK
;
3524 error
= miocpullup(mp
, sizeof (int));
3528 if (*(int *)mp
->b_cont
->b_rptr
== 0) {
3531 * XXX Arrangements to ensure that a break
3532 * isn't in progress should be sufficient.
3533 * This ugly delay() is the only thing
3534 * that seems to work on the NCR Worldmark.
3535 * It should be replaced. Note that an
3536 * asy_waiteot() also does not work.
3539 delay(drv_usectohz(asydelay
));
3541 while (async
->async_flags
& ASYNC_BREAK
) {
3542 cv_wait(&async
->async_flags_cv
,
3545 mutex_enter(&asy
->asy_excl_hi
);
3547 * We loop until the TSR is empty and then
3548 * set the break. ASYNC_BREAK has been set
3549 * to ensure that no characters are
3550 * transmitted while the TSR is being
3551 * flushed and SOUT is being used for the
3554 * The wait period is equal to
3555 * clock / (baud * 16) * 16 * 2.
3558 async
->async_ttycommon
.t_cflag
);
3559 async
->async_flags
|= ASYNC_BREAK
;
3561 while ((ddi_get8(asy
->asy_iohandle
,
3562 asy
->asy_ioaddr
+ LSR
) & XSRE
) == 0) {
3563 mutex_exit(&asy
->asy_excl_hi
);
3564 mutex_exit(&asy
->asy_excl
);
3566 32*asyspdtab
[index
] & 0xfff);
3567 mutex_enter(&asy
->asy_excl
);
3568 mutex_enter(&asy
->asy_excl_hi
);
3571 * Arrange for "async_restart"
3572 * to be called in 1/4 second;
3573 * it will turn the break bit off, and call
3574 * "async_start" to grab the next message.
3576 val
= ddi_get8(asy
->asy_iohandle
,
3577 asy
->asy_ioaddr
+ LCR
);
3578 ddi_put8(asy
->asy_iohandle
,
3579 asy
->asy_ioaddr
+ LCR
,
3581 mutex_exit(&asy
->asy_excl_hi
);
3582 (void) timeout(async_restart
, (caddr_t
)async
,
3583 drv_usectohz(1000000)/4);
3585 DEBUGCONT1(ASY_DEBUG_OUT
,
3586 "async%d_ioctl: wait for flush.\n",
3588 mutex_enter(&asy
->asy_excl_hi
);
3590 mutex_exit(&asy
->asy_excl_hi
);
3591 DEBUGCONT1(ASY_DEBUG_OUT
,
3592 "async%d_ioctl: ldterm satisfied.\n",
3598 if (!(async
->async_flags
& ASYNC_OUT_SUSPEND
)) {
3599 mutex_enter(&asy
->asy_excl_hi
);
3600 async
->async_flags
|= ASYNC_OUT_SUSPEND
;
3601 async
->async_flags
|= ASYNC_HOLD_UTBRK
;
3603 async
->async_ttycommon
.t_cflag
);
3604 while ((ddi_get8(asy
->asy_iohandle
,
3605 asy
->asy_ioaddr
+ LSR
) & XSRE
) == 0) {
3606 mutex_exit(&asy
->asy_excl_hi
);
3607 mutex_exit(&asy
->asy_excl
);
3609 32*asyspdtab
[index
] & 0xfff);
3610 mutex_enter(&asy
->asy_excl
);
3611 mutex_enter(&asy
->asy_excl_hi
);
3613 val
= ddi_get8(asy
->asy_iohandle
,
3614 asy
->asy_ioaddr
+ LCR
);
3615 ddi_put8(asy
->asy_iohandle
,
3616 asy
->asy_ioaddr
+ LCR
, (val
| SETBREAK
));
3617 mutex_exit(&asy
->asy_excl_hi
);
3618 /* wait for 100ms to hold BREAK */
3619 async
->async_utbrktid
=
3620 timeout((void (*)())async_hold_utbrk
,
3622 drv_usectohz(asy_min_utbrk
));
3624 mioc2ack(mp
, NULL
, 0, 0);
3628 if (async
->async_flags
& ASYNC_OUT_SUSPEND
)
3629 async_resume_utbrk(async
);
3630 mioc2ack(mp
, NULL
, 0, 0);
3636 if (iocp
->ioc_count
!= TRANSPARENT
) {
3637 DEBUGCONT1(ASY_DEBUG_IOCTL
, "async%d_ioctl: "
3638 "non-transparent\n", instance
);
3640 error
= miocpullup(mp
, sizeof (int));
3644 mutex_enter(&asy
->asy_excl_hi
);
3646 dmtoasy(*(int *)mp
->b_cont
->b_rptr
),
3648 mutex_exit(&asy
->asy_excl_hi
);
3649 iocp
->ioc_error
= 0;
3650 mp
->b_datap
->db_type
= M_IOCACK
;
3652 DEBUGCONT1(ASY_DEBUG_IOCTL
, "async%d_ioctl: "
3653 "transparent\n", instance
);
3654 mcopyin(mp
, NULL
, sizeof (int), NULL
);
3659 datamp
= allocb(sizeof (int), BPRI_MED
);
3660 if (datamp
== NULL
) {
3665 mutex_enter(&asy
->asy_excl_hi
);
3666 *(int *)datamp
->b_rptr
= asymctl(asy
, 0, TIOCMGET
);
3667 mutex_exit(&asy
->asy_excl_hi
);
3669 if (iocp
->ioc_count
== TRANSPARENT
) {
3670 DEBUGCONT1(ASY_DEBUG_IOCTL
, "async%d_ioctl: "
3671 "transparent\n", instance
);
3672 mcopyout(mp
, NULL
, sizeof (int), NULL
, datamp
);
3674 DEBUGCONT1(ASY_DEBUG_IOCTL
, "async%d_ioctl: "
3675 "non-transparent\n", instance
);
3676 mioc2ack(mp
, datamp
, sizeof (int), 0);
3680 case CONSOPENPOLLEDIO
:
3681 error
= miocpullup(mp
, sizeof (struct cons_polledio
*));
3685 *(struct cons_polledio
**)mp
->b_cont
->b_rptr
=
3688 mp
->b_datap
->db_type
= M_IOCACK
;
3691 case CONSCLOSEPOLLEDIO
:
3692 mp
->b_datap
->db_type
= M_IOCACK
;
3693 iocp
->ioc_error
= 0;
3697 case CONSSETABORTENABLE
:
3698 error
= secpolicy_console(iocp
->ioc_cr
);
3702 if (iocp
->ioc_count
!= TRANSPARENT
) {
3707 if (*(intptr_t *)mp
->b_cont
->b_rptr
)
3708 asy
->asy_flags
|= ASY_CONSOLE
;
3710 asy
->asy_flags
&= ~ASY_CONSOLE
;
3712 mp
->b_datap
->db_type
= M_IOCACK
;
3713 iocp
->ioc_error
= 0;
3717 case CONSGETABORTENABLE
:
3718 /*CONSTANTCONDITION*/
3719 ASSERT(sizeof (boolean_t
) <= sizeof (boolean_t
*));
3721 * Store the return value right in the payload
3722 * we were passed. Crude.
3724 mcopyout(mp
, NULL
, sizeof (boolean_t
), NULL
, NULL
);
3725 *(boolean_t
*)mp
->b_cont
->b_rptr
=
3726 (asy
->asy_flags
& ASY_CONSOLE
) != 0;
3731 * If we don't understand it, it's an error. NAK it.
3738 iocp
->ioc_error
= error
;
3739 mp
->b_datap
->db_type
= M_IOCNAK
;
3741 mutex_exit(&asy
->asy_excl
);
3743 DEBUGCONT1(ASY_DEBUG_PROCS
, "async%d_ioctl: done\n", instance
);
3750 struct asyncline
*async
;
3752 async
= (struct asyncline
*)q
->q_ptr
;
3754 while (canputnext(q
) && (bp
= getq(q
)))
3756 ASYSETSOFT(async
->async_common
);
3757 async
->async_polltid
= 0;
3762 * The ASYWPUTDO_NOT_SUSP macro indicates to asywputdo() whether it should
3763 * handle messages as though the driver is operating normally or is
3764 * suspended. In the suspended case, some or all of the processing may have
3765 * to be delayed until the driver is resumed.
3767 #define ASYWPUTDO_NOT_SUSP(async, wput) \
3768 !((wput) && ((async)->async_flags & ASYNC_DDI_SUSPENDED))
3771 * Processing for write queue put procedure.
3772 * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here;
3773 * set the flow control character for M_STOPI and M_STARTI messages;
3774 * queue up M_BREAK, M_DELAY, and M_DATA messages for processing
3775 * by the start routine, and then call the start routine; discard
3776 * everything else. Note that this driver does not incorporate any
3777 * mechanism to negotiate to handle the canonicalization process.
3778 * It expects that these functions are handled in upper module(s),
3779 * as we do in ldterm.
3782 asywputdo(queue_t
*q
, mblk_t
*mp
, boolean_t wput
)
3784 struct asyncline
*async
;
3791 async
= (struct asyncline
*)q
->q_ptr
;
3794 instance
= UNIT(async
->async_dev
);
3796 asy
= async
->async_common
;
3798 switch (mp
->b_datap
->db_type
) {
3802 * Since we don't do real DMA, we can just let the
3803 * chip coast to a stop after applying the brakes.
3805 mutex_enter(&asy
->asy_excl
);
3806 async
->async_flags
|= ASYNC_STOPPED
;
3807 mutex_exit(&asy
->asy_excl
);
3812 mutex_enter(&asy
->asy_excl
);
3813 if (async
->async_flags
& ASYNC_STOPPED
) {
3814 async
->async_flags
&= ~ASYNC_STOPPED
;
3815 if (ASYWPUTDO_NOT_SUSP(async
, wput
)) {
3817 * If an output operation is in progress,
3818 * resume it. Otherwise, prod the start
3821 if (async
->async_ocnt
> 0) {
3822 mutex_enter(&asy
->asy_excl_hi
);
3823 async_resume(async
);
3824 mutex_exit(&asy
->asy_excl_hi
);
3830 mutex_exit(&asy
->asy_excl
);
3835 switch (((struct iocblk
*)mp
->b_rptr
)->ioc_cmd
) {
3838 error
= miocpullup(mp
, sizeof (int));
3840 miocnak(q
, mp
, 0, error
);
3844 if (*(int *)mp
->b_cont
->b_rptr
!= 0) {
3845 DEBUGCONT1(ASY_DEBUG_OUT
,
3846 "async%d_ioctl: flush request.\n",
3850 mutex_enter(&asy
->asy_excl
);
3851 if (ASYWPUTDO_NOT_SUSP(async
, wput
)) {
3853 * If an TIOCSBRK is in progress,
3854 * clean it as TIOCCBRK does,
3855 * then kick off output.
3856 * If TIOCSBRK is not in progress,
3857 * just kick off output.
3859 async_resume_utbrk(async
);
3861 mutex_exit(&asy
->asy_excl
);
3870 * The changes do not take effect until all
3871 * output queued before them is drained.
3872 * Put this message on the queue, so that
3873 * "async_start" will see it when it's done
3874 * with the output before it. Poke the
3875 * start routine, just in case.
3879 mutex_enter(&asy
->asy_excl
);
3880 if (ASYWPUTDO_NOT_SUSP(async
, wput
)) {
3882 * If an TIOCSBRK is in progress,
3883 * clean it as TIOCCBRK does.
3884 * then kick off output.
3885 * If TIOCSBRK is not in progress,
3886 * just kick off output.
3888 async_resume_utbrk(async
);
3890 mutex_exit(&asy
->asy_excl
);
3897 mutex_enter(&asy
->asy_excl
);
3898 if (ASYWPUTDO_NOT_SUSP(async
, wput
)) {
3899 mutex_exit(&asy
->asy_excl
);
3900 async_ioctl(async
, q
, mp
);
3903 async_put_suspq(asy
, mp
);
3904 mutex_exit(&asy
->asy_excl
);
3910 if (*mp
->b_rptr
& FLUSHW
) {
3911 mutex_enter(&asy
->asy_excl
);
3914 * Abort any output in progress.
3916 mutex_enter(&asy
->asy_excl_hi
);
3917 if (async
->async_flags
& ASYNC_BUSY
) {
3918 DEBUGCONT1(ASY_DEBUG_BUSY
, "asy%dwput: "
3919 "Clearing async_ocnt, "
3920 "leaving ASYNC_BUSY set\n",
3922 async
->async_ocnt
= 0;
3923 async
->async_flags
&= ~ASYNC_BUSY
;
3926 if (ASYWPUTDO_NOT_SUSP(async
, wput
)) {
3927 /* Flush FIFO buffers */
3928 if (asy
->asy_use_fifo
== FIFO_ON
) {
3929 asy_reset_fifo(asy
, FIFOTXFLSH
);
3932 mutex_exit(&asy
->asy_excl_hi
);
3934 /* Flush FIFO buffers */
3935 if (asy
->asy_use_fifo
== FIFO_ON
) {
3936 asy_reset_fifo(asy
, FIFOTXFLSH
);
3940 * Flush our write queue.
3942 flushq(q
, FLUSHDATA
); /* XXX doesn't flush M_DELAY */
3943 if (async
->async_xmitblk
!= NULL
) {
3944 freeb(async
->async_xmitblk
);
3945 async
->async_xmitblk
= NULL
;
3947 mutex_exit(&asy
->asy_excl
);
3948 *mp
->b_rptr
&= ~FLUSHW
; /* it has been flushed */
3950 if (*mp
->b_rptr
& FLUSHR
) {
3951 if (ASYWPUTDO_NOT_SUSP(async
, wput
)) {
3952 /* Flush FIFO buffers */
3953 if (asy
->asy_use_fifo
== FIFO_ON
) {
3954 asy_reset_fifo(asy
, FIFORXFLSH
);
3957 flushq(RD(q
), FLUSHDATA
);
3958 qreply(q
, mp
); /* give the read queues a crack at it */
3964 * We must make sure we process messages that survive the
3967 if (ASYWPUTDO_NOT_SUSP(async
, wput
)) {
3968 mutex_enter(&asy
->asy_excl
);
3970 mutex_exit(&asy
->asy_excl
);
3978 * Queue the message up to be transmitted,
3979 * and poke the start routine.
3982 if (ASYWPUTDO_NOT_SUSP(async
, wput
)) {
3983 mutex_enter(&asy
->asy_excl
);
3985 mutex_exit(&asy
->asy_excl
);
3990 mutex_enter(&asy
->asy_excl
);
3991 if (ASYWPUTDO_NOT_SUSP(async
, wput
)) {
3992 mutex_enter(&asy
->asy_excl_hi
);
3993 if (!(async
->async_inflow_source
& IN_FLOW_USER
)) {
3994 async_flowcontrol_hw_input(asy
, FLOW_STOP
,
3996 (void) async_flowcontrol_sw_input(asy
,
3997 FLOW_STOP
, IN_FLOW_USER
);
3999 mutex_exit(&asy
->asy_excl_hi
);
4000 mutex_exit(&asy
->asy_excl
);
4004 async_put_suspq(asy
, mp
);
4005 mutex_exit(&asy
->asy_excl
);
4009 mutex_enter(&asy
->asy_excl
);
4010 if (ASYWPUTDO_NOT_SUSP(async
, wput
)) {
4011 mutex_enter(&asy
->asy_excl_hi
);
4012 if (async
->async_inflow_source
& IN_FLOW_USER
) {
4013 async_flowcontrol_hw_input(asy
, FLOW_START
,
4015 (void) async_flowcontrol_sw_input(asy
,
4016 FLOW_START
, IN_FLOW_USER
);
4018 mutex_exit(&asy
->asy_excl_hi
);
4019 mutex_exit(&asy
->asy_excl
);
4023 async_put_suspq(asy
, mp
);
4024 mutex_exit(&asy
->asy_excl
);
4028 if (MBLKL(mp
) >= sizeof (struct iocblk
) &&
4029 ((struct iocblk
*)mp
->b_rptr
)->ioc_cmd
== MC_POSIXQUERY
) {
4030 mutex_enter(&asy
->asy_excl
);
4031 if (ASYWPUTDO_NOT_SUSP(async
, wput
)) {
4032 ((struct iocblk
*)mp
->b_rptr
)->ioc_cmd
=
4034 mutex_exit(&asy
->asy_excl
);
4038 async_put_suspq(asy
, mp
);
4042 * These MC_SERVICE type messages are used by upper
4043 * modules to tell this driver to send input up
4044 * immediately, or that it can wait for normal
4045 * processing that may or may not be done. Sun
4046 * requires these for the mouse module.
4049 mutex_enter(&asy
->asy_excl
);
4050 switch (*mp
->b_rptr
) {
4053 async
->async_flags
|= ASYNC_SERVICEIMM
;
4057 async
->async_flags
&= ~ASYNC_SERVICEIMM
;
4060 mutex_exit(&asy
->asy_excl
);
4066 mutex_enter(&asy
->asy_excl
);
4067 if (ASYWPUTDO_NOT_SUSP(async
, wput
)) {
4068 mutex_exit(&asy
->asy_excl
);
4069 async_iocdata(q
, mp
);
4072 async_put_suspq(asy
, mp
);
4073 mutex_exit(&asy
->asy_excl
);
4084 asywput(queue_t
*q
, mblk_t
*mp
)
4086 return (asywputdo(q
, mp
, B_TRUE
));
4090 * Retry an "ioctl", now that "bufcall" claims we may be able to allocate
4091 * the buffer we need.
4094 async_reioctl(void *unit
)
4096 int instance
= (uintptr_t)unit
;
4097 struct asyncline
*async
;
4102 asy
= ddi_get_soft_state(asy_soft_state
, instance
);
4103 ASSERT(asy
!= NULL
);
4104 async
= asy
->asy_priv
;
4107 * The bufcall is no longer pending.
4109 mutex_enter(&asy
->asy_excl
);
4110 async
->async_wbufcid
= 0;
4111 if ((q
= async
->async_ttycommon
.t_writeq
) == NULL
) {
4112 mutex_exit(&asy
->asy_excl
);
4115 if ((mp
= async
->async_ttycommon
.t_iocpending
) != NULL
) {
4116 /* not pending any more */
4117 async
->async_ttycommon
.t_iocpending
= NULL
;
4118 mutex_exit(&asy
->asy_excl
);
4119 async_ioctl(async
, q
, mp
);
4121 mutex_exit(&asy
->asy_excl
);
4125 async_iocdata(queue_t
*q
, mblk_t
*mp
)
4127 struct asyncline
*async
= (struct asyncline
*)q
->q_ptr
;
4130 struct copyresp
*csp
;
4132 int instance
= UNIT(async
->async_dev
);
4135 asy
= async
->async_common
;
4136 ip
= (struct iocblk
*)mp
->b_rptr
;
4137 csp
= (struct copyresp
*)mp
->b_rptr
;
4139 if (csp
->cp_rval
!= 0) {
4140 if (csp
->cp_private
)
4141 freemsg(csp
->cp_private
);
4146 mutex_enter(&asy
->asy_excl
);
4147 DEBUGCONT2(ASY_DEBUG_MODEM
, "async%d_iocdata: case %s\n",
4149 csp
->cp_cmd
== TIOCMGET
? "TIOCMGET" :
4150 csp
->cp_cmd
== TIOCMSET
? "TIOCMSET" :
4151 csp
->cp_cmd
== TIOCMBIS
? "TIOCMBIS" :
4153 switch (csp
->cp_cmd
) {
4157 freemsg(mp
->b_cont
);
4160 mp
->b_datap
->db_type
= M_IOCACK
;
4164 mp
->b_wptr
= mp
->b_rptr
+ sizeof (struct iocblk
);
4170 mutex_enter(&asy
->asy_excl_hi
);
4171 (void) asymctl(asy
, dmtoasy(*(int *)mp
->b_cont
->b_rptr
),
4173 mutex_exit(&asy
->asy_excl_hi
);
4174 mioc2ack(mp
, NULL
, 0, 0);
4178 mp
->b_datap
->db_type
= M_IOCNAK
;
4179 ip
->ioc_error
= EINVAL
;
4183 mutex_exit(&asy
->asy_excl
);
4187 * debugger/console support routines.
4191 * put a character out
4192 * Do not use interrupts. If char is LF, put out CR, LF.
4195 asyputchar(cons_polledio_arg_t arg
, uchar_t c
)
4197 struct asycom
*asy
= (struct asycom
*)arg
;
4200 asyputchar(arg
, '\r');
4202 while ((ddi_get8(asy
->asy_iohandle
,
4203 asy
->asy_ioaddr
+ LSR
) & XHRE
) == 0) {
4204 /* wait for xmit to finish */
4208 /* put the character out */
4209 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ DAT
, c
);
4213 * See if there's a character available. If no character is
4214 * available, return 0. Run in polled mode, no interrupts.
4217 asyischar(cons_polledio_arg_t arg
)
4219 struct asycom
*asy
= (struct asycom
*)arg
;
4221 return ((ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LSR
) & RCA
)
4226 * Get a character. Run in polled mode, no interrupts.
4229 asygetchar(cons_polledio_arg_t arg
)
4231 struct asycom
*asy
= (struct asycom
*)arg
;
4233 while (!asyischar(arg
))
4235 return (ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ DAT
));
4239 * Set or get the modem control status.
4242 asymctl(struct asycom
*asy
, int bits
, int how
)
4245 int instance
= asy
->asy_unit
;
4247 ASSERT(mutex_owned(&asy
->asy_excl_hi
));
4248 ASSERT(mutex_owned(&asy
->asy_excl
));
4250 /* Read Modem Control Registers */
4251 mcr_r
= ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ MCR
);
4256 DEBUGCONT2(ASY_DEBUG_MODEM
,
4257 "asy%dmctl: TIOCMSET, bits = %x\n", instance
, bits
);
4258 mcr_r
= bits
; /* Set bits */
4262 DEBUGCONT2(ASY_DEBUG_MODEM
, "asy%dmctl: TIOCMBIS, bits = %x\n",
4264 mcr_r
|= bits
; /* Mask in bits */
4268 DEBUGCONT2(ASY_DEBUG_MODEM
, "asy%dmctl: TIOCMBIC, bits = %x\n",
4270 mcr_r
&= ~bits
; /* Mask out bits */
4274 /* Read Modem Status Registers */
4276 * If modem interrupts are enabled, we return the
4277 * saved value of msr. We read MSR only in async_msint()
4279 if (ddi_get8(asy
->asy_iohandle
,
4280 asy
->asy_ioaddr
+ ICR
) & MIEN
) {
4281 msr_r
= asy
->asy_msr
;
4282 DEBUGCONT2(ASY_DEBUG_MODEM
,
4283 "asy%dmctl: TIOCMGET, read msr_r = %x\n",
4286 msr_r
= ddi_get8(asy
->asy_iohandle
,
4287 asy
->asy_ioaddr
+ MSR
);
4288 DEBUGCONT2(ASY_DEBUG_MODEM
,
4289 "asy%dmctl: TIOCMGET, read MSR = %x\n",
4292 DEBUGCONT2(ASY_DEBUG_MODEM
, "asy%dtodm: modem_lines = %x\n",
4293 instance
, asytodm(mcr_r
, msr_r
));
4294 return (asytodm(mcr_r
, msr_r
));
4297 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ MCR
, mcr_r
);
4303 asytodm(int mcr_r
, int msr_r
)
4334 DEBUGCONT1(ASY_DEBUG_MODEM
, "dmtoasy: bits = %x\n", bits
);
4335 #ifdef CAN_NOT_SET /* only DTR and RTS can be set */
4336 if (bits
& TIOCM_CAR
)
4338 if (bits
& TIOCM_CTS
)
4340 if (bits
& TIOCM_DSR
)
4342 if (bits
& TIOCM_RNG
)
4346 if (bits
& TIOCM_RTS
) {
4347 DEBUGCONT0(ASY_DEBUG_MODEM
, "dmtoasy: set b & RTS\n");
4350 if (bits
& TIOCM_DTR
) {
4351 DEBUGCONT0(ASY_DEBUG_MODEM
, "dmtoasy: set b & DTR\n");
4359 asyerror(int level
, const char *fmt
, ...)
4363 static const char *lastfmt
;
4367 * Don't print the same error message too often.
4368 * Print the message only if we have not printed the
4369 * message within the last second.
4370 * Note: that fmt cannot be a pointer to a string
4371 * stored on the stack. The fmt pointer
4372 * must be in the data segment otherwise lastfmt would point
4375 now
= gethrestime_sec();
4376 if (last
== now
&& lastfmt
== fmt
)
4383 vcmn_err(level
, fmt
, adx
);
4388 * asy_parse_mode(dev_info_t *devi, struct asycom *asy)
4389 * The value of this property is in the form of "9600,8,n,1,-"
4390 * 1) speed: 9600, 4800, ...
4392 * 3) parity: n(none), e(even), o(odd)
4394 * 5) handshake: -(none), h(hardware: rts/cts), s(software: xon/off)
4396 * This parsing came from a SPARCstation eeprom.
4399 asy_parse_mode(dev_info_t
*devi
, struct asycom
*asy
)
4408 ASSERT(asy
->asy_com_port
!= 0);
4411 * Parse the ttyx-mode property
4413 (void) sprintf(name
, "tty%c-mode", asy
->asy_com_port
+ 'a' - 1);
4415 ret
= GET_PROP(devi
, name
, DDI_PROP_CANSLEEP
, val
, &len
);
4416 if (ret
!= DDI_PROP_SUCCESS
) {
4417 (void) sprintf(name
, "com%c-mode", asy
->asy_com_port
+ '0');
4419 ret
= GET_PROP(devi
, name
, DDI_PROP_CANSLEEP
, val
, &len
);
4422 /* no property to parse */
4424 if (ret
!= DDI_PROP_SUCCESS
)
4428 /* ---- baud rate ---- */
4429 asy
->asy_cflag
= CREAD
|B9600
; /* initial default */
4430 if (p
&& (p1
= strchr(p
, ',')) != 0) {
4433 asy
->asy_cflag
|= BITS8
; /* add default bits */
4437 if (strcmp(p
, "110") == 0)
4438 asy
->asy_bidx
= B110
;
4439 else if (strcmp(p
, "150") == 0)
4440 asy
->asy_bidx
= B150
;
4441 else if (strcmp(p
, "300") == 0)
4442 asy
->asy_bidx
= B300
;
4443 else if (strcmp(p
, "600") == 0)
4444 asy
->asy_bidx
= B600
;
4445 else if (strcmp(p
, "1200") == 0)
4446 asy
->asy_bidx
= B1200
;
4447 else if (strcmp(p
, "2400") == 0)
4448 asy
->asy_bidx
= B2400
;
4449 else if (strcmp(p
, "4800") == 0)
4450 asy
->asy_bidx
= B4800
;
4451 else if (strcmp(p
, "9600") == 0)
4452 asy
->asy_bidx
= B9600
;
4453 else if (strcmp(p
, "19200") == 0)
4454 asy
->asy_bidx
= B19200
;
4455 else if (strcmp(p
, "38400") == 0)
4456 asy
->asy_bidx
= B38400
;
4457 else if (strcmp(p
, "57600") == 0)
4458 asy
->asy_bidx
= B57600
;
4459 else if (strcmp(p
, "115200") == 0)
4460 asy
->asy_bidx
= B115200
;
4462 asy
->asy_bidx
= B9600
;
4464 asy
->asy_cflag
&= ~CBAUD
;
4465 if (asy
->asy_bidx
> CBAUD
) { /* > 38400 uses the CBAUDEXT bit */
4466 asy
->asy_cflag
|= CBAUDEXT
;
4467 asy
->asy_cflag
|= asy
->asy_bidx
- CBAUD
- 1;
4469 asy
->asy_cflag
|= asy
->asy_bidx
;
4472 ASSERT(asy
->asy_bidx
== BAUDINDEX(asy
->asy_cflag
));
4474 /* ---- Next item is data bits ---- */
4476 if (p
&& (p1
= strchr(p
, ',')) != 0) {
4479 asy
->asy_cflag
|= BITS8
; /* add default bits */
4485 asy
->asy_cflag
|= CS8
;
4486 asy
->asy_lcr
= BITS8
;
4489 asy
->asy_cflag
|= CS7
;
4490 asy
->asy_lcr
= BITS7
;
4493 asy
->asy_cflag
|= CS6
;
4494 asy
->asy_lcr
= BITS6
;
4497 /* LINTED: CS5 is currently zero (but might change) */
4498 asy
->asy_cflag
|= CS5
;
4499 asy
->asy_lcr
= BITS5
;
4503 /* ---- Parity info ---- */
4505 if (p
&& (p1
= strchr(p
, ',')) != 0) {
4515 asy
->asy_cflag
|= PARENB
;
4516 asy
->asy_lcr
|= PEN
; break;
4518 asy
->asy_cflag
|= PARENB
|PARODD
;
4519 asy
->asy_lcr
|= PEN
|EPS
;
4523 /* ---- Find stop bits ---- */
4525 if (p
&& (p1
= strchr(p
, ',')) != 0) {
4531 asy
->asy_cflag
|= CSTOPB
;
4532 asy
->asy_lcr
|= STB
;
4535 /* ---- handshake is next ---- */
4538 if ((p1
= strchr(p
, ',')) != 0)
4542 asy
->asy_cflag
|= CRTSCTS
;
4544 asy
->asy_cflag
|= CRTSXOFF
;
4549 * Check for abort character sequence
4552 abort_charseq_recognize(uchar_t ch
)
4554 static int state
= 0;
4555 #define CNTRL(c) ((c)&037)
4556 static char sequence
[] = { '\r', '~', CNTRL('b') };
4558 if (ch
== sequence
[state
]) {
4559 if (++state
>= sizeof (sequence
)) {
4564 state
= (ch
== sequence
[0]) ? 1 : 0;
4570 * Flow control functions
4573 * Software input flow control
4574 * This function can execute software input flow control sucessfully
4575 * at most of situations except that the line is in BREAK status
4576 * (timed and untimed break).
4577 * INPUT VALUE of onoff:
4578 * FLOW_START means to send out a XON char
4579 * and clear SW input flow control flag.
4580 * FLOW_STOP means to send out a XOFF char
4581 * and set SW input flow control flag.
4582 * FLOW_CHECK means to check whether there is pending XON/XOFF
4583 * if it is true, send it out.
4584 * INPUT VALUE of type:
4585 * IN_FLOW_RINGBUFF means flow control is due to RING BUFFER
4586 * IN_FLOW_STREAMS means flow control is due to STREAMS
4587 * IN_FLOW_USER means flow control is due to user's commands
4588 * RETURN VALUE: B_FALSE means no flow control char is sent
4589 * B_TRUE means one flow control char is sent
4592 async_flowcontrol_sw_input(struct asycom
*asy
, async_flowc_action onoff
,
4595 struct asyncline
*async
= asy
->asy_priv
;
4596 int instance
= UNIT(async
->async_dev
);
4599 ASSERT(mutex_owned(&asy
->asy_excl_hi
));
4601 if (!(async
->async_ttycommon
.t_iflag
& IXOFF
))
4605 * If we get this far, then we know IXOFF is set.
4609 async
->async_inflow_source
|= type
;
4612 * We'll send an XOFF character for each of up to
4613 * three different input flow control attempts to stop input.
4614 * If we already send out one XOFF, but FLOW_STOP comes again,
4615 * it seems that input flow control becomes more serious,
4616 * then send XOFF again.
4618 if (async
->async_inflow_source
& (IN_FLOW_RINGBUFF
|
4619 IN_FLOW_STREAMS
| IN_FLOW_USER
))
4620 async
->async_flags
|= ASYNC_SW_IN_FLOW
|
4622 DEBUGCONT2(ASY_DEBUG_SFLOW
, "async%d: input sflow stop, "
4623 "type = %x\n", instance
, async
->async_inflow_source
);
4626 async
->async_inflow_source
&= ~type
;
4627 if (async
->async_inflow_source
== 0) {
4628 async
->async_flags
= (async
->async_flags
&
4629 ~ASYNC_SW_IN_FLOW
) | ASYNC_SW_IN_NEEDED
;
4630 DEBUGCONT1(ASY_DEBUG_SFLOW
, "async%d: "
4631 "input sflow start\n", instance
);
4638 if (((async
->async_flags
& (ASYNC_SW_IN_NEEDED
| ASYNC_BREAK
|
4639 ASYNC_OUT_SUSPEND
)) == ASYNC_SW_IN_NEEDED
) &&
4640 (ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ LSR
) & XHRE
)) {
4642 * If we get this far, then we know we need to send out
4645 async
->async_flags
= (async
->async_flags
&
4646 ~ASYNC_SW_IN_NEEDED
) | ASYNC_BUSY
;
4647 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ DAT
,
4648 async
->async_flags
& ASYNC_SW_IN_FLOW
?
4649 async
->async_stopc
: async
->async_startc
);
4656 * Software output flow control
4657 * This function can be executed sucessfully at any situation.
4658 * It does not handle HW, and just change the SW output flow control flag.
4659 * INPUT VALUE of onoff:
4660 * FLOW_START means to clear SW output flow control flag,
4661 * also combine with HW output flow control status to
4662 * determine if we need to set ASYNC_OUT_FLW_RESUME.
4663 * FLOW_STOP means to set SW output flow control flag,
4664 * also clear ASYNC_OUT_FLW_RESUME.
4667 async_flowcontrol_sw_output(struct asycom
*asy
, async_flowc_action onoff
)
4669 struct asyncline
*async
= asy
->asy_priv
;
4670 int instance
= UNIT(async
->async_dev
);
4672 ASSERT(mutex_owned(&asy
->asy_excl_hi
));
4674 if (!(async
->async_ttycommon
.t_iflag
& IXON
))
4679 async
->async_flags
|= ASYNC_SW_OUT_FLW
;
4680 async
->async_flags
&= ~ASYNC_OUT_FLW_RESUME
;
4681 DEBUGCONT1(ASY_DEBUG_SFLOW
, "async%d: output sflow stop\n",
4685 async
->async_flags
&= ~ASYNC_SW_OUT_FLW
;
4686 if (!(async
->async_flags
& ASYNC_HW_OUT_FLW
))
4687 async
->async_flags
|= ASYNC_OUT_FLW_RESUME
;
4688 DEBUGCONT1(ASY_DEBUG_SFLOW
, "async%d: output sflow start\n",
4697 * Hardware input flow control
4698 * This function can be executed sucessfully at any situation.
4699 * It directly changes RTS depending on input parameter onoff.
4700 * INPUT VALUE of onoff:
4701 * FLOW_START means to clear HW input flow control flag,
4702 * and pull up RTS if it is low.
4703 * FLOW_STOP means to set HW input flow control flag,
4704 * and low RTS if it is high.
4705 * INPUT VALUE of type:
4706 * IN_FLOW_RINGBUFF means flow control is due to RING BUFFER
4707 * IN_FLOW_STREAMS means flow control is due to STREAMS
4708 * IN_FLOW_USER means flow control is due to user's commands
4711 async_flowcontrol_hw_input(struct asycom
*asy
, async_flowc_action onoff
,
4716 struct asyncline
*async
= asy
->asy_priv
;
4717 int instance
= UNIT(async
->async_dev
);
4719 ASSERT(mutex_owned(&asy
->asy_excl_hi
));
4721 if (!(async
->async_ttycommon
.t_cflag
& CRTSXOFF
))
4726 async
->async_inflow_source
|= type
;
4727 if (async
->async_inflow_source
& (IN_FLOW_RINGBUFF
|
4728 IN_FLOW_STREAMS
| IN_FLOW_USER
))
4729 async
->async_flags
|= ASYNC_HW_IN_FLOW
;
4730 DEBUGCONT2(ASY_DEBUG_HFLOW
, "async%d: input hflow stop, "
4731 "type = %x\n", instance
, async
->async_inflow_source
);
4734 async
->async_inflow_source
&= ~type
;
4735 if (async
->async_inflow_source
== 0) {
4736 async
->async_flags
&= ~ASYNC_HW_IN_FLOW
;
4737 DEBUGCONT1(ASY_DEBUG_HFLOW
, "async%d: "
4738 "input hflow start\n", instance
);
4744 mcr
= ddi_get8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ MCR
);
4745 flag
= (async
->async_flags
& ASYNC_HW_IN_FLOW
) ? 0 : RTS
;
4747 if (((mcr
^ flag
) & RTS
) != 0) {
4748 ddi_put8(asy
->asy_iohandle
,
4749 asy
->asy_ioaddr
+ MCR
, (mcr
^ RTS
));
4754 * Hardware output flow control
4755 * This function can execute HW output flow control sucessfully
4757 * It doesn't really change RTS, and just change
4758 * HW output flow control flag depending on CTS status.
4759 * INPUT VALUE of onoff:
4760 * FLOW_START means to clear HW output flow control flag.
4761 * also combine with SW output flow control status to
4762 * determine if we need to set ASYNC_OUT_FLW_RESUME.
4763 * FLOW_STOP means to set HW output flow control flag.
4764 * also clear ASYNC_OUT_FLW_RESUME.
4767 async_flowcontrol_hw_output(struct asycom
*asy
, async_flowc_action onoff
)
4769 struct asyncline
*async
= asy
->asy_priv
;
4770 int instance
= UNIT(async
->async_dev
);
4772 ASSERT(mutex_owned(&asy
->asy_excl_hi
));
4774 if (!(async
->async_ttycommon
.t_cflag
& CRTSCTS
))
4779 async
->async_flags
|= ASYNC_HW_OUT_FLW
;
4780 async
->async_flags
&= ~ASYNC_OUT_FLW_RESUME
;
4781 DEBUGCONT1(ASY_DEBUG_HFLOW
, "async%d: output hflow stop\n",
4785 async
->async_flags
&= ~ASYNC_HW_OUT_FLW
;
4786 if (!(async
->async_flags
& ASYNC_SW_OUT_FLW
))
4787 async
->async_flags
|= ASYNC_OUT_FLW_RESUME
;
4788 DEBUGCONT1(ASY_DEBUG_HFLOW
, "async%d: output hflow start\n",
4798 * quiesce(9E) entry point.
4800 * This function is called when the system is single-threaded at high
4801 * PIL with preemption disabled. Therefore, this function must not be
4804 * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
4805 * DDI_FAILURE indicates an error condition and should almost never happen.
4808 asyquiesce(dev_info_t
*devi
)
4813 instance
= ddi_get_instance(devi
); /* find out which unit */
4815 asy
= ddi_get_soft_state(asy_soft_state
, instance
);
4817 return (DDI_FAILURE
);
4819 /* disable all interrupts */
4820 ddi_put8(asy
->asy_iohandle
, asy
->asy_ioaddr
+ ICR
, 0);
4822 /* reset the FIFO */
4823 asy_reset_fifo(asy
, FIFOTXFLSH
| FIFORXFLSH
);
4825 return (DDI_SUCCESS
);